incanto 0.60.0 → 0.62.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-check.mjs +13 -0
- package/bin/incanto-verify.mjs +134 -55
- package/dist/2d.d.ts +64 -3
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +5 -5
- package/dist/3d.js +5 -4
- package/dist/{pathfinding-BqWBb0kh.d.ts → audio-player-D5GJgb_x.d.ts} +103 -38
- package/dist/{behavior-DWKTUzKI.d.ts → behavior-DsgayMsH.d.ts} +79 -1
- package/dist/{create-game-ClnIb_M5.js → create-game-BpunnGPX.js} +78 -13
- package/dist/{create-game-BCm38FJV.js → create-game-Caut3bqN.js} +21 -294
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +1 -1
- package/dist/{duplicate-DJQd44CD.js → duplicate-E4FUs5Bn.js} +1 -1
- package/dist/editor.js +41 -19
- package/dist/{environment-presets-8cjF3t6w.js → environment-presets-BAWeOeqf.js} +50 -13
- package/dist/{gameplay-BBEjPFsR.js → gameplay-CaHqDiQD.js} +177 -24
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +97 -6
- package/dist/index.js +7 -7
- package/dist/{loader-D8n7TU8W.js → loader-DEe272nY.js} +900 -2
- package/dist/{loader-TvkRFbyL.d.ts → loader-DolLJWJn.d.ts} +13 -1
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/pathfinding-_fGrCFmH.d.ts +28 -0
- package/dist/{physics-2d-DqdVp1bt.js → physics-2d-BXmu2i7W.js} +11 -3
- package/dist/{physics-3d-BP0DZb_1.js → physics-3d-ClxP6Uv7.js} +121 -17
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-BSu2dWGC.js → register-CNh4FlbD.js} +104 -16
- package/dist/{register-Da3hXh2H.js → register-D3yx8D4r.js} +228 -16
- package/dist/{registry-WWcQcfMr.js → registry-CF70EArN.js} +55 -3
- package/dist/{replay-BCMK_VRP.d.ts → replay-C5x2vPF5.d.ts} +2 -2
- package/dist/{replay-BlNuIDdg.js → replay-DlgHItNv.js} +57 -197
- package/dist/{split-screen-CL5Yvxse.js → split-screen-CBM9wcMX.js} +3 -3
- package/dist/{split-screen-BQ3tAsf-.d.ts → split-screen-D7OopelJ.d.ts} +2 -2
- package/dist/{src-DFpXBMJN.js → src-CH00_JsR.js} +1 -1
- package/dist/{teardown-CTTwhWSe.js → teardown-C7qP-dcC.js} +1 -1
- package/dist/{test-BeZ95pqw.js → test-BMkg8zMV.js} +29 -16
- package/dist/test.d.ts +45 -4
- package/dist/test.js +2 -2
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-DSJries_.js → agent8-m5mtAO_A.js} +1 -1
- package/editor/assets/{debug-D15Wi5TO.js → debug-CPhzCT8f.js} +1 -1
- package/editor/assets/{index-BjC88k97.js → index-D422P4kW.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +91 -0
- package/skills/incanto-3d-models.md +1 -1
- package/skills/incanto-assets.md +10 -1
- package/skills/incanto-audio.md +21 -13
- package/skills/incanto-behaviors-and-scripts.md +44 -3
- package/skills/incanto-building-2d-games.md +32 -4
- package/skills/incanto-building-3d-games.md +1 -1
- package/skills/incanto-editor.md +1 -1
- package/skills/incanto-gameplay-behaviors.md +39 -2
- package/skills/incanto-hud.md +5 -3
- package/skills/incanto-localization.md +7 -0
- package/skills/incanto-node-reference.md +12 -0
- package/skills/incanto-physics-and-input.md +23 -2
- package/skills/incanto-scene-json-authoring.md +6 -1
- package/skills/incanto-verifying-your-game.md +31 -1
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
- package/dist/particle-sim-C5OfBbmU.d.ts +0 -77
|
@@ -551,6 +551,14 @@ interface SynthOptions {
|
|
|
551
551
|
seed?: number;
|
|
552
552
|
}
|
|
553
553
|
/**
|
|
554
|
+
* How long a preset lasts, in seconds — the same `attack + sustain + decay`
|
|
555
|
+
* `synthSfx` uses to size its buffer.
|
|
556
|
+
*
|
|
557
|
+
* `pitch` deliberately does not enter into it: it is a frequency multiplier,
|
|
558
|
+
* not a playback rate, so a preset played an octave up is the same length.
|
|
559
|
+
*/
|
|
560
|
+
declare function sfxDuration(params: SfxParams): number;
|
|
561
|
+
/**
|
|
554
562
|
* Render a preset to mono PCM in [-1, 1]. PURE: identical inputs → identical
|
|
555
563
|
* output (the only randomness is the seeded noise generator), so determinism is
|
|
556
564
|
* unit-testable headlessly. Length = ceil((attack+sustain+decay) * sampleRate).
|
|
@@ -1780,6 +1788,44 @@ declare class Engine {
|
|
|
1780
1788
|
tick(nowMs: number): void;
|
|
1781
1789
|
}
|
|
1782
1790
|
//#endregion
|
|
1791
|
+
//#region src/core/prop-range.d.ts
|
|
1792
|
+
/**
|
|
1793
|
+
* The numeric range a prop documents, checked at LOAD.
|
|
1794
|
+
*
|
|
1795
|
+
* The docs give ranges constantly — `metalness`/`roughness` "0..1 … `-1` = keep
|
|
1796
|
+
* authored", `targetHeight` "> 0 uniformly scales the model", `opacity` 0..1 —
|
|
1797
|
+
* and the schema had no way to say so, so every one was a type check and
|
|
1798
|
+
* nothing more. Measured on `ModelInstance3D`:
|
|
1799
|
+
*
|
|
1800
|
+
* ```
|
|
1801
|
+
* targetHeight: -1.8 NO ERROR (kept -1.8)
|
|
1802
|
+
* metalness: 5 NO ERROR (kept 5)
|
|
1803
|
+
* roughness: -3 NO ERROR (kept -3)
|
|
1804
|
+
* ```
|
|
1805
|
+
*
|
|
1806
|
+
* The two failure modes differ, which is why neither is noticed. A negative
|
|
1807
|
+
* `targetHeight` folds into the same branch as "not set", so the model keeps
|
|
1808
|
+
* its authored size and looks like the prop did nothing; `metalness: 5` reaches
|
|
1809
|
+
* three and the surface goes wrong. An author who mistypes a decimal point gets
|
|
1810
|
+
* a different wrong answer depending on which prop they mistyped it in, and no
|
|
1811
|
+
* message in either case.
|
|
1812
|
+
*/
|
|
1813
|
+
interface PropRange {
|
|
1814
|
+
/** Lowest accepted value. */
|
|
1815
|
+
min?: number;
|
|
1816
|
+
/** Highest accepted value. */
|
|
1817
|
+
max?: number;
|
|
1818
|
+
/** `min` is a bound the value must EXCEED, not reach (`targetHeight > 0`). */
|
|
1819
|
+
exclusiveMin?: boolean;
|
|
1820
|
+
/**
|
|
1821
|
+
* Values outside the range that mean something anyway.
|
|
1822
|
+
*
|
|
1823
|
+
* `metalness: -1` is "keep whatever the GLB authored" — a real value with a
|
|
1824
|
+
* real meaning, and the reason a range cannot just be a clamp.
|
|
1825
|
+
*/
|
|
1826
|
+
allow?: readonly number[];
|
|
1827
|
+
}
|
|
1828
|
+
//#endregion
|
|
1783
1829
|
//#region src/core/registry.d.ts
|
|
1784
1830
|
/** A node prop definition. The default both documents and types the prop. */
|
|
1785
1831
|
interface PropDef {
|
|
@@ -1808,6 +1854,38 @@ interface PropDef {
|
|
|
1808
1854
|
*/
|
|
1809
1855
|
required?: boolean;
|
|
1810
1856
|
/**
|
|
1857
|
+
* This prop names an entry in the scene's `assets{}` block.
|
|
1858
|
+
*
|
|
1859
|
+
* Declaring it is what makes a typo a LOAD error. The check already existed —
|
|
1860
|
+
* `AssetStore2D.resolve` throws `UNKNOWN_ASSET` — and it runs inside the
|
|
1861
|
+
* renderer, in a browser, when the texture is first needed. Headless the
|
|
1862
|
+
* scene loaded clean, so `incanto-check` certified a sprite that will draw
|
|
1863
|
+
* nothing, and the docs' promise that "the engine hard-fails on a raw URL"
|
|
1864
|
+
* was not true anywhere an author could see it.
|
|
1865
|
+
*
|
|
1866
|
+
* The same story as `nodePath` and `required`: the rule existed, nothing
|
|
1867
|
+
* routed to it at author time.
|
|
1868
|
+
*/
|
|
1869
|
+
/**
|
|
1870
|
+
* The numeric range this prop documents.
|
|
1871
|
+
*
|
|
1872
|
+
* Without it a documented range is prose only: `metalness: 5` and
|
|
1873
|
+
* `targetHeight: -1.8` both passed the kind check and then failed in two
|
|
1874
|
+
* different silent ways. See prop-range.
|
|
1875
|
+
*/
|
|
1876
|
+
range?: PropRange;
|
|
1877
|
+
asset?: {
|
|
1878
|
+
/** The `assets{}` entry `type` a `$ref` here must point at. */kind: "texture" | "spritesheet" | "model" | "animation";
|
|
1879
|
+
/**
|
|
1880
|
+
* A value that is not a `$ref` is an error.
|
|
1881
|
+
*
|
|
1882
|
+
* False for props that legitimately take something else — a raw URL
|
|
1883
|
+
* (`UiImage.src`, `Sprite3D.texture`) or the name of a clip baked into a
|
|
1884
|
+
* GLB (`ModelInstance3D.animation`).
|
|
1885
|
+
*/
|
|
1886
|
+
refOnly?: boolean;
|
|
1887
|
+
};
|
|
1888
|
+
/**
|
|
1811
1889
|
* For an OBJECT-valued prop shaped as a tagged union — `collider`, whose
|
|
1812
1890
|
* `shape` decides which other keys are required. One declaration gives an
|
|
1813
1891
|
* inspector both the dropdown AND something valid to write when the tag
|
|
@@ -1943,4 +2021,4 @@ declare function registeredBehaviors(): string[];
|
|
|
1943
2021
|
/** Test isolation helper. */
|
|
1944
2022
|
declare function clearBehaviors(): void;
|
|
1945
2023
|
//#endregion
|
|
1946
|
-
export { T_PREFIX as $, SettingsValues as A,
|
|
2024
|
+
export { T_PREFIX as $, SettingsValues as A, BusName as At, captureBehaviors as B, EngineStats as C, sfxDuration as Ct, QualityCaps as D, MusicTrack as Dt, DeviceHints as E, MusicManager as Et, BehaviorState as F, LogLevel as Ft, ORDER_GROUP_BASE as G, savesWithoutUid as H, RestoreReport as I, LogManager as It, effectiveOrder as J, OrderGroup as K, SaveSlot as L, Signal as Lt, readDeviceHints as M, NodeLifecycle as Mt, suggestQuality as N, SceneTree as Nt, QualityTier as O, PlayMusicOptions as Ot, Scene as P, LogEntry as Pt, Localization as Q, SaveSlots as R, SignalListener as Rt, Scheduler as S, SynthOptions as St, RendererStats as T, MusicBackend as Tt, SaveStore as U, restoreBehaviors as V, createSaveStore as W, BASE_LOCALE as X, resolveOrderGroups as Y, LocaleTables as Z, mergeStaticSignals as _, spatialPan as _t, clearBehaviors as a, EffectLog as at, Engine as b, SfxParams as bt, registeredBehaviors as c, isAudioContextAvailable as ct, PropSchema as d, Listener as dt, suggestLocale as et, clearRegistry as f, ROLLOFF_MODELS as ft, getNodeType as g, spatialGain as gt, getNodeSignals as h, Vec3 as ht, behaviorSignals as i, EffectKind as it, qualityEnvironment as j, Node as jt, Settings as k, AudioBuses as kt, NodeCtor as l, Voice as lt, getNodeSchema as m, SpatialParams as mt, BehaviorCtor as n, InputMap as nt, getBehavior as o, SfxEngine as ot, createNode as p, RolloffModel as pt, OrderGroupTable as q, behaviorSchema as r, EffectEvent as rt, registerBehavior as s, SfxPlayOptions as st, Behavior as t, translationKey as tt, PropDef as u, VoicePreset as ut, registerNode as v, SFX_PRESETS as vt, GameStats as w, synthSfx as wt, EngineOptions as x, SfxWave as xt, registeredTypes as y, SFX_PRESET_NAMES as yt, behaviorsWithoutSave as z };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import { _ as
|
|
2
|
+
import { d as computeViewport, f as resolveViewport, k as diagnose, n as loadScene, z as registerBehavior } from "./loader-DEe272nY.js";
|
|
3
|
+
import { _ as AudioPlayer, v as Engine } from "./register-D3yx8D4r.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-DESwnpOc.js";
|
|
6
|
-
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-
|
|
6
|
+
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-C7qP-dcC.js";
|
|
7
7
|
import { o as frameStats } from "./frame-report-BSMny7oe.js";
|
|
8
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
9
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
8
|
+
import { n as registerGameplayBehaviors } from "./gameplay-CaHqDiQD.js";
|
|
9
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-CNh4FlbD.js";
|
|
10
10
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
11
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
11
|
+
import { n as enablePhysics2D } from "./physics-2d-BXmu2i7W.js";
|
|
12
12
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
13
13
|
//#region src/2d/assets.ts
|
|
14
14
|
/**
|
|
@@ -199,14 +199,19 @@ function selectionOutline2D(node) {
|
|
|
199
199
|
function createSync2DScratch() {
|
|
200
200
|
return {
|
|
201
201
|
visited: /* @__PURE__ */ new Set(),
|
|
202
|
-
cameras: []
|
|
202
|
+
cameras: [],
|
|
203
|
+
emitters: []
|
|
203
204
|
};
|
|
204
205
|
}
|
|
206
|
+
function isSpatialConsumer(node) {
|
|
207
|
+
return node.spatial === true && typeof node._setSpatialPose === "function";
|
|
208
|
+
}
|
|
205
209
|
function syncTree2D(root, world, ui, assets, uiSize, scratch, opts) {
|
|
206
210
|
const s = scratch ?? createSync2DScratch();
|
|
207
211
|
s.visited.clear();
|
|
208
212
|
s.cameras.length = 0;
|
|
209
|
-
|
|
213
|
+
s.emitters.length = 0;
|
|
214
|
+
walk(root, world, ui, false, s.visited, s.cameras, s.emitters, assets, uiSize, opts?.ignoreStatic === true);
|
|
210
215
|
prune(world, s.visited);
|
|
211
216
|
prune(ui, s.visited);
|
|
212
217
|
let current = null;
|
|
@@ -218,9 +223,66 @@ function syncTree2D(root, world, ui, assets, uiSize, scratch, opts) {
|
|
|
218
223
|
}
|
|
219
224
|
}
|
|
220
225
|
if (!current) current = s.cameras[0] ?? null;
|
|
226
|
+
feedSpatialEmitters2D(s.emitters, current);
|
|
221
227
|
return { activeCamera: current };
|
|
222
228
|
}
|
|
223
|
-
|
|
229
|
+
const emitterScratch = new Vector3();
|
|
230
|
+
const listenerScratch = new Vector3();
|
|
231
|
+
const upScratch = new Vector3();
|
|
232
|
+
/**
|
|
233
|
+
* Give a 2D scene's spatial AudioPlayers the same per-frame pose the 3D sync
|
|
234
|
+
* gives its own.
|
|
235
|
+
*
|
|
236
|
+
* `spatial`, `refDistance`, `maxDistance` and `rolloff` have been props of
|
|
237
|
+
* `AudioPlayer` since positional audio landed, and in a 2D scene all four did
|
|
238
|
+
* NOTHING: the node only attenuates when an adapter pushes it a pose, and only
|
|
239
|
+
* the 3D one ever did. The scene loaded, `incanto check` said valid, and the
|
|
240
|
+
* sound played at full volume from the far side of the level.
|
|
241
|
+
*
|
|
242
|
+
* The listener is the active `Camera2D` — its world position is the view
|
|
243
|
+
* centre, its own rotation turns the stereo image with it, and the view looks
|
|
244
|
+
* down −Z like every other camera. Distances are therefore in the same units
|
|
245
|
+
* the rest of a 2D scene is in: PIXELS. `refDistance: 1` / `maxDistance: 50`
|
|
246
|
+
* are metre-shaped defaults and read as "silent one sprite away" here, so a 2D
|
|
247
|
+
* scene has to set them; `auditScene` says so when it sees the defaults.
|
|
248
|
+
*/
|
|
249
|
+
function feedSpatialEmitters2D(emitters, camera) {
|
|
250
|
+
if (emitters.length === 0 || !camera) return;
|
|
251
|
+
const view = camera._ensureObject2D();
|
|
252
|
+
view.updateWorldMatrix(true, false);
|
|
253
|
+
view.getWorldPosition(listenerScratch);
|
|
254
|
+
upScratch.set(0, 1, 0).applyQuaternion(view.quaternion);
|
|
255
|
+
const listener = {
|
|
256
|
+
position: [
|
|
257
|
+
listenerScratch.x,
|
|
258
|
+
listenerScratch.y,
|
|
259
|
+
0
|
|
260
|
+
],
|
|
261
|
+
forward: [
|
|
262
|
+
0,
|
|
263
|
+
0,
|
|
264
|
+
-1
|
|
265
|
+
],
|
|
266
|
+
up: [
|
|
267
|
+
upScratch.x,
|
|
268
|
+
upScratch.y,
|
|
269
|
+
upScratch.z
|
|
270
|
+
]
|
|
271
|
+
};
|
|
272
|
+
for (const { node, parent } of emitters) {
|
|
273
|
+
parent.updateWorldMatrix(true, false);
|
|
274
|
+
parent.getWorldPosition(emitterScratch);
|
|
275
|
+
node._setSpatialPose({
|
|
276
|
+
position: [
|
|
277
|
+
emitterScratch.x,
|
|
278
|
+
emitterScratch.y,
|
|
279
|
+
0
|
|
280
|
+
],
|
|
281
|
+
listener
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function walk(node, parentObj, ui, inUi, visited, cameras, emitters, assets, uiSize, ignoreStatic) {
|
|
224
286
|
let nextParent = parentObj;
|
|
225
287
|
let nextInUi = inUi;
|
|
226
288
|
if (node instanceof Node2D && !(node instanceof UILayer)) {
|
|
@@ -249,11 +311,14 @@ function walk(node, parentObj, ui, inUi, visited, cameras, assets, uiSize, ignor
|
|
|
249
311
|
visited.add(obj);
|
|
250
312
|
if (node instanceof Camera2D && !inUi) cameras.push(node);
|
|
251
313
|
nextParent = obj;
|
|
252
|
-
}
|
|
253
|
-
|
|
314
|
+
} else if (isSpatialConsumer(node)) emitters.push({
|
|
315
|
+
node,
|
|
316
|
+
parent: nextParent
|
|
317
|
+
});
|
|
318
|
+
for (const child of node.children) walk(child, nextParent, ui, nextInUi, visited, cameras, emitters, assets, uiSize, ignoreStatic);
|
|
254
319
|
if (node instanceof Node2D && !(node instanceof UILayer)) {
|
|
255
320
|
const obj = node._ensureObject2D();
|
|
256
|
-
if (node.static && !ignoreStatic) {
|
|
321
|
+
if (node.static && !ignoreStatic && node._staticReady(assets)) {
|
|
257
322
|
obj.userData.incantoStaticSynced = true;
|
|
258
323
|
obj.userData.incantoStatic = true;
|
|
259
324
|
} else if (obj.userData.incantoStaticSynced === true) {
|
|
@@ -474,7 +539,7 @@ var Renderer2D = class {
|
|
|
474
539
|
const { activeCamera } = syncTree2D(scene.root, this.worldScene, this.uiScene, this.assets, {
|
|
475
540
|
width: uiW,
|
|
476
541
|
height: uiH
|
|
477
|
-
}, this.syncScratch);
|
|
542
|
+
}, this.syncScratch, { ignoreStatic: this.ignoreStatic });
|
|
478
543
|
let center = vp ? {
|
|
479
544
|
x: vp.design[0] / 2,
|
|
480
545
|
y: vp.design[1] / 2
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { c as parseEnvironment3D, k as diagnose, n as loadScene, s as horizonColorFromSky, u as sunDirectionFromSky, z as registerBehavior } from "./loader-DEe272nY.js";
|
|
3
|
+
import { C as qualityRendering, _ as AudioPlayer, v as Engine, x as qualityCaps } from "./register-D3yx8D4r.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-DESwnpOc.js";
|
|
6
|
-
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-
|
|
6
|
+
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-C7qP-dcC.js";
|
|
7
7
|
import { o as frameStats } from "./frame-report-BSMny7oe.js";
|
|
8
|
-
import {
|
|
8
|
+
import { K as createCausticsQuad, V as PhysicsBody3D, W as Node3D, n as registerGameplayBehaviors } from "./gameplay-CaHqDiQD.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
|
-
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-
|
|
11
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
10
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-BAWeOeqf.js";
|
|
11
|
+
import { n as enablePhysics3D } from "./physics-3d-ClxP6Uv7.js";
|
|
12
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";
|
|
13
13
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
14
14
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -125,6 +125,10 @@ var AssetStore3D = class {
|
|
|
125
125
|
} else entry.scene = gltf.scene;
|
|
126
126
|
entry.animations = gltf.animations ?? [];
|
|
127
127
|
entry.status = "ready";
|
|
128
|
+
if (!hasAnyMesh(entry.scene)) {
|
|
129
|
+
entry.error = "loaded, but it contains no meshes — this is an animation-only GLB. A clip file belongs in `animation`, not `model`.";
|
|
130
|
+
diagnose(this.engine, "error", `incanto: '${url}' ${entry.error}`);
|
|
131
|
+
}
|
|
128
132
|
}, void 0, (error) => {
|
|
129
133
|
entry.status = "error";
|
|
130
134
|
entry.error = error instanceof Error ? error.message : String(error);
|
|
@@ -172,6 +176,16 @@ var AssetStore3D = class {
|
|
|
172
176
|
this.animations.clear();
|
|
173
177
|
}
|
|
174
178
|
};
|
|
179
|
+
/** Does this loaded scene graph contain anything that will actually draw? */
|
|
180
|
+
function hasAnyMesh(scene) {
|
|
181
|
+
const walk = scene?.traverse;
|
|
182
|
+
if (!walk) return true;
|
|
183
|
+
let found = false;
|
|
184
|
+
walk.call(scene, (o) => {
|
|
185
|
+
if (o.isMesh) found = true;
|
|
186
|
+
});
|
|
187
|
+
return found;
|
|
188
|
+
}
|
|
175
189
|
//#endregion
|
|
176
190
|
//#region src/core/yield-frame.ts
|
|
177
191
|
/**
|
|
@@ -226,293 +240,6 @@ function yieldToFrame(maxWaitMs = DEFAULT_MAX_WAIT_MS, raf = typeof requestAnima
|
|
|
226
240
|
});
|
|
227
241
|
}
|
|
228
242
|
//#endregion
|
|
229
|
-
//#region src/3d/environment.ts
|
|
230
|
-
const SKY_KEYS = [
|
|
231
|
-
"type",
|
|
232
|
-
"sunPosition",
|
|
233
|
-
"elevationDeg",
|
|
234
|
-
"azimuthDeg",
|
|
235
|
-
"turbidity",
|
|
236
|
-
"rayleigh"
|
|
237
|
-
];
|
|
238
|
-
const FOG_KEYS = [
|
|
239
|
-
"color",
|
|
240
|
-
"near",
|
|
241
|
-
"far"
|
|
242
|
-
];
|
|
243
|
-
const CLOUD_KEYS = [
|
|
244
|
-
"coverage",
|
|
245
|
-
"density",
|
|
246
|
-
"base",
|
|
247
|
-
"top",
|
|
248
|
-
"color",
|
|
249
|
-
"shadeColor",
|
|
250
|
-
"speed",
|
|
251
|
-
"scale"
|
|
252
|
-
];
|
|
253
|
-
const BLOOM_KEYS = ["threshold", "strength"];
|
|
254
|
-
const POST_KEYS = [
|
|
255
|
-
"vignette",
|
|
256
|
-
"saturation",
|
|
257
|
-
"contrast"
|
|
258
|
-
];
|
|
259
|
-
const SHADOW_KEYS = [
|
|
260
|
-
"mapSize",
|
|
261
|
-
"radius",
|
|
262
|
-
"static"
|
|
263
|
-
];
|
|
264
|
-
const SHADOW_MAP_SIZES = [1024, 2048];
|
|
265
|
-
const DEFAULT_TURBIDITY = 2;
|
|
266
|
-
const DEFAULT_RAYLEIGH = 1;
|
|
267
|
-
const DEFAULT_FOG_NEAR = 50;
|
|
268
|
-
const DEFAULT_FOG_FAR = 800;
|
|
269
|
-
/** Fallback haze when fog is declared without a sky to derive a horizon from. */
|
|
270
|
-
const DEFAULT_FOG_COLOR = "#cfd8e0";
|
|
271
|
-
const DEG2RAD = Math.PI / 180;
|
|
272
|
-
/** Parse + hard-validate the 3D slice of a scene's `environment` header. */
|
|
273
|
-
function parseEnvironment3D(env) {
|
|
274
|
-
return {
|
|
275
|
-
exposure: parseExposure(env?.exposure),
|
|
276
|
-
sky: parseSky(env?.sky),
|
|
277
|
-
fog: parseFog(env?.fog, env?.sky !== void 0),
|
|
278
|
-
clouds: parseClouds(env?.clouds),
|
|
279
|
-
bloom: parseBloom(env?.bloom),
|
|
280
|
-
post: parsePost(env?.post),
|
|
281
|
-
shadows: parseShadows(env?.shadows)
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* elevation/azimuth (degrees) → unit direction. Matches three's Sky example
|
|
286
|
-
* (`Vector3.setFromSphericalCoords`): azimuth 0 = +Z, 90 = +X; elevation 90 =
|
|
287
|
-
* straight up.
|
|
288
|
-
*/
|
|
289
|
-
function sunDirectionFromElevationAzimuth(elevationDeg, azimuthDeg) {
|
|
290
|
-
const phi = (90 - elevationDeg) * DEG2RAD;
|
|
291
|
-
const theta = azimuthDeg * DEG2RAD;
|
|
292
|
-
return [
|
|
293
|
-
Math.sin(phi) * Math.sin(theta),
|
|
294
|
-
Math.cos(phi),
|
|
295
|
-
Math.sin(phi) * Math.cos(theta)
|
|
296
|
-
];
|
|
297
|
-
}
|
|
298
|
-
/** The sky's sun as a UNIT vector — what Water3D/Foliage3D uniforms consume. */
|
|
299
|
-
function sunDirectionFromSky(sky) {
|
|
300
|
-
const [x, y, z] = sky.sunPosition;
|
|
301
|
-
const len = Math.hypot(x, y, z) || 1;
|
|
302
|
-
return [
|
|
303
|
-
x / len,
|
|
304
|
-
y / len,
|
|
305
|
-
z / len
|
|
306
|
-
];
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* A horizon-ish haze color derived from the sky config — the default fog
|
|
310
|
-
* color. Cheap model, judged by eye: clear skies haze blue-grey, turbid skies
|
|
311
|
-
* whiten, and a low sun warms the band toward amber.
|
|
312
|
-
*/
|
|
313
|
-
function horizonColorFromSky(sky) {
|
|
314
|
-
const clear = [
|
|
315
|
-
191,
|
|
316
|
-
213,
|
|
317
|
-
232
|
|
318
|
-
];
|
|
319
|
-
const hazy = [
|
|
320
|
-
233,
|
|
321
|
-
228,
|
|
322
|
-
217
|
|
323
|
-
];
|
|
324
|
-
const warm = [
|
|
325
|
-
242,
|
|
326
|
-
201,
|
|
327
|
-
150
|
|
328
|
-
];
|
|
329
|
-
const t = clamp01((sky.turbidity - DEFAULT_TURBIDITY) / 8);
|
|
330
|
-
const dir = sunDirectionFromSky(sky);
|
|
331
|
-
const w = clamp01((18 - Math.asin(clamp(dir[1], -1, 1)) / DEG2RAD) / 18) * .8;
|
|
332
|
-
const mix = (i) => Math.round(lerp(lerp(clear[i], hazy[i], t), warm[i], w));
|
|
333
|
-
return `#${[
|
|
334
|
-
mix(0),
|
|
335
|
-
mix(1),
|
|
336
|
-
mix(2)
|
|
337
|
-
].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
|
|
338
|
-
}
|
|
339
|
-
function parseExposure(value) {
|
|
340
|
-
if (value === void 0) return 1;
|
|
341
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new IncantoError("BAD_FORMAT", `environment.exposure must be a finite number > 0 (tone-mapping exposure, default 1), got ${JSON.stringify(value)}.`, { prop: "exposure" });
|
|
342
|
-
return value;
|
|
343
|
-
}
|
|
344
|
-
function parseSky(value) {
|
|
345
|
-
if (value === void 0) return null;
|
|
346
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.sky must be an object ({ type?: "atmosphere", sunPosition? | elevationDeg?+azimuthDeg?, turbidity?, rayleigh? }), got ${JSON.stringify(value)}.`, { prop: "sky" });
|
|
347
|
-
const sky = value;
|
|
348
|
-
for (const key of Object.keys(sky)) if (!SKY_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.sky has unknown key '${key}'. Valid keys: [${SKY_KEYS.join(", ")}].`, {
|
|
349
|
-
prop: "sky",
|
|
350
|
-
validOptions: SKY_KEYS
|
|
351
|
-
});
|
|
352
|
-
if (sky.type !== void 0 && sky.type !== "atmosphere") throw new IncantoError("BAD_FORMAT", `environment.sky.type must be 'atmosphere' (the only sky type so far), got ${JSON.stringify(sky.type)}.`, {
|
|
353
|
-
prop: "sky",
|
|
354
|
-
validOptions: ["atmosphere"]
|
|
355
|
-
});
|
|
356
|
-
const hasAngles = sky.elevationDeg !== void 0 || sky.azimuthDeg !== void 0;
|
|
357
|
-
if (sky.sunPosition !== void 0 && hasAngles) throw new IncantoError("BAD_FORMAT", `environment.sky takes sunPosition OR elevationDeg/azimuthDeg, not both.`, {
|
|
358
|
-
prop: "sky",
|
|
359
|
-
validOptions: ["sunPosition", "elevationDeg+azimuthDeg"]
|
|
360
|
-
});
|
|
361
|
-
let sunPosition;
|
|
362
|
-
if (sky.sunPosition !== void 0) {
|
|
363
|
-
const sp = sky.sunPosition;
|
|
364
|
-
if (!Array.isArray(sp) || sp.length !== 3 || !sp.every((v) => typeof v === "number" && Number.isFinite(v)) || Math.hypot(sp[0], sp[1], sp[2]) === 0) throw new IncantoError("BAD_FORMAT", `environment.sky.sunPosition must be a non-zero [x, y, z] vector, got ${JSON.stringify(sp)}.`, { prop: "sky" });
|
|
365
|
-
sunPosition = [
|
|
366
|
-
sp[0],
|
|
367
|
-
sp[1],
|
|
368
|
-
sp[2]
|
|
369
|
-
];
|
|
370
|
-
} else {
|
|
371
|
-
const elevation = numberOr(sky.elevationDeg, 32, "sky.elevationDeg");
|
|
372
|
-
const azimuth = numberOr(sky.azimuthDeg, 135, "sky.azimuthDeg");
|
|
373
|
-
if (elevation < -90 || elevation > 90) throw new IncantoError("BAD_FORMAT", `environment.sky.elevationDeg must be in [-90, 90] (degrees above the horizon), got ${elevation}.`, { prop: "sky" });
|
|
374
|
-
sunPosition = sunDirectionFromElevationAzimuth(elevation, azimuth);
|
|
375
|
-
}
|
|
376
|
-
const turbidity = numberOr(sky.turbidity, DEFAULT_TURBIDITY, "sky.turbidity");
|
|
377
|
-
if (turbidity <= 0) throw new IncantoError("BAD_FORMAT", `environment.sky.turbidity must be > 0 (atmospheric haze; 2 ≈ clear day), got ${turbidity}.`, { prop: "sky" });
|
|
378
|
-
const rayleigh = numberOr(sky.rayleigh, DEFAULT_RAYLEIGH, "sky.rayleigh");
|
|
379
|
-
if (rayleigh < 0) throw new IncantoError("BAD_FORMAT", `environment.sky.rayleigh must be >= 0 (Rayleigh scattering; 1 ≈ earth-like), got ${rayleigh}.`, { prop: "sky" });
|
|
380
|
-
return {
|
|
381
|
-
sunPosition,
|
|
382
|
-
turbidity,
|
|
383
|
-
rayleigh
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
function parseFog(value, hasSky) {
|
|
387
|
-
if (value === void 0) return null;
|
|
388
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.fog must be an object ({ color?, near?, far? }), got ${JSON.stringify(value)}.`, { prop: "fog" });
|
|
389
|
-
const fog = value;
|
|
390
|
-
for (const key of Object.keys(fog)) if (!FOG_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.fog has unknown key '${key}'. Valid keys: [${FOG_KEYS.join(", ")}].`, {
|
|
391
|
-
prop: "fog",
|
|
392
|
-
validOptions: FOG_KEYS
|
|
393
|
-
});
|
|
394
|
-
if (fog.color !== void 0 && typeof fog.color !== "string") throw new IncantoError("BAD_FORMAT", `environment.fog.color must be a hex color string, got ${JSON.stringify(fog.color)}.`, { prop: "fog" });
|
|
395
|
-
const near = numberOr(fog.near, DEFAULT_FOG_NEAR, "fog.near");
|
|
396
|
-
const far = numberOr(fog.far, DEFAULT_FOG_FAR, "fog.far");
|
|
397
|
-
if (near < 0) throw new IncantoError("BAD_FORMAT", `environment.fog.near must be >= 0 meters, got ${near}.`, { prop: "fog" });
|
|
398
|
-
if (far <= near) throw new IncantoError("BAD_FORMAT", `environment.fog.far must be > near (got near ${near}, far ${far}).`, { prop: "fog" });
|
|
399
|
-
return {
|
|
400
|
-
color: fog.color ?? (hasSky ? "" : DEFAULT_FOG_COLOR),
|
|
401
|
-
near,
|
|
402
|
-
far
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
function parseClouds(value) {
|
|
406
|
-
if (value === void 0) return null;
|
|
407
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.clouds must be an object ({ coverage?, density?, base?, top?, color?, shadeColor?, speed?, scale? }), got ${JSON.stringify(value)}.`, { prop: "clouds" });
|
|
408
|
-
const c = value;
|
|
409
|
-
for (const key of Object.keys(c)) if (!CLOUD_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.clouds has unknown key '${key}'. Valid keys: [${CLOUD_KEYS.join(", ")}].`, {
|
|
410
|
-
prop: "clouds",
|
|
411
|
-
validOptions: CLOUD_KEYS
|
|
412
|
-
});
|
|
413
|
-
for (const k of ["color", "shadeColor"]) if (c[k] !== void 0 && typeof c[k] !== "string") throw new IncantoError("BAD_FORMAT", `environment.clouds.${k} must be a hex color string, got ${JSON.stringify(c[k])}.`, { prop: "clouds" });
|
|
414
|
-
const coverage = numberOr(c.coverage, .5, "clouds.coverage");
|
|
415
|
-
if (coverage < 0 || coverage > 1) throw new IncantoError("BAD_FORMAT", `environment.clouds.coverage must be in [0, 1] (how much sky is cloudy), got ${coverage}.`, { prop: "clouds" });
|
|
416
|
-
const density = numberOr(c.density, 1, "clouds.density");
|
|
417
|
-
if (density < 0) throw new IncantoError("BAD_FORMAT", `environment.clouds.density must be >= 0 (optical thickness), got ${density}.`, { prop: "clouds" });
|
|
418
|
-
const base = numberOr(c.base, 120, "clouds.base");
|
|
419
|
-
const top = numberOr(c.top, 320, "clouds.top");
|
|
420
|
-
if (top <= base) throw new IncantoError("BAD_FORMAT", `environment.clouds.top must be > base (got base ${base}, top ${top}).`, { prop: "clouds" });
|
|
421
|
-
const speed = numberOr(c.speed, 1, "clouds.speed");
|
|
422
|
-
const scale = numberOr(c.scale, 240, "clouds.scale");
|
|
423
|
-
if (scale <= 0) throw new IncantoError("BAD_FORMAT", `environment.clouds.scale must be > 0 (feature size in world units), got ${scale}.`, { prop: "clouds" });
|
|
424
|
-
return {
|
|
425
|
-
coverage,
|
|
426
|
-
density,
|
|
427
|
-
base,
|
|
428
|
-
top,
|
|
429
|
-
color: c.color ?? "#ffffff",
|
|
430
|
-
shadeColor: c.shadeColor ?? "#9fb0c8",
|
|
431
|
-
speed,
|
|
432
|
-
scale
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
function parseBloom(value) {
|
|
436
|
-
if (value === void 0) return null;
|
|
437
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.bloom must be an object ({ threshold?, strength? }), got ${JSON.stringify(value)}.`, { prop: "bloom" });
|
|
438
|
-
const b = value;
|
|
439
|
-
for (const key of Object.keys(b)) if (!BLOOM_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.bloom has unknown key '${key}'. Valid keys: [${BLOOM_KEYS.join(", ")}].`, {
|
|
440
|
-
prop: "bloom",
|
|
441
|
-
validOptions: BLOOM_KEYS
|
|
442
|
-
});
|
|
443
|
-
const threshold = numberOr(b.threshold, 1, "bloom.threshold");
|
|
444
|
-
if (threshold < 0 || threshold > 8) throw new IncantoError("BAD_FORMAT", `environment.bloom.threshold must be in [0, 8] (linear-HDR luminance; 1 = white), got ${threshold}.`, { prop: "bloom" });
|
|
445
|
-
const strength = numberOr(b.strength, .8, "bloom.strength");
|
|
446
|
-
if (strength < 0) throw new IncantoError("BAD_FORMAT", `environment.bloom.strength must be >= 0, got ${strength}.`, { prop: "bloom" });
|
|
447
|
-
return {
|
|
448
|
-
threshold,
|
|
449
|
-
strength
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
function parsePost(value) {
|
|
453
|
-
if (value === void 0) return null;
|
|
454
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.post must be an object ({ vignette?, saturation?, contrast? }), got ${JSON.stringify(value)}.`, { prop: "post" });
|
|
455
|
-
const p = value;
|
|
456
|
-
for (const key of Object.keys(p)) if (!POST_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.post has unknown key '${key}'. Valid keys: [${POST_KEYS.join(", ")}].`, {
|
|
457
|
-
prop: "post",
|
|
458
|
-
validOptions: POST_KEYS
|
|
459
|
-
});
|
|
460
|
-
const vignette = numberOr(p.vignette, 0, "post.vignette");
|
|
461
|
-
if (vignette < 0 || vignette > 1) throw new IncantoError("BAD_FORMAT", `environment.post.vignette must be in [0, 1], got ${vignette}.`, { prop: "post" });
|
|
462
|
-
const saturation = numberOr(p.saturation, 1, "post.saturation");
|
|
463
|
-
if (saturation < 0 || saturation > 4) throw new IncantoError("BAD_FORMAT", `environment.post.saturation must be in [0, 4] (1 = neutral), got ${saturation}.`, { prop: "post" });
|
|
464
|
-
const contrast = numberOr(p.contrast, 1, "post.contrast");
|
|
465
|
-
if (contrast < .2 || contrast > 3) throw new IncantoError("BAD_FORMAT", `environment.post.contrast must be in [0.2, 3] (1 = neutral), got ${contrast}.`, { prop: "post" });
|
|
466
|
-
return {
|
|
467
|
-
vignette,
|
|
468
|
-
saturation,
|
|
469
|
-
contrast
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
function parseShadows(value) {
|
|
473
|
-
if (value === void 0) return null;
|
|
474
|
-
if (value === false) return false;
|
|
475
|
-
if (value === true) return {
|
|
476
|
-
mapSize: 2048,
|
|
477
|
-
radius: 1,
|
|
478
|
-
static: false
|
|
479
|
-
};
|
|
480
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new IncantoError("BAD_FORMAT", `environment.shadows must be true, false or an object ({ mapSize?, radius? }), got ${JSON.stringify(value)}.`, { prop: "shadows" });
|
|
481
|
-
const shadows = value;
|
|
482
|
-
for (const key of Object.keys(shadows)) if (!SHADOW_KEYS.includes(key)) throw new IncantoError("BAD_FORMAT", `environment.shadows has unknown key '${key}'. Valid keys: [${SHADOW_KEYS.join(", ")}].`, {
|
|
483
|
-
prop: "shadows",
|
|
484
|
-
validOptions: SHADOW_KEYS
|
|
485
|
-
});
|
|
486
|
-
const mapSize = shadows.mapSize === void 0 ? 2048 : shadows.mapSize;
|
|
487
|
-
if (!SHADOW_MAP_SIZES.includes(mapSize)) throw new IncantoError("BAD_FORMAT", `environment.shadows.mapSize must be one of [${SHADOW_MAP_SIZES.join(", ")}], got ${JSON.stringify(shadows.mapSize)}.`, {
|
|
488
|
-
prop: "shadows",
|
|
489
|
-
validOptions: SHADOW_MAP_SIZES.map(String)
|
|
490
|
-
});
|
|
491
|
-
const radius = numberOr(shadows.radius, 1, "shadows.radius");
|
|
492
|
-
if (radius < 0) throw new IncantoError("BAD_FORMAT", `environment.shadows.radius must be >= 0, got ${radius}.`, { prop: "shadows" });
|
|
493
|
-
const staticShadows = shadows.static === void 0 ? false : shadows.static;
|
|
494
|
-
if (typeof staticShadows !== "boolean") throw new IncantoError("BAD_FORMAT", `environment.shadows.static must be a boolean, got ${JSON.stringify(shadows.static)}.`, { prop: "shadows" });
|
|
495
|
-
return {
|
|
496
|
-
mapSize,
|
|
497
|
-
radius,
|
|
498
|
-
static: staticShadows
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
function numberOr(value, fallback, at) {
|
|
502
|
-
if (value === void 0) return fallback;
|
|
503
|
-
if (typeof value !== "number" || !Number.isFinite(value)) throw new IncantoError("BAD_FORMAT", `environment.${at} must be a finite number, got ${JSON.stringify(value)}.`, { prop: at });
|
|
504
|
-
return value;
|
|
505
|
-
}
|
|
506
|
-
function clamp(v, min, max) {
|
|
507
|
-
return Math.min(Math.max(v, min), max);
|
|
508
|
-
}
|
|
509
|
-
function clamp01(v) {
|
|
510
|
-
return clamp(v, 0, 1);
|
|
511
|
-
}
|
|
512
|
-
function lerp(a, b, t) {
|
|
513
|
-
return a + (b - a) * t;
|
|
514
|
-
}
|
|
515
|
-
//#endregion
|
|
516
243
|
//#region src/3d/environment-runtime.ts
|
|
517
244
|
/**
|
|
518
245
|
* Live environment editing — the renderer re-applies `scene.environment`
|
|
@@ -2666,4 +2393,4 @@ function wireAudioUnlock(engine, root, canvas) {
|
|
|
2666
2393
|
return detachAll;
|
|
2667
2394
|
}
|
|
2668
2395
|
//#endregion
|
|
2669
|
-
export { Environment3D as a,
|
|
2396
|
+
export { Environment3D as a, syncTree as i, create_game_exports as n, setEnvironment3D as o, Renderer3D as r, AssetStore3D as s, createGame3D as t };
|
package/dist/debug.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ft as LogLevel, T as RendererStats, b as Engine } from "./behavior-DsgayMsH.js";
|
|
2
2
|
|
|
3
3
|
//#region src/debug/panel.d.ts
|
|
4
4
|
/** Minimal document surface the overlay needs (injectable for tests). */
|
package/dist/debug.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as jsonClone } from "./json-CwwhxQgb.js";
|
|
2
|
-
import { s as mergeStaticProps } from "./registry-
|
|
2
|
+
import { s as mergeStaticProps } from "./registry-CF70EArN.js";
|
|
3
3
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
4
4
|
//#region src/debug/capture.ts
|
|
5
5
|
/**
|