incanto 0.27.0 → 0.29.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 +118 -1
- package/dist/2d.d.ts +38 -5
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +496 -150
- package/dist/3d.js +5 -5
- package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
- package/dist/{create-game-DUAtTqID.js → create-game-CNKXGfpr.js} +270 -47
- package/dist/{create-game-C0bPoURs.js → create-game-CbuLWorm.js} +53 -14
- 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-switch-CAKlJMIY.js +161 -0
- package/dist/editor.d.ts +49 -0
- package/dist/editor.js +8439 -0
- package/dist/{gameplay-Ddk13pQ6.js → gameplay-L05WgWd1.js} +220 -37
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +22 -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-CCVTrKOd.js} +62 -2
- package/dist/{physics-3d-4mGzOZ2J.js → physics-3d-BZZLtwJu.js} +439 -8
- 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-DcHXS1MA.js → register-C44aSduO.js} +5694 -5108
- package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
- package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
- package/dist/test.d.ts +35 -3
- package/dist/test.js +104 -10
- package/dist/vite.d.ts +53 -1
- package/dist/vite.js +94 -2
- package/editor/assets/{agent8-DElIPozC.js → agent8-DEVkEa3d.js} +1 -1
- package/editor/assets/debug-zGAtpDF0.js +2 -0
- package/editor/assets/index-Bx4UtWYY.js +10586 -0
- package/editor/index.html +3 -157
- package/package.json +3 -2
- package/schemas/scene.schema.json +110 -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 +151 -0
- package/skills/incanto-environment.md +88 -1
- package/skills/incanto-node-reference.md +35 -1
- package/skills/incanto-physics-and-input.md +45 -6
- package/skills/incanto-verifying-your-game.md +51 -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/tps-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/editor/assets/index-BrneVqN0.js +0 -8330
- package/editor/assets/index-D8QvwvOm.css +0 -1
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 PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-
|
|
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-
|
|
4
|
-
import { A as
|
|
2
|
+
import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-L05WgWd1.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-CNKXGfpr.js";
|
|
4
|
+
import { A as DEFAULT_TERRAIN_TEXTURE_BASE, B as movementState, C as FLOWER_VARIETIES, D as BoneAttachment3D, E as BoneLookAt3D, F as MeshInstance3D, L as QUARTER_PITCH, M as terrainThemeLayers, N as Joint3D, O as Billboard3D, P as InstancedMesh3D, R as cameraRelative, S as resolveFlowerDensity, T as Camera3D, V as rigPose, _ as DirectionalLight3D, a as Trail3D, b as DENSITY_PRESETS, c as findRiverCoverageGaps, d as riverStepFor, f as smoothCourse, g as LoftMesh3D, h as ModelInstance3D, i as Tree3D, j as TERRAIN_THEMES, k as Terrain3D, l as projectToRiver, m as Particles3D, n as VOXEL_PALETTE, o as River3D, p as traceDownhillPath, r as VoxelGrid3D, s as buildRiverRings, t as registerNodes3D, u as riverCarveChannels, v as OmniLight3D, w as CharacterController3D, x as Flowers3D, y as Foliage3D, z as keyboardIntensity } from "./register-C44aSduO.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-
|
|
6
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-BZZLtwJu.js";
|
|
7
7
|
//#region src/3d/environment-runtime.ts
|
|
8
8
|
/**
|
|
9
9
|
* Live environment editing — the renderer re-applies `scene.environment`
|
|
@@ -142,4 +142,4 @@ function createNavDebugNode(nav, opts) {
|
|
|
142
142
|
return node;
|
|
143
143
|
}
|
|
144
144
|
//#endregion
|
|
145
|
-
export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, buildHeightmap, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, setEnvironment3D, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
|
145
|
+
export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, findRiverCoverageGaps, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
|
@@ -791,6 +791,16 @@ declare class Engine {
|
|
|
791
791
|
* `engine.music.play(src)` / `crossfadeTo(src, secs)` / `stop(fadeOut)`;
|
|
792
792
|
* routes through the music bus and is advanced each frame by the loop. */
|
|
793
793
|
readonly music: MusicManager;
|
|
794
|
+
/**
|
|
795
|
+
* Where nodes that build DOM (HudLayer and its widgets) mount their elements.
|
|
796
|
+
*
|
|
797
|
+
* `null` — the default — means `document.body` with `position: fixed`, i.e. a
|
|
798
|
+
* game's HUD covers the window, which is what a game wants. A host that only
|
|
799
|
+
* OWNS PART of the page sets this to its own (positioned) container so the
|
|
800
|
+
* HUD stays inside it: the scene editor points it at the viewport pane, which
|
|
801
|
+
* is why an edited scene's HUD no longer floats over the inspector.
|
|
802
|
+
*/
|
|
803
|
+
uiHost: HTMLElement | null;
|
|
794
804
|
private readonly fixedStep;
|
|
795
805
|
private readonly maxFixedSteps;
|
|
796
806
|
private readonly scheduler;
|
|
@@ -870,6 +880,17 @@ interface PropDef {
|
|
|
870
880
|
/** For enum-like string props: the valid values. Drives editor/debug
|
|
871
881
|
* dropdowns and the generated JSON-Schema enum — keep it exhaustive. */
|
|
872
882
|
options?: readonly string[];
|
|
883
|
+
/**
|
|
884
|
+
* For an OBJECT-valued prop shaped as a tagged union — `collider`, whose
|
|
885
|
+
* `shape` decides which other keys are required. One declaration gives an
|
|
886
|
+
* inspector both the dropdown AND something valid to write when the tag
|
|
887
|
+
* changes: switching `box` → `sphere` must not leave a `size` and no
|
|
888
|
+
* `radius`, which is a hard load error.
|
|
889
|
+
*/
|
|
890
|
+
variants?: {
|
|
891
|
+
/** The key that picks the variant (e.g. `'shape'`). */tag: string; /** Tag value → a COMPLETE, valid value for the prop. */
|
|
892
|
+
byTag: Record<string, JsonValue>;
|
|
893
|
+
};
|
|
873
894
|
}
|
|
874
895
|
type PropSchema = Record<string, PropDef>;
|
|
875
896
|
interface NodeCtor {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { _ as registerBehavior, n as loadScene } from "./loader-BZqOKfI2.js";
|
|
3
|
-
import { l as AudioPlayer, u as Engine } from "./register-
|
|
3
|
+
import { l as AudioPlayer, u as Engine } from "./register-BTg0EM7s.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 {
|
|
6
|
+
import { i as poseFromRenderer, n as devServerLibrary, r as openBundledEditor, t as crossFade } from "./editor-switch-CAKlJMIY.js";
|
|
7
|
+
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-L05WgWd1.js";
|
|
7
8
|
import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
|
|
8
|
-
import {
|
|
9
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
9
|
+
import { T as Camera3D, _ as DirectionalLight3D, h as ModelInstance3D, t as registerNodes3D } from "./register-C44aSduO.js";
|
|
10
|
+
import { n as enablePhysics3D } from "./physics-3d-BZZLtwJu.js";
|
|
10
11
|
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
12
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
12
13
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -1163,49 +1164,132 @@ function parseIblIntensity(value) {
|
|
|
1163
1164
|
//#endregion
|
|
1164
1165
|
//#region src/3d/selection-outline.ts
|
|
1165
1166
|
/**
|
|
1166
|
-
* Where IS the node the dev-overlay selected? —
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1167
|
+
* Where IS the node the dev-overlay selected? — outline segments for the
|
|
1168
|
+
* renderer's highlight pass. A renderable node gets its actual bounding box
|
|
1169
|
+
* (children included); a node with no visual extent gets a small marker cube at
|
|
1170
|
+
* its world position so it is still findable.
|
|
1171
|
+
*
|
|
1172
|
+
* The box is ORIENTED — measured in the node's own frame, then turned by its
|
|
1173
|
+
* world matrix. A world-axis-aligned box lies about anything rotated: a 14 m
|
|
1174
|
+
* footbridge deck turned 143° would be wrapped in a 12.5 × 10.2 m rectangle,
|
|
1175
|
+
* three times its real footprint, and the outline would sit nowhere near the
|
|
1176
|
+
* plank it is supposed to be pointing at.
|
|
1170
1177
|
*/
|
|
1171
1178
|
const box = new Box3();
|
|
1172
|
-
const
|
|
1179
|
+
const childBox = new Box3();
|
|
1180
|
+
const toLocal = new Matrix4();
|
|
1181
|
+
const childMat = new Matrix4();
|
|
1182
|
+
const corner = new Vector3();
|
|
1183
|
+
/** Reused every frame — the outline refreshes while a node stays selected. */
|
|
1184
|
+
const points = Array.from({ length: 8 }, () => new Vector3());
|
|
1173
1185
|
const MARKER_HALF = .4;
|
|
1174
1186
|
/** 12 box edges = 24 points × xyz. */
|
|
1175
1187
|
const OUT = new Float32Array(72);
|
|
1188
|
+
/** The eight corners in the order the edge list below indexes them. */
|
|
1189
|
+
const CORNERS = [
|
|
1190
|
+
[
|
|
1191
|
+
0,
|
|
1192
|
+
0,
|
|
1193
|
+
0
|
|
1194
|
+
],
|
|
1195
|
+
[
|
|
1196
|
+
1,
|
|
1197
|
+
0,
|
|
1198
|
+
0
|
|
1199
|
+
],
|
|
1200
|
+
[
|
|
1201
|
+
1,
|
|
1202
|
+
0,
|
|
1203
|
+
1
|
|
1204
|
+
],
|
|
1205
|
+
[
|
|
1206
|
+
0,
|
|
1207
|
+
0,
|
|
1208
|
+
1
|
|
1209
|
+
],
|
|
1210
|
+
[
|
|
1211
|
+
0,
|
|
1212
|
+
1,
|
|
1213
|
+
0
|
|
1214
|
+
],
|
|
1215
|
+
[
|
|
1216
|
+
1,
|
|
1217
|
+
1,
|
|
1218
|
+
0
|
|
1219
|
+
],
|
|
1220
|
+
[
|
|
1221
|
+
1,
|
|
1222
|
+
1,
|
|
1223
|
+
1
|
|
1224
|
+
],
|
|
1225
|
+
[
|
|
1226
|
+
0,
|
|
1227
|
+
1,
|
|
1228
|
+
1
|
|
1229
|
+
]
|
|
1230
|
+
];
|
|
1231
|
+
const EDGES = [
|
|
1232
|
+
[0, 1],
|
|
1233
|
+
[1, 2],
|
|
1234
|
+
[2, 3],
|
|
1235
|
+
[3, 0],
|
|
1236
|
+
[4, 5],
|
|
1237
|
+
[5, 6],
|
|
1238
|
+
[6, 7],
|
|
1239
|
+
[7, 4],
|
|
1240
|
+
[0, 4],
|
|
1241
|
+
[1, 5],
|
|
1242
|
+
[2, 6],
|
|
1243
|
+
[3, 7]
|
|
1244
|
+
];
|
|
1245
|
+
/** The subtree's extent in `root`'s own frame — no ancestor rotation baked in. */
|
|
1246
|
+
function localBox(root, out) {
|
|
1247
|
+
out.makeEmpty();
|
|
1248
|
+
toLocal.copy(root.matrixWorld).invert();
|
|
1249
|
+
root.traverse((child) => {
|
|
1250
|
+
const mesh = child;
|
|
1251
|
+
let source;
|
|
1252
|
+
if (mesh.isInstancedMesh) {
|
|
1253
|
+
if (!mesh.boundingBox) mesh.computeBoundingBox?.();
|
|
1254
|
+
source = mesh.boundingBox;
|
|
1255
|
+
} else if (mesh.geometry) {
|
|
1256
|
+
if (!mesh.geometry.boundingBox) mesh.geometry.computeBoundingBox();
|
|
1257
|
+
source = mesh.geometry.boundingBox;
|
|
1258
|
+
}
|
|
1259
|
+
if (!source) return;
|
|
1260
|
+
childBox.copy(source).applyMatrix4(childMat.multiplyMatrices(toLocal, child.matrixWorld));
|
|
1261
|
+
out.union(childBox);
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1176
1264
|
function selectionOutline3D(node) {
|
|
1177
1265
|
if (!(node instanceof Node3D)) return null;
|
|
1178
1266
|
const obj = node._ensureObject3D();
|
|
1179
1267
|
obj.updateWorldMatrix(true, true);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
box.
|
|
1268
|
+
localBox(obj, box);
|
|
1269
|
+
let oriented = true;
|
|
1270
|
+
if (box.isEmpty()) {
|
|
1271
|
+
oriented = false;
|
|
1272
|
+
corner.setFromMatrixPosition(obj.matrixWorld);
|
|
1273
|
+
box.min.set(corner.x - MARKER_HALF, corner.y - MARKER_HALF, corner.z - MARKER_HALF);
|
|
1274
|
+
box.max.set(corner.x + MARKER_HALF, corner.y + MARKER_HALF, corner.z + MARKER_HALF);
|
|
1186
1275
|
} else box.expandByScalar(.02);
|
|
1187
1276
|
const { min, max } = box;
|
|
1277
|
+
for (let c = 0; c < 8; c++) {
|
|
1278
|
+
const [cx, cy, cz] = CORNERS[c];
|
|
1279
|
+
const p = points[c].set(cx ? max.x : min.x, cy ? max.y : min.y, cz ? max.z : min.z);
|
|
1280
|
+
if (oriented) p.applyMatrix4(obj.matrixWorld);
|
|
1281
|
+
}
|
|
1188
1282
|
let i = 0;
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
OUT[i++] =
|
|
1193
|
-
OUT[i++] =
|
|
1194
|
-
OUT[i++] =
|
|
1195
|
-
OUT[i++] =
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
seg(max.x, min.y, max.z, min.x, min.y, max.z);
|
|
1200
|
-
seg(min.x, min.y, max.z, min.x, min.y, min.z);
|
|
1201
|
-
seg(min.x, max.y, min.z, max.x, max.y, min.z);
|
|
1202
|
-
seg(max.x, max.y, min.z, max.x, max.y, max.z);
|
|
1203
|
-
seg(max.x, max.y, max.z, min.x, max.y, max.z);
|
|
1204
|
-
seg(min.x, max.y, max.z, min.x, max.y, min.z);
|
|
1205
|
-
seg(min.x, min.y, min.z, min.x, max.y, min.z);
|
|
1206
|
-
seg(max.x, min.y, min.z, max.x, max.y, min.z);
|
|
1207
|
-
seg(max.x, min.y, max.z, max.x, max.y, max.z);
|
|
1208
|
-
seg(min.x, min.y, max.z, min.x, max.y, max.z);
|
|
1283
|
+
for (const [a, b] of EDGES) {
|
|
1284
|
+
const p = points[a];
|
|
1285
|
+
const q = points[b];
|
|
1286
|
+
OUT[i++] = p.x;
|
|
1287
|
+
OUT[i++] = p.y;
|
|
1288
|
+
OUT[i++] = p.z;
|
|
1289
|
+
OUT[i++] = q.x;
|
|
1290
|
+
OUT[i++] = q.y;
|
|
1291
|
+
OUT[i++] = q.z;
|
|
1292
|
+
}
|
|
1209
1293
|
return OUT;
|
|
1210
1294
|
}
|
|
1211
1295
|
//#endregion
|
|
@@ -1420,6 +1504,11 @@ var Renderer3D = class {
|
|
|
1420
1504
|
loadedAssetScenes = /* @__PURE__ */ new WeakSet();
|
|
1421
1505
|
/** The scene whose GPU programs have been pre-compiled (warm-up, see render). */
|
|
1422
1506
|
compiledScene = null;
|
|
1507
|
+
/** The camera the last frame drew with — what warmUp compiles against. */
|
|
1508
|
+
warmCamera = null;
|
|
1509
|
+
/** Set while a caller intends to run {@link warmUp} instead: the first frame
|
|
1510
|
+
* then compiles only what it actually draws, and the rest goes async. */
|
|
1511
|
+
deferCompile = false;
|
|
1423
1512
|
/** Reused per-frame walk scratch (instance-scoped — never a module global). */
|
|
1424
1513
|
syncScratch = createSyncScratch();
|
|
1425
1514
|
/** Editors set this: keep syncing `static: true` subtrees every frame. */
|
|
@@ -1465,6 +1554,7 @@ var Renderer3D = class {
|
|
|
1465
1554
|
canvas: opts.canvas,
|
|
1466
1555
|
antialias: rendering.antialias
|
|
1467
1556
|
});
|
|
1557
|
+
this.webgl.info.autoReset = false;
|
|
1468
1558
|
this.basePixelRatio = rendering.pixelRatio;
|
|
1469
1559
|
this.webgl.setPixelRatio(rendering.pixelRatio);
|
|
1470
1560
|
this.adaptive = opts.adaptiveResolution === false ? null : new AdaptiveResolution();
|
|
@@ -1543,9 +1633,56 @@ var Renderer3D = class {
|
|
|
1543
1633
|
const size = this.lastSize;
|
|
1544
1634
|
if (size.w > 0 && size.h > 0) this.webgl.setSize(size.w, size.h, false);
|
|
1545
1635
|
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Compile the scene's GPU programs off the critical path.
|
|
1638
|
+
*
|
|
1639
|
+
* The compile itself is unavoidable — a dressed outdoor scene is 40+ programs
|
|
1640
|
+
* and a driver takes hundreds of milliseconds each (measured: SEVEN SECONDS
|
|
1641
|
+
* for this engine's creek template on macOS/Metal). What is avoidable is
|
|
1642
|
+
* doing it in ONE synchronous block, which freezes the tab and leaves a
|
|
1643
|
+
* loading bar with nothing to say.
|
|
1644
|
+
*
|
|
1645
|
+
* Called by `createGame3D` behind the app's loading screen.
|
|
1646
|
+
*/
|
|
1647
|
+
/** @internal Tell the next frame to leave the bulk compile to {@link warmUp}. */
|
|
1648
|
+
deferFirstCompile() {
|
|
1649
|
+
this.deferCompile = true;
|
|
1650
|
+
}
|
|
1651
|
+
async warmUp(onProgress) {
|
|
1652
|
+
this.deferCompile = false;
|
|
1653
|
+
const camera = this.warmCamera;
|
|
1654
|
+
if (!camera) return;
|
|
1655
|
+
const drawn = [];
|
|
1656
|
+
this.threeScene.traverse((obj) => {
|
|
1657
|
+
if (obj.isMesh) drawn.push(obj);
|
|
1658
|
+
else if (obj.isPoints) drawn.push(obj);
|
|
1659
|
+
});
|
|
1660
|
+
const hidden = drawn.filter((o) => o.visible);
|
|
1661
|
+
if (hidden.length === 0) {
|
|
1662
|
+
this.webgl.compile(this.threeScene, camera);
|
|
1663
|
+
onProgress?.(1);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
for (const obj of hidden) obj.visible = false;
|
|
1667
|
+
const slices = Math.min(WARMUP_SLICES, hidden.length);
|
|
1668
|
+
const per = Math.ceil(hidden.length / slices);
|
|
1669
|
+
try {
|
|
1670
|
+
for (let i = 0; i < slices; i++) {
|
|
1671
|
+
for (let k = i * per; k < Math.min((i + 1) * per, hidden.length); k++) hidden[k].visible = true;
|
|
1672
|
+
this.webgl.compile(this.threeScene, camera);
|
|
1673
|
+
onProgress?.((i + 1) / slices);
|
|
1674
|
+
if (typeof requestAnimationFrame === "function") await new Promise((resolve) => {
|
|
1675
|
+
requestAnimationFrame(() => setTimeout(resolve, 0));
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
} finally {
|
|
1679
|
+
for (const obj of hidden) obj.visible = true;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1546
1682
|
render() {
|
|
1547
1683
|
const scene = this.engine.scene;
|
|
1548
1684
|
if (!scene) return;
|
|
1685
|
+
this.webgl.info.reset();
|
|
1549
1686
|
this.governResolution();
|
|
1550
1687
|
this.syncDebugLines();
|
|
1551
1688
|
this.syncSelectionOutline();
|
|
@@ -1574,10 +1711,11 @@ var Renderer3D = class {
|
|
|
1574
1711
|
const threeLight = sunLight ? sunLight._ensureObject3D() : null;
|
|
1575
1712
|
const followsCamera = !!threeLight && sunLight.shadowFollowsCamera === true;
|
|
1576
1713
|
this.environment.applySunLight(threeLight, followsCamera ? camWorld : null);
|
|
1577
|
-
if (this.compiledScene !== scene) {
|
|
1714
|
+
if (this.compiledScene !== scene && !this.deferCompile) {
|
|
1578
1715
|
this.compiledScene = scene;
|
|
1579
1716
|
this.webgl.compile(this.threeScene, activeCamera);
|
|
1580
1717
|
}
|
|
1718
|
+
this.warmCamera = activeCamera;
|
|
1581
1719
|
const w = this.canvas.clientWidth || this.canvas.width;
|
|
1582
1720
|
const h = this.canvas.clientHeight || this.canvas.height;
|
|
1583
1721
|
const size = this.webgl.getSize(sizeScratch);
|
|
@@ -1645,7 +1783,10 @@ var Renderer3D = class {
|
|
|
1645
1783
|
this.bloomBlurTarget = null;
|
|
1646
1784
|
}
|
|
1647
1785
|
if (!this.bloomTarget) {
|
|
1648
|
-
this.bloomTarget = new WebGLRenderTarget(w, h, {
|
|
1786
|
+
this.bloomTarget = new WebGLRenderTarget(w, h, {
|
|
1787
|
+
type: HalfFloatType,
|
|
1788
|
+
samples: 4
|
|
1789
|
+
});
|
|
1649
1790
|
this.bloomBrightTarget = new WebGLRenderTarget(hw, hh, { type: HalfFloatType });
|
|
1650
1791
|
this.bloomBlurTarget = new WebGLRenderTarget(hw, hh, { type: HalfFloatType });
|
|
1651
1792
|
}
|
|
@@ -1882,8 +2023,8 @@ var Renderer3D = class {
|
|
|
1882
2023
|
return null;
|
|
1883
2024
|
}
|
|
1884
2025
|
/**
|
|
1885
|
-
* GPU counters for the
|
|
1886
|
-
*
|
|
2026
|
+
* GPU counters for the last rendered frame — EVERY pass of it, summed (see
|
|
2027
|
+
* the `info.reset()` in `render`), plus GPU memory.
|
|
1887
2028
|
*/
|
|
1888
2029
|
stats() {
|
|
1889
2030
|
const info = this.webgl.info;
|
|
@@ -1894,6 +2035,24 @@ var Renderer3D = class {
|
|
|
1894
2035
|
textures: info.memory.textures
|
|
1895
2036
|
};
|
|
1896
2037
|
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Where the last rendered frame was seen FROM.
|
|
2040
|
+
*
|
|
2041
|
+
* Paired with {@link cameraBasis} this is the view on screen — a third-person
|
|
2042
|
+
* rig, a cutscene camera and the editor's free-fly all answer here, which is
|
|
2043
|
+
* what a mode switch needs to carry across so the cut does not read as a
|
|
2044
|
+
* page load.
|
|
2045
|
+
*/
|
|
2046
|
+
lastCameraPosition() {
|
|
2047
|
+
const cam = this.lastCamera;
|
|
2048
|
+
if (!cam) return null;
|
|
2049
|
+
cam.getWorldPosition(basisPos);
|
|
2050
|
+
return {
|
|
2051
|
+
x: basisPos.x,
|
|
2052
|
+
y: basisPos.y,
|
|
2053
|
+
z: basisPos.z
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
1897
2056
|
/** The last rendered camera's world-space basis (for screen-plane dragging). */
|
|
1898
2057
|
cameraBasis() {
|
|
1899
2058
|
const cam = this.lastCamera;
|
|
@@ -1949,12 +2108,16 @@ var Renderer3D = class {
|
|
|
1949
2108
|
this.webgl.dispose();
|
|
1950
2109
|
}
|
|
1951
2110
|
};
|
|
2111
|
+
/** Slices the warm-up compile is broken into — enough for a bar to move, few
|
|
2112
|
+
* enough that walking the scene per slice stays negligible. */
|
|
2113
|
+
const WARMUP_SLICES = 12;
|
|
1952
2114
|
const sizeScratch = new Vector2();
|
|
1953
2115
|
const lookScratch = new Vector3();
|
|
1954
2116
|
const projScratch = new Vector3();
|
|
1955
2117
|
const sunDirScratch = new Vector3();
|
|
1956
2118
|
const sunTargetScratch = new Vector3();
|
|
1957
2119
|
const basisQuat = new Quaternion();
|
|
2120
|
+
const basisPos = new Vector3();
|
|
1958
2121
|
/** Reused camera world-position scratch (shadow-follow focus + underwater test). */
|
|
1959
2122
|
const camPosScratch = new Vector3();
|
|
1960
2123
|
/** Reused click-pick scratches (no per-pick allocation). */
|
|
@@ -1968,6 +2131,14 @@ var create_game_exports = /* @__PURE__ */ __exportAll({ createGame3D: () => crea
|
|
|
1968
2131
|
* (engine attached for onReady) → physics (auto) → input → renderer → start.
|
|
1969
2132
|
*/
|
|
1970
2133
|
async function createGame3D(opts) {
|
|
2134
|
+
const report = async (fraction, label) => {
|
|
2135
|
+
if (!opts.onProgress) return;
|
|
2136
|
+
opts.onProgress(fraction, label);
|
|
2137
|
+
if (typeof requestAnimationFrame !== "function") return;
|
|
2138
|
+
await new Promise((resolve) => {
|
|
2139
|
+
requestAnimationFrame(() => setTimeout(resolve, 0));
|
|
2140
|
+
});
|
|
2141
|
+
};
|
|
1971
2142
|
registerNodes3D();
|
|
1972
2143
|
if (opts.gameplay ?? true) registerGameplayBehaviors();
|
|
1973
2144
|
for (const [name, ctor] of Object.entries(opts.behaviors ?? {})) registerBehavior(name, ctor, { replace: true });
|
|
@@ -1976,13 +2147,16 @@ async function createGame3D(opts) {
|
|
|
1976
2147
|
fixedHz: opts.fixedHz,
|
|
1977
2148
|
...opts._scheduler ? { scheduler: opts._scheduler } : {}
|
|
1978
2149
|
});
|
|
1979
|
-
const
|
|
2150
|
+
const sourceJson = cloneSceneJson(opts.scene);
|
|
2151
|
+
const scene = loadScene(cloneSceneJson(sourceJson), {
|
|
1980
2152
|
resolveScene: opts.resolveScene,
|
|
1981
2153
|
engine
|
|
1982
2154
|
});
|
|
1983
2155
|
engine.setScene(scene);
|
|
2156
|
+
await report(.06, "scene");
|
|
1984
2157
|
let physics = null;
|
|
1985
2158
|
if (opts.physics === true || (opts.physics ?? "auto") === "auto" && hasBody(scene.root)) physics = await enablePhysics3D(engine);
|
|
2159
|
+
await report(.1, "physics");
|
|
1986
2160
|
const cleanups = [];
|
|
1987
2161
|
const keyboard = opts.keyboard ?? (typeof window !== "undefined" ? window : false);
|
|
1988
2162
|
if (keyboard) engine.input.attachKeyboard(keyboard);
|
|
@@ -2016,22 +2190,75 @@ async function createGame3D(opts) {
|
|
|
2016
2190
|
...opts.pixelRatio !== void 0 ? { pixelRatio: opts.pixelRatio } : {},
|
|
2017
2191
|
...opts.adaptiveResolution !== void 0 ? { adaptiveResolution: opts.adaptiveResolution } : {}
|
|
2018
2192
|
});
|
|
2019
|
-
|
|
2193
|
+
await report(.14, "renderer");
|
|
2194
|
+
const wantDebug = opts.debug ?? false;
|
|
2195
|
+
const wantEditor = opts.editor ?? wantDebug;
|
|
2196
|
+
const editorSwitch = wantEditor === true ? {} : wantEditor ? wantEditor : null;
|
|
2197
|
+
const editorAction = {
|
|
2198
|
+
label: "✎ edit this scene",
|
|
2199
|
+
run: async () => {
|
|
2200
|
+
if (!editorSwitch) return;
|
|
2201
|
+
const camera = poseFromRenderer(renderer);
|
|
2202
|
+
const open = editorSwitch.open ?? openBundledEditor;
|
|
2203
|
+
const reboot = async (working) => {
|
|
2204
|
+
await createGame3D({
|
|
2205
|
+
...opts,
|
|
2206
|
+
scene: working
|
|
2207
|
+
});
|
|
2208
|
+
};
|
|
2209
|
+
await crossFade(async () => {
|
|
2210
|
+
disposeGame();
|
|
2211
|
+
try {
|
|
2212
|
+
await open({
|
|
2213
|
+
scene: sourceJson,
|
|
2214
|
+
...camera ? { camera } : {},
|
|
2215
|
+
play: reboot,
|
|
2216
|
+
exit: () => reboot(sourceJson),
|
|
2217
|
+
...editorSwitch.save ? { save: editorSwitch.save } : {},
|
|
2218
|
+
...editorSwitch.library ? { library: editorSwitch.library === true ? devServerLibrary() : editorSwitch.library } : {}
|
|
2219
|
+
});
|
|
2220
|
+
} catch (error) {
|
|
2221
|
+
console.error("[incanto] could not open the editor:", error);
|
|
2222
|
+
await reboot(sourceJson);
|
|
2223
|
+
}
|
|
2224
|
+
}, "opening the editor…");
|
|
2225
|
+
}
|
|
2226
|
+
};
|
|
2227
|
+
if (wantDebug) {
|
|
2020
2228
|
const host = opts.touchContainer ?? opts.canvas.parentElement ?? (typeof document !== "undefined" ? document.body : null);
|
|
2021
2229
|
if (host) {
|
|
2022
2230
|
const { attachDebugOverlay } = await import("./debug.js");
|
|
2023
2231
|
const overlay = attachDebugOverlay(engine, {
|
|
2024
2232
|
container: host,
|
|
2025
2233
|
statsSource: () => renderer.stats?.() ?? {},
|
|
2234
|
+
...editorSwitch ? { actions: [editorAction] } : {},
|
|
2026
2235
|
...opts._debugDoc ? { doc: opts._debugDoc } : {}
|
|
2027
2236
|
});
|
|
2028
2237
|
if (overlay) cleanups.push(() => overlay.dispose());
|
|
2029
2238
|
}
|
|
2030
2239
|
}
|
|
2031
2240
|
engine.start();
|
|
2241
|
+
await report(.16, "shaders");
|
|
2242
|
+
try {
|
|
2243
|
+
renderer.deferFirstCompile?.();
|
|
2244
|
+
const nowMs = globalThis.performance?.now?.() ?? 0;
|
|
2245
|
+
engine.tick(nowMs);
|
|
2246
|
+
engine.tick(nowMs + 16);
|
|
2247
|
+
await renderer.warmUp?.((f) => {
|
|
2248
|
+
opts.onProgress?.(.16 + f * .82, "shaders");
|
|
2249
|
+
});
|
|
2250
|
+
} catch {}
|
|
2251
|
+
await report(1, "ready");
|
|
2252
|
+
function disposeGame() {
|
|
2253
|
+
renderer.dispose();
|
|
2254
|
+
physics?.dispose();
|
|
2255
|
+
for (const cleanup of cleanups) cleanup();
|
|
2256
|
+
engine.dispose();
|
|
2257
|
+
}
|
|
2032
2258
|
return {
|
|
2033
2259
|
engine,
|
|
2034
2260
|
scene,
|
|
2261
|
+
sourceJson,
|
|
2035
2262
|
renderer,
|
|
2036
2263
|
physics,
|
|
2037
2264
|
stats() {
|
|
@@ -2044,12 +2271,8 @@ async function createGame3D(opts) {
|
|
|
2044
2271
|
...renderer.stats?.()
|
|
2045
2272
|
};
|
|
2046
2273
|
},
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
physics?.dispose();
|
|
2050
|
-
for (const cleanup of cleanups) cleanup();
|
|
2051
|
-
engine.dispose();
|
|
2052
|
-
}
|
|
2274
|
+
openEditor: editorSwitch ? editorAction.run : null,
|
|
2275
|
+
dispose: disposeGame
|
|
2053
2276
|
};
|
|
2054
2277
|
}
|
|
2055
2278
|
function cloneSceneJson(json) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { _ as registerBehavior, n as loadScene, o as computeViewport, s as resolveViewport } from "./loader-BZqOKfI2.js";
|
|
3
|
-
import { l as AudioPlayer, u as Engine } from "./register-
|
|
3
|
+
import { l as AudioPlayer, u as Engine } from "./register-BTg0EM7s.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
|
|
7
|
-
import {
|
|
6
|
+
import { n as devServerLibrary, r as openBundledEditor, t as crossFade } from "./editor-switch-CAKlJMIY.js";
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-L05WgWd1.js";
|
|
8
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-DWcWq4QG.js";
|
|
8
9
|
import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
|
|
9
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
10
|
+
import { n as enablePhysics2D } from "./physics-2d-CCVTrKOd.js";
|
|
10
11
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
11
12
|
//#region src/2d/assets.ts
|
|
12
13
|
/**
|
|
@@ -266,6 +267,7 @@ var Renderer2D = class {
|
|
|
266
267
|
canvas: opts.canvas,
|
|
267
268
|
antialias: rendering.antialias
|
|
268
269
|
});
|
|
270
|
+
this.webgl.info.autoReset = false;
|
|
269
271
|
this.webgl.setPixelRatio(rendering.pixelRatio);
|
|
270
272
|
this.webgl.info.autoReset = false;
|
|
271
273
|
this.debugLines = new LineSegments(new BufferGeometry(), new LineBasicMaterial({
|
|
@@ -386,6 +388,7 @@ var Renderer2D = class {
|
|
|
386
388
|
}
|
|
387
389
|
frame(this.worldCam, center.x, center.y, uiW / zoom, uiH / zoom);
|
|
388
390
|
frame(this.uiCam, uiW / 2, uiH / 2, uiW, uiH);
|
|
391
|
+
this.webgl.info.reset();
|
|
389
392
|
this.webgl.autoClear = true;
|
|
390
393
|
this.webgl.render(this.worldScene, this.worldCam);
|
|
391
394
|
this.webgl.autoClear = false;
|
|
@@ -405,8 +408,8 @@ var Renderer2D = class {
|
|
|
405
408
|
return { ...this.lastView };
|
|
406
409
|
}
|
|
407
410
|
/**
|
|
408
|
-
* GPU counters for the LAST rendered frame
|
|
409
|
-
*
|
|
411
|
+
* GPU counters for the LAST rendered frame — world + UI passes combined (see
|
|
412
|
+
* the `info.reset()` in the render pass), plus GPU memory.
|
|
410
413
|
*/
|
|
411
414
|
stats() {
|
|
412
415
|
const info = this.webgl.info;
|
|
@@ -517,7 +520,8 @@ async function createGame2D(opts) {
|
|
|
517
520
|
fixedHz: opts.fixedHz,
|
|
518
521
|
...opts._scheduler ? { scheduler: opts._scheduler } : {}
|
|
519
522
|
});
|
|
520
|
-
const
|
|
523
|
+
const sourceJson = cloneSceneJson(opts.scene);
|
|
524
|
+
const scene = loadScene(cloneSceneJson(sourceJson), {
|
|
521
525
|
resolveScene: opts.resolveScene,
|
|
522
526
|
engine
|
|
523
527
|
});
|
|
@@ -553,22 +557,61 @@ async function createGame2D(opts) {
|
|
|
553
557
|
...opts.pixelRatio !== void 0 ? { pixelRatio: opts.pixelRatio } : {},
|
|
554
558
|
...opts.antialias !== void 0 ? { antialias: opts.antialias } : {}
|
|
555
559
|
});
|
|
556
|
-
|
|
560
|
+
const wantDebug = opts.debug ?? false;
|
|
561
|
+
const wantEditor = opts.editor ?? wantDebug;
|
|
562
|
+
const editorSwitch = wantEditor === true ? {} : wantEditor ? wantEditor : null;
|
|
563
|
+
const editorAction = {
|
|
564
|
+
label: "✎ edit this scene",
|
|
565
|
+
run: async () => {
|
|
566
|
+
if (!editorSwitch) return;
|
|
567
|
+
const open = editorSwitch.open ?? openBundledEditor;
|
|
568
|
+
const reboot = async (working) => {
|
|
569
|
+
await createGame2D({
|
|
570
|
+
...opts,
|
|
571
|
+
scene: working
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
await crossFade(async () => {
|
|
575
|
+
disposeGame();
|
|
576
|
+
try {
|
|
577
|
+
await open({
|
|
578
|
+
scene: sourceJson,
|
|
579
|
+
play: reboot,
|
|
580
|
+
exit: () => reboot(sourceJson),
|
|
581
|
+
...editorSwitch.save ? { save: editorSwitch.save } : {},
|
|
582
|
+
...editorSwitch.library ? { library: editorSwitch.library === true ? devServerLibrary() : editorSwitch.library } : {}
|
|
583
|
+
});
|
|
584
|
+
} catch (error) {
|
|
585
|
+
console.error("[incanto] could not open the editor:", error);
|
|
586
|
+
await reboot(sourceJson);
|
|
587
|
+
}
|
|
588
|
+
}, "opening the editor…");
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
if (wantDebug) {
|
|
557
592
|
const host = opts.touchContainer ?? opts.canvas.parentElement ?? (typeof document !== "undefined" ? document.body : null);
|
|
558
593
|
if (host) {
|
|
559
594
|
const { attachDebugOverlay } = await import("./debug.js");
|
|
560
595
|
const overlay = attachDebugOverlay(engine, {
|
|
561
596
|
container: host,
|
|
562
597
|
statsSource: () => renderer.stats?.() ?? {},
|
|
598
|
+
...editorSwitch ? { actions: [editorAction] } : {},
|
|
563
599
|
...opts._debugDoc ? { doc: opts._debugDoc } : {}
|
|
564
600
|
});
|
|
565
601
|
if (overlay) cleanups.push(() => overlay.dispose());
|
|
566
602
|
}
|
|
567
603
|
}
|
|
568
604
|
engine.start();
|
|
605
|
+
function disposeGame() {
|
|
606
|
+
renderer.dispose();
|
|
607
|
+
physics?.dispose();
|
|
608
|
+
for (const cleanup of cleanups) cleanup();
|
|
609
|
+
engine.dispose();
|
|
610
|
+
}
|
|
569
611
|
return {
|
|
570
612
|
engine,
|
|
571
613
|
scene,
|
|
614
|
+
sourceJson,
|
|
572
615
|
renderer,
|
|
573
616
|
physics,
|
|
574
617
|
stats() {
|
|
@@ -581,12 +624,8 @@ async function createGame2D(opts) {
|
|
|
581
624
|
...renderer.stats?.()
|
|
582
625
|
};
|
|
583
626
|
},
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
physics?.dispose();
|
|
587
|
-
for (const cleanup of cleanups) cleanup();
|
|
588
|
-
engine.dispose();
|
|
589
|
-
}
|
|
627
|
+
openEditor: editorSwitch ? editorAction.run : null,
|
|
628
|
+
dispose: disposeGame
|
|
590
629
|
};
|
|
591
630
|
}
|
|
592
631
|
function cloneSceneJson(json) {
|