incanto 0.42.0 → 0.44.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/incanto-feel.mjs +20 -1
- package/bin/incanto-frame.mjs +55 -11
- package/dist/2d.js +1 -1
- package/dist/3d.d.ts +2 -40
- package/dist/3d.js +6 -5
- package/dist/{create-game-DItIRyXH.js → create-game-D90pyPMx.js} +17 -81
- package/dist/{create-game-9KPppR0L.js → create-game-DFRbG0Bj.js} +3 -2
- package/dist/debug.d.ts +44 -3
- package/dist/debug.js +562 -4
- package/dist/{environment-presets-Vl5xBsXp.js → environment-presets-Ds5kXLoF.js} +1 -1
- package/dist/frame-report-Ct8XgsmV.d.ts +80 -0
- package/dist/frame-report-Lr3VO24R.js +197 -0
- package/dist/{gameplay-DRi9524r.js → gameplay-BQOeAid6.js} +37 -0
- package/dist/gameplay.d.ts +17 -0
- package/dist/gameplay.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{physics-3d-C2G604O1.js → physics-3d-CLPFv99o.js} +2 -2
- package/dist/react.js +1 -1
- package/dist/{src-cU57Uwdw.js → src-B3n06SsL.js} +1 -1
- package/dist/{teardown-CCtAMDLB.js → teardown-yePMOE1K.js} +32 -1
- package/dist/{test-DuOD1DO8.js → test-BMgiiD5i.js} +117 -5
- package/dist/test.d.ts +35 -1
- package/dist/test.js +2 -2
- package/dist/vite.d.ts +15 -1
- package/dist/vite.js +49 -6
- package/editor/assets/{agent8-Csw0T7jh.js → agent8-MgX6kYWH.js} +1 -1
- package/editor/assets/debug-Czri9cgX.js +3 -0
- package/editor/assets/{index-B1rUkWxB.js → index-D9MX3UHF.js} +60 -60
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/skills/incanto-3d-character.md +19 -0
- package/skills/incanto-gameplay-behaviors.md +27 -0
- package/skills/incanto-node-reference.md +2 -0
- package/skills/incanto-verifying-your-game.md +60 -4
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +47 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +53 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +43 -0
- package/editor/assets/debug-C3qeBD4X.js +0 -3
package/bin/incanto-feel.mjs
CHANGED
|
@@ -52,7 +52,9 @@ if (args.help || !args.scene) {
|
|
|
52
52
|
process.exit(args.help && args.scene !== undefined ? 0 : 1);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const { feelReport, feelText } = await import(
|
|
55
|
+
const { facingReport, facingText, feelReport, feelText } = await import(
|
|
56
|
+
pathToFileURL(join(PKG, 'dist', 'test.js')).href
|
|
57
|
+
);
|
|
56
58
|
const incanto = await import(pathToFileURL(join(PKG, 'dist', 'index.js')).href);
|
|
57
59
|
|
|
58
60
|
const behaviors = {};
|
|
@@ -90,4 +92,21 @@ try {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
process.stdout.write(args.json ? `${JSON.stringify(report, null, 2)}\n` : `${feelText(report)}\n`);
|
|
95
|
+
|
|
96
|
+
// "It runs backwards" is this repo's most repeated 3D bug and the check is one
|
|
97
|
+
// dot product — which nobody runs by hand. It rides along here because this is
|
|
98
|
+
// already the command that drives the character.
|
|
99
|
+
if (!args.json) {
|
|
100
|
+
try {
|
|
101
|
+
const facing = await facingReport(sceneJson, {
|
|
102
|
+
behaviors,
|
|
103
|
+
stubMissingBehaviors: !args.behaviors,
|
|
104
|
+
resolveScene: (p) => JSON.parse(readFileSync(resolve(sceneDir, p), 'utf-8')),
|
|
105
|
+
});
|
|
106
|
+
if (facing.skin) process.stdout.write(`${facingText(facing)}\n`);
|
|
107
|
+
} catch {
|
|
108
|
+
// a scene this probe cannot drive is not a failing scene
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
93
112
|
process.exit(report.player ? 0 : 1);
|
package/bin/incanto-frame.mjs
CHANGED
|
@@ -25,20 +25,46 @@ const PKG = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
25
25
|
const { listeningPorts, parseProcNetTcp } = await import(
|
|
26
26
|
pathToFileURL(join(PKG, 'dist', 'vite.js')).href
|
|
27
27
|
);
|
|
28
|
-
const { frameText } = await import(pathToFileURL(join(PKG, 'dist', '3d.js')).href);
|
|
28
|
+
const { frameText, diffText } = await import(pathToFileURL(join(PKG, 'dist', '3d.js')).href);
|
|
29
29
|
|
|
30
30
|
const args = process.argv.slice(2);
|
|
31
31
|
const asJson = args.includes('--json');
|
|
32
|
-
|
|
32
|
+
/** `--flag` with an optional value: `--remember` and `--remember shader` both. */
|
|
33
|
+
const flag = (name) => {
|
|
34
|
+
const i = args.indexOf(name);
|
|
35
|
+
if (i < 0) return null;
|
|
36
|
+
const next = args[i + 1];
|
|
37
|
+
return next && !next.startsWith('-') ? next : '';
|
|
38
|
+
};
|
|
39
|
+
const gridArg = flag('--grid');
|
|
40
|
+
const remember = flag('--remember');
|
|
41
|
+
const diff = flag('--diff');
|
|
42
|
+
const threshold = flag('--threshold');
|
|
33
43
|
if (args.includes('--help') || args.includes('-h')) {
|
|
34
44
|
console.error(`Usage: incanto-frame [options]
|
|
35
45
|
|
|
36
|
-
--json
|
|
37
|
-
--grid WxH
|
|
38
|
-
--port N
|
|
46
|
+
--json the full report as JSON instead of prose
|
|
47
|
+
--grid WxH cells across and down (default 16x9)
|
|
48
|
+
--port N skip discovery and use this port
|
|
49
|
+
|
|
50
|
+
--remember [L] keep this frame as the baseline named L (default "last")
|
|
51
|
+
--diff [L] say what changed since baseline L
|
|
52
|
+
--threshold N a cell has changed when it moves this far, 0..255 (default 3)
|
|
39
53
|
|
|
40
54
|
Run your game's dev server and open the preview page first: the pixels live in
|
|
41
|
-
the browser, not in the dev server
|
|
55
|
+
the browser, not in the dev server.
|
|
56
|
+
|
|
57
|
+
The report of ONE frame has a floor: a one-pixel seam is a fraction of a level
|
|
58
|
+
once a cell is averaged, and nothing can tell it apart from a thin rope in the
|
|
59
|
+
scene. Two frames of the SAME view can:
|
|
60
|
+
|
|
61
|
+
incanto-frame --remember # before your change
|
|
62
|
+
...edit the shader...
|
|
63
|
+
incanto-frame --diff # "a vertical band at x 640..660"
|
|
64
|
+
|
|
65
|
+
Baselines live in the dev server's memory and die with it — no file is written.
|
|
66
|
+
Pause the game first (debug ☰ → Time → Pause) or you are measuring time
|
|
67
|
+
passing, not your change.`);
|
|
42
68
|
process.exit(0);
|
|
43
69
|
}
|
|
44
70
|
|
|
@@ -110,7 +136,13 @@ if (!found) {
|
|
|
110
136
|
process.exit(1);
|
|
111
137
|
}
|
|
112
138
|
|
|
113
|
-
const
|
|
139
|
+
const params = new URLSearchParams();
|
|
140
|
+
if (gridArg !== null) params.set('grid', gridArg);
|
|
141
|
+
if (remember !== null) params.set('remember', remember);
|
|
142
|
+
if (diff !== null) params.set('diff', diff);
|
|
143
|
+
if (threshold) params.set('threshold', threshold);
|
|
144
|
+
const q = params.toString();
|
|
145
|
+
const res = await fetch(`http://${found.host}:${found.port}/__incanto/frame${q ? `?${q}` : ''}`, {
|
|
114
146
|
signal: AbortSignal.timeout(10_000),
|
|
115
147
|
});
|
|
116
148
|
const body = await res.json().catch(() => null);
|
|
@@ -134,11 +166,23 @@ if (report.error) {
|
|
|
134
166
|
}
|
|
135
167
|
|
|
136
168
|
if (asJson) {
|
|
137
|
-
process.stdout.write(`${JSON.stringify(
|
|
169
|
+
process.stdout.write(`${JSON.stringify(body, null, 2)}\n`);
|
|
138
170
|
} else {
|
|
139
|
-
|
|
171
|
+
const lines = [frameText(report)];
|
|
172
|
+
if (body.diffError === 'no-baseline') {
|
|
173
|
+
lines.push(
|
|
174
|
+
`no baseline named "${diff || 'last'}" — run \`incanto-frame --remember${diff ? ` ${diff}` : ''}\` first,\n` +
|
|
175
|
+
' then again with --diff after your change. The baseline lives in the dev\n' +
|
|
176
|
+
' server, so restarting it forgets.',
|
|
177
|
+
);
|
|
178
|
+
} else if (body.diffError) {
|
|
179
|
+
lines.push(`cannot compare: ${body.diffError}`);
|
|
180
|
+
}
|
|
181
|
+
if (body.diff) lines.push(diffText(body.diff));
|
|
182
|
+
if (body.remembered) lines.push(`remembered as "${body.remembered}"`);
|
|
183
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
140
184
|
}
|
|
141
|
-
|
|
185
|
+
// A missing baseline is a usage error, not a picture: fail so a script notices.
|
|
186
|
+
process.exit(report.black || body.diffError ? 1 : 0);
|
|
142
187
|
|
|
143
188
|
void parseProcNetTcp; // re-exported for tests; referenced so bundlers keep it
|
|
144
|
-
void gridArg;
|
package/dist/2d.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-
|
|
2
|
+
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-DFRbG0Bj.js";
|
|
3
3
|
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-R2JTnIMw.js";
|
|
4
4
|
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-BmgXBNDB.js";
|
|
5
5
|
//#region src/2d/library-sprite.ts
|
package/dist/3d.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-D
|
|
|
3
3
|
import { i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
|
|
4
4
|
import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
|
|
5
5
|
import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
|
|
6
|
+
import { a as GridCell, c as diffText, d as frameText, i as FrameStatsOptions, l as frameSignature, n as FrameSignature, o as SIGNATURE_GRID, r as FrameStats, s as diffSignatures, t as FrameDiff, u as frameStats } from "./frame-report-Ct8XgsmV.js";
|
|
6
7
|
import { n as PathGrid, s as SpatialPose } from "./pathfinding-C49JSNNq.js";
|
|
7
8
|
import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
|
|
8
9
|
import { VRM } from "@pixiv/three-vrm";
|
|
@@ -190,45 +191,6 @@ declare function keyboardIntensity(moving: boolean, sprinting: boolean): number;
|
|
|
190
191
|
/** intensity → movement state (the animation driver). */
|
|
191
192
|
declare function movementState(grounded: boolean, intensity: number): "idle" | "walk" | "run" | "fastRun" | "airborne";
|
|
192
193
|
//#endregion
|
|
193
|
-
//#region src/3d/frame-report.d.ts
|
|
194
|
-
/**
|
|
195
|
-
* What the frame looks like, as something worth saying.
|
|
196
|
-
*
|
|
197
|
-
* The pixels are not the deliverable. A 1280x720 frame is 2.8 million numbers:
|
|
198
|
-
* unreadable by a person, unaffordable for a model (roughly a thousand tokens
|
|
199
|
-
* as an image, roughly a million as text). The deliverable is the JUDGEMENT —
|
|
200
|
-
* black screen, one flat colour, washed out — and the numbers behind it, small
|
|
201
|
-
* enough to print and compare.
|
|
202
|
-
*
|
|
203
|
-
* Same principle as `framing`'s "lit by: NOTHING": a report is a sentence
|
|
204
|
-
* before it is a number, because a number needs a reader who already knows what
|
|
205
|
-
* to compare it against.
|
|
206
|
-
*
|
|
207
|
-
* Pure arithmetic over an RGBA buffer — no three, no DOM, no GPU. Whoever has
|
|
208
|
-
* the pixels calls this; in practice that is the browser holding the canvas.
|
|
209
|
-
*/
|
|
210
|
-
/** Per-cell mean colour, `[r, g, b]` 0..255. */
|
|
211
|
-
type GridCell = [number, number, number];
|
|
212
|
-
interface FrameStats {
|
|
213
|
-
width: number;
|
|
214
|
-
height: number;
|
|
215
|
-
/** Every pixel below the black threshold — the render produced nothing. */
|
|
216
|
-
black: boolean;
|
|
217
|
-
/** One colour everywhere: a clear-colour fill, or a camera inside geometry. */
|
|
218
|
-
uniform: boolean;
|
|
219
|
-
/** 0..1 mean relative luminance. */
|
|
220
|
-
meanLuma: number;
|
|
221
|
-
/** Coarse mean colour per cell, rows top to bottom. */
|
|
222
|
-
grid: GridCell[][];
|
|
223
|
-
}
|
|
224
|
-
interface FrameStatsOptions {
|
|
225
|
-
/** Cells across and down. Default 16x9 — 144 cells, printable. */
|
|
226
|
-
grid?: [number, number];
|
|
227
|
-
}
|
|
228
|
-
declare function frameStats(pixels: Uint8Array | Uint8ClampedArray, width: number, height: number, opts?: FrameStatsOptions): FrameStats;
|
|
229
|
-
/** The report a person or an agent reads. Verdict first, numbers after. */
|
|
230
|
-
declare function frameText(stats: FrameStats): string;
|
|
231
|
-
//#endregion
|
|
232
194
|
//#region src/3d/nodes/node-3d.d.ts
|
|
233
195
|
/**
|
|
234
196
|
* Per-frame hand-off from `Renderer3D` to nodes that need the LIVE WebGL
|
|
@@ -3647,4 +3609,4 @@ declare function createNavDebugNode(nav: TerrainNav, opts?: {
|
|
|
3647
3609
|
name?: string;
|
|
3648
3610
|
}): InstancedMesh3D;
|
|
3649
3611
|
//#endregion
|
|
3650
|
-
export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type FrameStats, type FrameStatsOptions, type Game3D, type GridCell, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, River3D, type RiverCarveOptions, type RiverCoverageGap, type RiverHit, type RiverRing, type RiverRingOptions, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, TextureCache3D, type TextureSpec, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, findRiverCoverageGaps, frameStats, frameText, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
|
3612
|
+
export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type FrameDiff, type FrameSignature, type FrameStats, type FrameStatsOptions, type Game3D, type GridCell, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, River3D, type RiverCarveOptions, type RiverCoverageGap, type RiverHit, type RiverRing, type RiverRingOptions, SIGNATURE_GRID, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, TextureCache3D, type TextureSpec, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
package/dist/3d.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
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-
|
|
2
|
-
import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-
|
|
3
|
-
import { a as Environment3D, c as
|
|
1
|
+
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-BQOeAid6.js";
|
|
2
|
+
import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-Ds5kXLoF.js";
|
|
3
|
+
import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-D90pyPMx.js";
|
|
4
|
+
import { a as frameStats, i as frameSignature, n as diffSignatures, o as frameText, r as diffText, t as SIGNATURE_GRID } from "./frame-report-Lr3VO24R.js";
|
|
4
5
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
5
|
-
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-
|
|
6
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-CLPFv99o.js";
|
|
6
7
|
//#region src/3d/terrain-nav.ts
|
|
7
8
|
function buildTerrainNav(terrain, opts) {
|
|
8
9
|
const cellSize = opts?.cellSize ?? 2;
|
|
@@ -114,4 +115,4 @@ function createNavDebugNode(nav, opts) {
|
|
|
114
115
|
return node;
|
|
115
116
|
}
|
|
116
117
|
//#endregion
|
|
117
|
-
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, TextureCache3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, findRiverCoverageGaps, frameStats, frameText, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
|
118
|
+
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, SIGNATURE_GRID, StaticBody3D, TERRAIN_THEMES, Terrain3D, TextureCache3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
|
|
@@ -3,11 +3,12 @@ import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-r
|
|
|
3
3
|
import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-BSXV8T9F.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
|
-
import { a as
|
|
7
|
-
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-
|
|
6
|
+
import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, t as teardown } from "./teardown-yePMOE1K.js";
|
|
7
|
+
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-BQOeAid6.js";
|
|
8
8
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
9
|
-
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-
|
|
10
|
-
import {
|
|
9
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-Ds5kXLoF.js";
|
|
10
|
+
import { a as frameStats, i as frameSignature } from "./frame-report-Lr3VO24R.js";
|
|
11
|
+
import { n as enablePhysics3D } from "./physics-3d-CLPFv99o.js";
|
|
11
12
|
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";
|
|
12
13
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
13
14
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -486,76 +487,6 @@ function setEnvironment3D(engine, patch) {
|
|
|
486
487
|
scene.environment = merged;
|
|
487
488
|
}
|
|
488
489
|
//#endregion
|
|
489
|
-
//#region src/3d/frame-report.ts
|
|
490
|
-
/** Rec. 709 relative luminance of an 8-bit triple, 0..1. */
|
|
491
|
-
function luma(r, g, b) {
|
|
492
|
-
return (.2126 * r + .7152 * g + .0722 * b) / 255;
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* A pixel this dark is indistinguishable from "nothing was drawn". Not zero:
|
|
496
|
-
* a real render of a night scene still lands a few levels above black, and
|
|
497
|
-
* calling that a failure would make the check useless where it matters most.
|
|
498
|
-
*/
|
|
499
|
-
const BLACK_LEVEL = .012;
|
|
500
|
-
/** Cell-to-cell luminance range below this: the frame is one flat colour. */
|
|
501
|
-
const UNIFORM_EPSILON = .004;
|
|
502
|
-
function frameStats(pixels, width, height, opts = {}) {
|
|
503
|
-
const [cols, rows] = opts.grid ?? [16, 9];
|
|
504
|
-
const grid = [];
|
|
505
|
-
let total = 0;
|
|
506
|
-
let maxLuma = 0;
|
|
507
|
-
let minLuma = 1;
|
|
508
|
-
for (let row = 0; row < rows; row++) {
|
|
509
|
-
const line = [];
|
|
510
|
-
for (let col = 0; col < cols; col++) {
|
|
511
|
-
const x0 = Math.floor(col * width / cols);
|
|
512
|
-
const x1 = Math.max(x0 + 1, Math.floor((col + 1) * width / cols));
|
|
513
|
-
const y0 = Math.floor(row * height / rows);
|
|
514
|
-
const y1 = Math.max(y0 + 1, Math.floor((row + 1) * height / rows));
|
|
515
|
-
let r = 0;
|
|
516
|
-
let g = 0;
|
|
517
|
-
let b = 0;
|
|
518
|
-
let n = 0;
|
|
519
|
-
for (let y = y0; y < y1; y++) for (let x = x0; x < x1; x++) {
|
|
520
|
-
const i = (y * width + x) * 4;
|
|
521
|
-
r += pixels[i] ?? 0;
|
|
522
|
-
g += pixels[i + 1] ?? 0;
|
|
523
|
-
b += pixels[i + 2] ?? 0;
|
|
524
|
-
n += 1;
|
|
525
|
-
}
|
|
526
|
-
const cell = [
|
|
527
|
-
Math.round(r / n),
|
|
528
|
-
Math.round(g / n),
|
|
529
|
-
Math.round(b / n)
|
|
530
|
-
];
|
|
531
|
-
const cellLuma = luma(cell[0], cell[1], cell[2]);
|
|
532
|
-
total += cellLuma;
|
|
533
|
-
maxLuma = Math.max(maxLuma, cellLuma);
|
|
534
|
-
minLuma = Math.min(minLuma, cellLuma);
|
|
535
|
-
line.push(cell);
|
|
536
|
-
}
|
|
537
|
-
grid.push(line);
|
|
538
|
-
}
|
|
539
|
-
const cells = cols * rows;
|
|
540
|
-
const meanLuma = total / cells;
|
|
541
|
-
return {
|
|
542
|
-
width,
|
|
543
|
-
height,
|
|
544
|
-
black: maxLuma <= BLACK_LEVEL,
|
|
545
|
-
uniform: maxLuma - minLuma < UNIFORM_EPSILON,
|
|
546
|
-
meanLuma: Math.round(meanLuma * 1e3) / 1e3,
|
|
547
|
-
grid
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
/** The report a person or an agent reads. Verdict first, numbers after. */
|
|
551
|
-
function frameText(stats) {
|
|
552
|
-
const lines = [];
|
|
553
|
-
if (stats.black) lines.push("BLACK SCREEN — nothing was drawn (no light, no camera, or nothing in view)");
|
|
554
|
-
else if (stats.uniform) lines.push("one flat colour — the camera may be inside geometry, or only the sky is drawn");
|
|
555
|
-
lines.push(`frame ${stats.width}×${stats.height} · luminance ${stats.meanLuma.toFixed(2)}`);
|
|
556
|
-
return lines.join("\n");
|
|
557
|
-
}
|
|
558
|
-
//#endregion
|
|
559
490
|
//#region src/3d/adaptive-resolution.ts
|
|
560
491
|
const DEFAULTS = {
|
|
561
492
|
slowMs: 20,
|
|
@@ -2475,6 +2406,7 @@ async function createGame3D(opts) {
|
|
|
2475
2406
|
const overlay = attachDebugOverlay(engine, {
|
|
2476
2407
|
container: host,
|
|
2477
2408
|
statsSource: () => renderer.stats?.() ?? {},
|
|
2409
|
+
...renderer.captureFrame ? { frameSource: () => captureFramePixels() } : {},
|
|
2478
2410
|
...editorSwitch ? { actions: [editorAction] } : {},
|
|
2479
2411
|
...opts._debugDoc ? { doc: opts._debugDoc } : {}
|
|
2480
2412
|
});
|
|
@@ -2504,19 +2436,22 @@ async function createGame3D(opts) {
|
|
|
2504
2436
|
cleanups.push(engine.settings.bindRenderScale((scale) => {
|
|
2505
2437
|
renderer.setRenderScale?.(scale);
|
|
2506
2438
|
}));
|
|
2507
|
-
const
|
|
2439
|
+
const captureFramePixels = () => {
|
|
2508
2440
|
const capture = renderer.captureFrame;
|
|
2509
2441
|
if (!capture) throw new IncantoError("TREE_VIOLATION", "this renderer cannot capture frames");
|
|
2510
|
-
|
|
2511
|
-
return frameStats(shot.pixels, shot.width, shot.height, frameOpts);
|
|
2442
|
+
return capture.call(renderer);
|
|
2512
2443
|
};
|
|
2513
2444
|
const hot = import.meta.hot;
|
|
2514
2445
|
if (hot) {
|
|
2515
2446
|
const onFrameRequest = (data) => {
|
|
2516
|
-
const
|
|
2517
|
-
|
|
2447
|
+
const request = data;
|
|
2448
|
+
const id = request?.id;
|
|
2449
|
+
captureFramePixels().then((shot) => hot.send("incanto:frame-report", {
|
|
2518
2450
|
id,
|
|
2519
|
-
report:
|
|
2451
|
+
report: {
|
|
2452
|
+
...frameStats(shot.pixels, shot.width, shot.height, { grid: request?.grid }),
|
|
2453
|
+
signature: frameSignature(shot.pixels, shot.width, shot.height)
|
|
2454
|
+
}
|
|
2520
2455
|
})).catch((error) => hot.send("incanto:frame-report", {
|
|
2521
2456
|
id,
|
|
2522
2457
|
report: { error: error instanceof Error ? error.message : String(error) }
|
|
@@ -2525,6 +2460,7 @@ async function createGame3D(opts) {
|
|
|
2525
2460
|
hot.on("incanto:frame-request", onFrameRequest);
|
|
2526
2461
|
cleanups.push(() => hot.off?.("incanto:frame-request", onFrameRequest));
|
|
2527
2462
|
}
|
|
2463
|
+
cleanups.push(pauseWhenHidden(engine));
|
|
2528
2464
|
await report(1, "ready");
|
|
2529
2465
|
function disposeGame() {
|
|
2530
2466
|
teardown([
|
|
@@ -2617,4 +2553,4 @@ function wireAudioUnlock(engine, root, canvas) {
|
|
|
2617
2553
|
return detachAll;
|
|
2618
2554
|
}
|
|
2619
2555
|
//#endregion
|
|
2620
|
-
export { Environment3D as a,
|
|
2556
|
+
export { Environment3D as a, parseEnvironment3D as c, AssetStore3D as d, syncTree as i, sunDirectionFromElevationAzimuth as l, create_game_exports as n, setEnvironment3D as o, Renderer3D as r, horizonColorFromSky as s, createGame3D as t, sunDirectionFromSky as u };
|
|
@@ -3,8 +3,8 @@ import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagno
|
|
|
3
3
|
import { h as Engine, m as AudioPlayer } from "./register-BSXV8T9F.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
|
-
import {
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
6
|
+
import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, t as teardown } from "./teardown-yePMOE1K.js";
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-BQOeAid6.js";
|
|
8
8
|
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-R2JTnIMw.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
10
|
import { n as enablePhysics2D } from "./physics-2d-BmgXBNDB.js";
|
|
@@ -545,6 +545,7 @@ async function createGame2D(opts) {
|
|
|
545
545
|
if (keyboard) engine.input.attachKeyboard(keyboard);
|
|
546
546
|
cleanups.push(wireAudioUnlock(engine, scene.root, opts.canvas));
|
|
547
547
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
548
|
+
cleanups.push(pauseWhenHidden(engine));
|
|
548
549
|
cleanups.push(engine.settings.bindLocale(engine.locale));
|
|
549
550
|
const touchMode = opts.touch ?? "auto";
|
|
550
551
|
if (touchMode !== false) {
|
package/dist/debug.d.ts
CHANGED
|
@@ -3,16 +3,21 @@ import { At as LogLevel, T as RendererStats, b as Engine } from "./behavior-62q0
|
|
|
3
3
|
//#region src/debug/panel.d.ts
|
|
4
4
|
/** Minimal document surface the overlay needs (injectable for tests). */
|
|
5
5
|
interface DocumentLike {
|
|
6
|
+
/** Optional: present on a real document, absent in the test double. */
|
|
7
|
+
addEventListener?(type: string, cb: (event: unknown) => void): void;
|
|
8
|
+
removeEventListener?(type: string, cb: (event: unknown) => void): void;
|
|
6
9
|
createElement(tag: string): HTMLElement;
|
|
7
10
|
}
|
|
8
11
|
//#endregion
|
|
9
12
|
//#region src/debug/index.d.ts
|
|
10
13
|
type DebugPanelId = "explorer" | "inspector" | "logs" | "stats" | "time";
|
|
14
|
+
/** Menu entries that DO something once rather than opening a panel. */
|
|
15
|
+
type DebugCommandId = "copyScene" | "captureRegion";
|
|
11
16
|
/** Menu entries that are switches rather than panels. */
|
|
12
17
|
type DebugSwitchId = "colliders";
|
|
13
18
|
/** `all` is every collider in the world; `selected` follows the Explorer. */
|
|
14
19
|
type ColliderMode = "off" | "all" | "selected";
|
|
15
|
-
type DebugMenuId = DebugPanelId | DebugSwitchId;
|
|
20
|
+
type DebugMenuId = DebugPanelId | DebugSwitchId | DebugCommandId;
|
|
16
21
|
/** An extra ☰ menu entry the host app supplies (e.g. "edit this scene"). */
|
|
17
22
|
interface DebugAction {
|
|
18
23
|
/** Menu label. Keep it a verb — the menu is a list of things you can do. */
|
|
@@ -37,6 +42,19 @@ interface DebugOverlayOptions {
|
|
|
37
42
|
* renderers): the chip shows engine stats only.
|
|
38
43
|
*/
|
|
39
44
|
statsSource?: () => Partial<RendererStats>;
|
|
45
|
+
/**
|
|
46
|
+
* The renderer's pixels, for the region capture. Wired by createGame like
|
|
47
|
+
* `statsSource`, because the overlay holds the engine and not the renderer.
|
|
48
|
+
*
|
|
49
|
+
* It must be `captureFrame()` and not `canvas.toDataURL()`: the drawing
|
|
50
|
+
* buffer is gone by the time anything outside a render can read it, and the
|
|
51
|
+
* image comes back black. That trap has been hit here before.
|
|
52
|
+
*/
|
|
53
|
+
frameSource?: () => Promise<{
|
|
54
|
+
pixels: Uint8Array;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
}>;
|
|
40
58
|
/** @internal Test seam — replaces `document`. */
|
|
41
59
|
doc?: DocumentLike;
|
|
42
60
|
}
|
|
@@ -48,6 +66,7 @@ declare class DebugOverlay {
|
|
|
48
66
|
private readonly doc;
|
|
49
67
|
private readonly statsSource?;
|
|
50
68
|
private readonly actions;
|
|
69
|
+
private readonly frameSource?;
|
|
51
70
|
private readonly panels;
|
|
52
71
|
private readonly cleanups;
|
|
53
72
|
private readonly menuButton;
|
|
@@ -75,7 +94,7 @@ declare class DebugOverlay {
|
|
|
75
94
|
private timeEls;
|
|
76
95
|
/** Pointer inside the inspector — it stops refreshing under your hand. */
|
|
77
96
|
private hovering;
|
|
78
|
-
constructor(engine: Engine, container: HTMLElement, doc: DocumentLike, statsSource?: (() => Partial<RendererStats>) | undefined, actions?: DebugAction[]);
|
|
97
|
+
constructor(engine: Engine, container: HTMLElement, doc: DocumentLike, statsSource?: (() => Partial<RendererStats>) | undefined, actions?: DebugAction[], frameSource?: DebugOverlayOptions["frameSource"] | undefined);
|
|
79
98
|
isOpen(id: DebugMenuId): boolean;
|
|
80
99
|
/** How much of the physics world the wireframes show. */
|
|
81
100
|
colliderMode: ColliderMode;
|
|
@@ -117,6 +136,28 @@ declare class DebugOverlay {
|
|
|
117
136
|
private trackEditing;
|
|
118
137
|
private pushLog;
|
|
119
138
|
/**
|
|
139
|
+
* The scene, on the clipboard, ready to paste into a conversation.
|
|
140
|
+
*
|
|
141
|
+
* This is half of "here is what I am looking at" — the half a screenshot
|
|
142
|
+
* cannot carry. The other half is the region capture below.
|
|
143
|
+
*/
|
|
144
|
+
copyScene(): Promise<boolean>;
|
|
145
|
+
/**
|
|
146
|
+
* Drag a rectangle over the game; the selection copies as an image.
|
|
147
|
+
*
|
|
148
|
+
* The pixels come from `frameSource` — the renderer's own end-of-frame read —
|
|
149
|
+
* and never from `canvas.toDataURL()`, which returns black once the frame has
|
|
150
|
+
* composited unless `preserveDrawingBuffer` is on (it is not, because it costs
|
|
151
|
+
* bandwidth on every frame).
|
|
152
|
+
*/
|
|
153
|
+
captureRegion(): void;
|
|
154
|
+
private capture;
|
|
155
|
+
private captureScale;
|
|
156
|
+
/** Crop the last frame to `drag` and put the image on the clipboard. */
|
|
157
|
+
private copyRegion;
|
|
158
|
+
/** A line that says what happened and gets out of the way. */
|
|
159
|
+
private toast;
|
|
160
|
+
/**
|
|
120
161
|
* Set game time, defensively. `timeScale` multiplies every dt in the engine,
|
|
121
162
|
* so a NaN from a text field would poison physics, timers and animation in
|
|
122
163
|
* one frame, and a negative would run the simulation backwards through code
|
|
@@ -144,4 +185,4 @@ declare class DebugOverlay {
|
|
|
144
185
|
private renderLogs;
|
|
145
186
|
}
|
|
146
187
|
//#endregion
|
|
147
|
-
export { ColliderMode, DebugAction, DebugMenuId, DebugOverlay, DebugOverlayOptions, DebugPanelId, DebugSwitchId, attachDebugOverlay };
|
|
188
|
+
export { ColliderMode, DebugAction, DebugCommandId, DebugMenuId, DebugOverlay, DebugOverlayOptions, DebugPanelId, DebugSwitchId, attachDebugOverlay };
|