@jgengine/core 0.8.0 → 0.10.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/CHANGELOG.md +36 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.d.ts +13 -0
- package/dist/ai/spawnDirector.js +41 -17
- package/dist/ai/spawnPoint.d.ts +5 -0
- package/dist/ai/spawnPoint.js +5 -0
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +14 -0
- package/dist/authoring.js +14 -0
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +17 -2
- package/dist/combat/abilityKit.js +62 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/dotField.d.ts +43 -0
- package/dist/combat/dotField.js +48 -0
- package/dist/combat/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +214 -129
- package/dist/combat/regenShield.d.ts +35 -0
- package/dist/combat/regenShield.js +45 -0
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +52 -0
- package/dist/combat/shotOrigin.js +86 -0
- package/dist/combat/telegraph.d.ts +25 -0
- package/dist/combat/telegraph.js +47 -0
- package/dist/crafting/recipe.js +4 -1
- package/dist/devtools/devtools.d.ts +72 -24
- package/dist/devtools/devtools.js +449 -101
- package/dist/devtools/transformTunables.js +3 -3
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/editor/commands.d.ts +65 -0
- package/dist/editor/commands.js +193 -0
- package/dist/editor/document.d.ts +40 -0
- package/dist/editor/document.js +153 -0
- package/dist/editor/index.d.ts +30 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/types.d.ts +69 -0
- package/dist/editor/types.js +29 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +2 -0
- package/dist/game/chat.js +9 -1
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/connectedPlayers.d.ts +28 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +44 -1
- package/dist/game/defineGame.js +1 -0
- package/dist/game/events.d.ts +25 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/lootTable.d.ts +14 -1
- package/dist/game/lootTable.js +28 -7
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/playableGame.d.ts +116 -4
- package/dist/game/playableGame.js +1 -1
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +39 -1
- package/dist/game/race.js +96 -11
- package/dist/game/recordBook.d.ts +27 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/input/actionBindings.d.ts +2 -0
- package/dist/input/actionBindings.js +1 -0
- package/dist/input/axisInput.d.ts +33 -3
- package/dist/input/axisInput.js +60 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +6 -0
- package/dist/input/touchScheme.js +45 -7
- package/dist/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/proximityPrompt.d.ts +3 -1
- package/dist/interaction/proximityPrompt.js +3 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +44 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/movementModel.d.ts +4 -0
- package/dist/movement/movementModel.js +13 -0
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +181 -0
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/presenceModel.d.ts +14 -2
- package/dist/multiplayer/presenceModel.js +15 -3
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navGrid.js +30 -4
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +38 -0
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +13 -6
- package/dist/physics/physicsWorld.js +169 -40
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/vehicleBody.js +2 -1
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +2 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/render/postProcessing.d.ts +56 -0
- package/dist/render/postProcessing.js +1 -0
- package/dist/runtime/adapter.d.ts +19 -0
- package/dist/runtime/adapter.js +11 -5
- package/dist/runtime/cameraDirector.d.ts +6 -1
- package/dist/runtime/cameraDirector.js +6 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +72 -8
- package/dist/runtime/gameContext.js +374 -61
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +9 -1
- package/dist/runtime/hostPersistence.js +23 -8
- package/dist/runtime/hostedGameRunner.d.ts +49 -0
- package/dist/runtime/hostedGameRunner.js +62 -0
- package/dist/runtime/hostedWorldSession.d.ts +65 -0
- package/dist/runtime/hostedWorldSession.js +57 -0
- package/dist/runtime/inputSnapshot.d.ts +18 -0
- package/dist/runtime/inputSnapshot.js +7 -0
- package/dist/runtime/motionIntents.d.ts +16 -6
- package/dist/runtime/motionIntents.js +31 -2
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/runtime/worldChannel.d.ts +58 -0
- package/dist/runtime/worldChannel.js +69 -0
- package/dist/runtime/worldMirror.d.ts +24 -0
- package/dist/runtime/worldMirror.js +31 -0
- package/dist/runtime/worldReplication.d.ts +46 -0
- package/dist/runtime/worldReplication.js +185 -0
- package/dist/runtime/worldSnapshot.d.ts +17 -0
- package/dist/runtime/worldSnapshot.js +15 -0
- package/dist/scene/assetCatalog.d.ts +1 -0
- package/dist/scene/colliders.d.ts +50 -0
- package/dist/scene/colliders.js +101 -0
- package/dist/scene/entityStats.d.ts +4 -0
- package/dist/scene/entityStats.js +21 -0
- package/dist/scene/entityStore.d.ts +51 -5
- package/dist/scene/entityStore.js +119 -12
- package/dist/scene/objectQuery.d.ts +13 -2
- package/dist/scene/objectQuery.js +34 -2
- package/dist/scene/objectStore.d.ts +2 -0
- package/dist/scene/objectStore.js +31 -12
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +11 -16
- package/dist/scene/spatial.js +99 -38
- package/dist/sensor/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.d.ts +4 -0
- package/dist/store/observableKeyedStore.js +17 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +2 -2
- package/dist/tactics/fallingGrid.js +2 -2
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/ui/gameLayout.d.ts +125 -0
- package/dist/ui/gameLayout.js +129 -0
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +212 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +21 -4
- package/dist/world/environmentSummary.js +22 -3
- package/dist/world/features.d.ts +185 -8
- package/dist/world/features.js +64 -6
- package/dist/world/gridInstances.d.ts +7 -8
- package/dist/world/gridInstances.js +145 -7
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/roads.d.ts +47 -0
- package/dist/world/roads.js +146 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terrain.d.ts +69 -3
- package/dist/world/terrain.js +309 -16
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +3013 -1726
- package/package.json +12 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { DEFAULT_STREAMING_SETTINGS, mergeStreamingSettings } from "./settings.js";
|
|
2
|
+
import { nowMs } from "./diagnostics.js";
|
|
3
|
+
export function createAssetStreamingSystem(options) {
|
|
4
|
+
const settings = mergeStreamingSettings(DEFAULT_STREAMING_SETTINGS, options.settings ?? {});
|
|
5
|
+
const clock = options.now ?? nowMs;
|
|
6
|
+
const records = new Map();
|
|
7
|
+
const queued = new Set();
|
|
8
|
+
const signals = new Map();
|
|
9
|
+
const inFlight = new Set();
|
|
10
|
+
let unloadedTotal = 0;
|
|
11
|
+
let cancelledTotal = 0;
|
|
12
|
+
function ensure(id) {
|
|
13
|
+
let record = records.get(id);
|
|
14
|
+
if (record === undefined) {
|
|
15
|
+
record = { id, state: "unloaded", bytes: 0, priority: 0, refCount: 0, pinned: false, lastActiveMs: clock(), value: undefined };
|
|
16
|
+
records.set(id, record);
|
|
17
|
+
}
|
|
18
|
+
return record;
|
|
19
|
+
}
|
|
20
|
+
function enqueue(record, priority) {
|
|
21
|
+
if (record.state === "loaded" || record.state === "loading") {
|
|
22
|
+
record.priority = Math.max(record.priority, priority);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
record.state = "queued";
|
|
26
|
+
record.priority = Math.max(record.priority, priority);
|
|
27
|
+
queued.add(record.id);
|
|
28
|
+
}
|
|
29
|
+
function startLoad(record) {
|
|
30
|
+
queued.delete(record.id);
|
|
31
|
+
record.state = "loading";
|
|
32
|
+
const signal = { cancelled: false };
|
|
33
|
+
signals.set(record.id, signal);
|
|
34
|
+
const promise = options
|
|
35
|
+
.load(record.id, signal)
|
|
36
|
+
.then((result) => {
|
|
37
|
+
signals.delete(record.id);
|
|
38
|
+
if (signal.cancelled || !records.has(record.id)) {
|
|
39
|
+
record.state = "unloaded";
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
record.state = "loaded";
|
|
43
|
+
record.bytes = result.bytes ?? 0;
|
|
44
|
+
record.value = result.value;
|
|
45
|
+
record.lastActiveMs = clock();
|
|
46
|
+
})
|
|
47
|
+
.catch(() => {
|
|
48
|
+
signals.delete(record.id);
|
|
49
|
+
if (!signal.cancelled)
|
|
50
|
+
record.state = "error";
|
|
51
|
+
})
|
|
52
|
+
.finally(() => {
|
|
53
|
+
inFlight.delete(promise);
|
|
54
|
+
});
|
|
55
|
+
inFlight.add(promise);
|
|
56
|
+
}
|
|
57
|
+
function evictable(record, now) {
|
|
58
|
+
if (record.state !== "loaded")
|
|
59
|
+
return false;
|
|
60
|
+
if (record.pinned || record.refCount > 0)
|
|
61
|
+
return false;
|
|
62
|
+
if (record.bytes > 0 && record.bytes <= settings.keepResidentBytes)
|
|
63
|
+
return false;
|
|
64
|
+
return now - record.lastActiveMs >= settings.unloadGraceSeconds * 1000;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
request(assetId, priority = 0) {
|
|
68
|
+
const record = ensure(assetId);
|
|
69
|
+
record.lastActiveMs = clock();
|
|
70
|
+
enqueue(record, priority);
|
|
71
|
+
},
|
|
72
|
+
retain(assetId) {
|
|
73
|
+
ensure(assetId).refCount += 1;
|
|
74
|
+
},
|
|
75
|
+
release(assetId) {
|
|
76
|
+
const record = records.get(assetId);
|
|
77
|
+
if (record !== undefined && record.refCount > 0)
|
|
78
|
+
record.refCount -= 1;
|
|
79
|
+
},
|
|
80
|
+
pin(assetId) {
|
|
81
|
+
ensure(assetId).pinned = true;
|
|
82
|
+
},
|
|
83
|
+
unpin(assetId) {
|
|
84
|
+
const record = records.get(assetId);
|
|
85
|
+
if (record !== undefined)
|
|
86
|
+
record.pinned = false;
|
|
87
|
+
},
|
|
88
|
+
markActive(assetId) {
|
|
89
|
+
const record = records.get(assetId);
|
|
90
|
+
if (record !== undefined)
|
|
91
|
+
record.lastActiveMs = clock();
|
|
92
|
+
},
|
|
93
|
+
tick() {
|
|
94
|
+
const now = clock();
|
|
95
|
+
if (queued.size > 0) {
|
|
96
|
+
const pending = [];
|
|
97
|
+
for (const id of queued) {
|
|
98
|
+
const record = records.get(id);
|
|
99
|
+
if (record !== undefined)
|
|
100
|
+
pending.push(record);
|
|
101
|
+
}
|
|
102
|
+
pending.sort((a, b) => b.priority - a.priority);
|
|
103
|
+
const budget = Math.min(settings.maxLoadsPerFrame, pending.length);
|
|
104
|
+
for (let i = 0; i < budget; i += 1)
|
|
105
|
+
startLoad(pending[i]);
|
|
106
|
+
}
|
|
107
|
+
let unloads = 0;
|
|
108
|
+
for (const record of records.values()) {
|
|
109
|
+
if (unloads >= settings.maxUnloadsPerFrame)
|
|
110
|
+
break;
|
|
111
|
+
if (!evictable(record, now))
|
|
112
|
+
continue;
|
|
113
|
+
options.unload?.(record.id);
|
|
114
|
+
record.state = "unloaded";
|
|
115
|
+
record.bytes = 0;
|
|
116
|
+
record.value = undefined;
|
|
117
|
+
unloadedTotal += 1;
|
|
118
|
+
unloads += 1;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
cancel(assetId) {
|
|
122
|
+
if (queued.delete(assetId)) {
|
|
123
|
+
const record = records.get(assetId);
|
|
124
|
+
if (record !== undefined)
|
|
125
|
+
record.state = "unloaded";
|
|
126
|
+
cancelledTotal += 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const signal = signals.get(assetId);
|
|
130
|
+
if (signal !== undefined) {
|
|
131
|
+
signal.cancelled = true;
|
|
132
|
+
const record = records.get(assetId);
|
|
133
|
+
if (record !== undefined)
|
|
134
|
+
record.state = "unloaded";
|
|
135
|
+
cancelledTotal += 1;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
stateOf(assetId) {
|
|
139
|
+
return records.get(assetId)?.state;
|
|
140
|
+
},
|
|
141
|
+
isLoaded(assetId) {
|
|
142
|
+
return records.get(assetId)?.state === "loaded";
|
|
143
|
+
},
|
|
144
|
+
record(assetId) {
|
|
145
|
+
return records.get(assetId);
|
|
146
|
+
},
|
|
147
|
+
stats() {
|
|
148
|
+
let queuedCount = 0, loading = 0, loaded = 0, errored = 0, bytes = 0;
|
|
149
|
+
for (const record of records.values()) {
|
|
150
|
+
switch (record.state) {
|
|
151
|
+
case "queued":
|
|
152
|
+
queuedCount += 1;
|
|
153
|
+
break;
|
|
154
|
+
case "loading":
|
|
155
|
+
loading += 1;
|
|
156
|
+
break;
|
|
157
|
+
case "loaded":
|
|
158
|
+
loaded += 1;
|
|
159
|
+
bytes += record.bytes;
|
|
160
|
+
break;
|
|
161
|
+
case "error":
|
|
162
|
+
errored += 1;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
queued: queuedCount,
|
|
168
|
+
loading,
|
|
169
|
+
loaded,
|
|
170
|
+
unloaded: unloadedTotal,
|
|
171
|
+
errored,
|
|
172
|
+
inFlight: inFlight.size,
|
|
173
|
+
cancelled: cancelledTotal,
|
|
174
|
+
bytes,
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
applyTo(stats) {
|
|
178
|
+
const s = this.stats();
|
|
179
|
+
stats.assetsQueued = s.queued;
|
|
180
|
+
stats.assetsLoading = s.loading;
|
|
181
|
+
stats.assetsLoaded = s.loaded;
|
|
182
|
+
stats.assetsUnloaded = s.unloaded;
|
|
183
|
+
stats.streamedBytes = s.bytes;
|
|
184
|
+
},
|
|
185
|
+
async settle() {
|
|
186
|
+
while (inFlight.size > 0) {
|
|
187
|
+
await Promise.all(Array.from(inFlight));
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
clear() {
|
|
191
|
+
for (const signal of signals.values())
|
|
192
|
+
signal.cancelled = true;
|
|
193
|
+
records.clear();
|
|
194
|
+
queued.clear();
|
|
195
|
+
signals.clear();
|
|
196
|
+
inFlight.clear();
|
|
197
|
+
unloadedTotal = 0;
|
|
198
|
+
cancelledTotal = 0;
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { EntityPosition } from "../scene/entityStore.js";
|
|
2
|
+
export type Vec3 = EntityPosition;
|
|
3
|
+
/**
|
|
4
|
+
* World-space bounds a culler tests against. Kept as flat scalars (not tuples) so the
|
|
5
|
+
* hot culling path reads them without allocating. Carries both a bounding sphere (cheap
|
|
6
|
+
* broad reject) and a tight AABB (precise reject) — the standard "engine bounds" the
|
|
7
|
+
* frustum, distance, and spatial-index tests all consume.
|
|
8
|
+
*/
|
|
9
|
+
export interface RenderBounds {
|
|
10
|
+
centerX: number;
|
|
11
|
+
centerY: number;
|
|
12
|
+
centerZ: number;
|
|
13
|
+
radius: number;
|
|
14
|
+
minX: number;
|
|
15
|
+
minY: number;
|
|
16
|
+
minZ: number;
|
|
17
|
+
maxX: number;
|
|
18
|
+
maxY: number;
|
|
19
|
+
maxZ: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* How a renderable declares its extent. AABB, bounding sphere, and 2D rectangle cover the
|
|
23
|
+
* common cases; `point` is the degenerate zero-size default for objects that never override.
|
|
24
|
+
* `offset` shifts the volume from the object origin (e.g. a tall model whose pivot is at its feet).
|
|
25
|
+
*/
|
|
26
|
+
export type BoundsSpec = {
|
|
27
|
+
readonly kind: "sphere";
|
|
28
|
+
readonly radius: number;
|
|
29
|
+
readonly offset?: Vec3;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: "aabb";
|
|
32
|
+
readonly half: Vec3;
|
|
33
|
+
readonly offset?: Vec3;
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: "rect";
|
|
36
|
+
readonly halfWidth: number;
|
|
37
|
+
readonly halfDepth: number;
|
|
38
|
+
readonly halfHeight?: number;
|
|
39
|
+
readonly offset?: Vec3;
|
|
40
|
+
} | {
|
|
41
|
+
readonly kind: "point";
|
|
42
|
+
};
|
|
43
|
+
/** Conservative default: a unit sphere. Objects without explicit bounds cull as a small volume, never a point. */
|
|
44
|
+
export declare const DEFAULT_BOUNDS: BoundsSpec;
|
|
45
|
+
export declare function createRenderBounds(): RenderBounds;
|
|
46
|
+
/** Resolve a spec + world position into concrete bounds. Writes into `out` when supplied (no allocation). */
|
|
47
|
+
export declare function resolveBounds(spec: BoundsSpec, position: Vec3, out?: RenderBounds): RenderBounds;
|
|
48
|
+
export declare function expandRenderBounds(bounds: RenderBounds, margin: number, out?: RenderBounds): RenderBounds;
|
|
49
|
+
export declare function aabbIntersects(aMinX: number, aMinY: number, aMinZ: number, aMaxX: number, aMaxY: number, aMaxZ: number, bMinX: number, bMinY: number, bMinZ: number, bMaxX: number, bMaxY: number, bMaxZ: number): boolean;
|
|
50
|
+
export declare function boundsIntersect(a: RenderBounds, b: RenderBounds): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* BoundsSystem: caches resolved bounds per object and recomputes only when the caller's
|
|
53
|
+
* version advances — i.e. when a transform, geometry, or the bounds spec itself changed.
|
|
54
|
+
* The cached `RenderBounds` object is reused across recomputes, so a steady-state frame
|
|
55
|
+
* that touches thousands of objects performs zero bounds allocations.
|
|
56
|
+
*/
|
|
57
|
+
export interface BoundsCache {
|
|
58
|
+
get(id: string, version: number, spec: BoundsSpec, position: Vec3): RenderBounds;
|
|
59
|
+
peek(id: string): RenderBounds | undefined;
|
|
60
|
+
invalidate(id: string): void;
|
|
61
|
+
delete(id: string): void;
|
|
62
|
+
clear(): void;
|
|
63
|
+
size(): number;
|
|
64
|
+
recomputes(): number;
|
|
65
|
+
}
|
|
66
|
+
export declare function createBoundsCache(): BoundsCache;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** Conservative default: a unit sphere. Objects without explicit bounds cull as a small volume, never a point. */
|
|
2
|
+
export const DEFAULT_BOUNDS = { kind: "sphere", radius: 1 };
|
|
3
|
+
export function createRenderBounds() {
|
|
4
|
+
return {
|
|
5
|
+
centerX: 0,
|
|
6
|
+
centerY: 0,
|
|
7
|
+
centerZ: 0,
|
|
8
|
+
radius: 0,
|
|
9
|
+
minX: 0,
|
|
10
|
+
minY: 0,
|
|
11
|
+
minZ: 0,
|
|
12
|
+
maxX: 0,
|
|
13
|
+
maxY: 0,
|
|
14
|
+
maxZ: 0,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function fillBounds(out, cx, cy, cz, hx, hy, hz) {
|
|
18
|
+
out.centerX = cx;
|
|
19
|
+
out.centerY = cy;
|
|
20
|
+
out.centerZ = cz;
|
|
21
|
+
out.minX = cx - hx;
|
|
22
|
+
out.minY = cy - hy;
|
|
23
|
+
out.minZ = cz - hz;
|
|
24
|
+
out.maxX = cx + hx;
|
|
25
|
+
out.maxY = cy + hy;
|
|
26
|
+
out.maxZ = cz + hz;
|
|
27
|
+
out.radius = Math.sqrt(hx * hx + hy * hy + hz * hz);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** Resolve a spec + world position into concrete bounds. Writes into `out` when supplied (no allocation). */
|
|
31
|
+
export function resolveBounds(spec, position, out = createRenderBounds()) {
|
|
32
|
+
if (spec.kind === "point") {
|
|
33
|
+
return fillBounds(out, position[0], position[1], position[2], 0, 0, 0);
|
|
34
|
+
}
|
|
35
|
+
const offset = spec.offset;
|
|
36
|
+
const cx = position[0] + (offset?.[0] ?? 0);
|
|
37
|
+
const cy = position[1] + (offset?.[1] ?? 0);
|
|
38
|
+
const cz = position[2] + (offset?.[2] ?? 0);
|
|
39
|
+
switch (spec.kind) {
|
|
40
|
+
case "sphere": {
|
|
41
|
+
const r = spec.radius;
|
|
42
|
+
fillBounds(out, cx, cy, cz, r, r, r);
|
|
43
|
+
out.radius = r;
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
case "aabb":
|
|
47
|
+
return fillBounds(out, cx, cy, cz, spec.half[0], spec.half[1], spec.half[2]);
|
|
48
|
+
case "rect":
|
|
49
|
+
return fillBounds(out, cx, cy, cz, spec.halfWidth, spec.halfHeight ?? 0, spec.halfDepth);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function expandRenderBounds(bounds, margin, out = createRenderBounds()) {
|
|
53
|
+
out.centerX = bounds.centerX;
|
|
54
|
+
out.centerY = bounds.centerY;
|
|
55
|
+
out.centerZ = bounds.centerZ;
|
|
56
|
+
out.radius = bounds.radius + margin;
|
|
57
|
+
out.minX = bounds.minX - margin;
|
|
58
|
+
out.minY = bounds.minY - margin;
|
|
59
|
+
out.minZ = bounds.minZ - margin;
|
|
60
|
+
out.maxX = bounds.maxX + margin;
|
|
61
|
+
out.maxY = bounds.maxY + margin;
|
|
62
|
+
out.maxZ = bounds.maxZ + margin;
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
export function aabbIntersects(aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ, bMinX, bMinY, bMinZ, bMaxX, bMaxY, bMaxZ) {
|
|
66
|
+
return (aMinX <= bMaxX && aMaxX >= bMinX &&
|
|
67
|
+
aMinY <= bMaxY && aMaxY >= bMinY &&
|
|
68
|
+
aMinZ <= bMaxZ && aMaxZ >= bMinZ);
|
|
69
|
+
}
|
|
70
|
+
export function boundsIntersect(a, b) {
|
|
71
|
+
return aabbIntersects(a.minX, a.minY, a.minZ, a.maxX, a.maxY, a.maxZ, b.minX, b.minY, b.minZ, b.maxX, b.maxY, b.maxZ);
|
|
72
|
+
}
|
|
73
|
+
export function createBoundsCache() {
|
|
74
|
+
const entries = new Map();
|
|
75
|
+
let recomputes = 0;
|
|
76
|
+
return {
|
|
77
|
+
get(id, version, spec, position) {
|
|
78
|
+
const existing = entries.get(id);
|
|
79
|
+
if (existing !== undefined && existing.version === version)
|
|
80
|
+
return existing.bounds;
|
|
81
|
+
if (existing === undefined) {
|
|
82
|
+
const bounds = resolveBounds(spec, position);
|
|
83
|
+
entries.set(id, { version, bounds });
|
|
84
|
+
recomputes += 1;
|
|
85
|
+
return bounds;
|
|
86
|
+
}
|
|
87
|
+
resolveBounds(spec, position, existing.bounds);
|
|
88
|
+
existing.version = version;
|
|
89
|
+
recomputes += 1;
|
|
90
|
+
return existing.bounds;
|
|
91
|
+
},
|
|
92
|
+
peek(id) {
|
|
93
|
+
return entries.get(id)?.bounds;
|
|
94
|
+
},
|
|
95
|
+
invalidate(id) {
|
|
96
|
+
const existing = entries.get(id);
|
|
97
|
+
if (existing !== undefined)
|
|
98
|
+
existing.version = Number.NaN;
|
|
99
|
+
},
|
|
100
|
+
delete(id) {
|
|
101
|
+
entries.delete(id);
|
|
102
|
+
},
|
|
103
|
+
clear() {
|
|
104
|
+
entries.clear();
|
|
105
|
+
},
|
|
106
|
+
size() {
|
|
107
|
+
return entries.size;
|
|
108
|
+
},
|
|
109
|
+
recomputes() {
|
|
110
|
+
return recomputes;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CameraView } from "./frustum.js";
|
|
2
|
+
export type CameraKind = "main" | "splitScreen" | "renderToTexture" | "editor" | "minimap";
|
|
3
|
+
/**
|
|
4
|
+
* A camera's contribution to visibility. The VisibilitySystem unions results across every
|
|
5
|
+
* active context: an object stays renderable/loaded if *any* relevant camera needs it.
|
|
6
|
+
* A camera can opt out of driving asset streaming (e.g. a minimap that only needs positions,
|
|
7
|
+
* not loaded meshes) via `influencesStreaming: false`.
|
|
8
|
+
*/
|
|
9
|
+
export interface CameraVisibilityContext {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly view: CameraView;
|
|
12
|
+
/** Default "main". Editor/minimap cameras are still honored but tagged for diagnostics. */
|
|
13
|
+
readonly kind?: CameraKind;
|
|
14
|
+
/** Default true. When false this camera keeps objects renderable but does not preload their assets. */
|
|
15
|
+
readonly influencesStreaming?: boolean;
|
|
16
|
+
/** When true this camera renders everything (no frustum/distance reject). Default false. */
|
|
17
|
+
readonly cullingDisabled?: boolean;
|
|
18
|
+
/** Overrides the global render distance for this camera. */
|
|
19
|
+
readonly maxRenderDistance?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function cameraInfluencesStreaming(context: CameraVisibilityContext): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CullingSettings, StreamingSettings, ObjectVisibilityOverrides } from "./settings.js";
|
|
2
|
+
/**
|
|
3
|
+
* Per-game visibility configuration, surfaced on `PlayableGame.visibility`. Everything is
|
|
4
|
+
* optional: an existing game that sets nothing gets the conservative engine defaults
|
|
5
|
+
* automatically. This is the scene-level and per-kind override seam (requirement: per-object,
|
|
6
|
+
* per-layer, per-scene, and global controls).
|
|
7
|
+
*/
|
|
8
|
+
export interface VisibilityConfig {
|
|
9
|
+
/** Master switch for render culling in the shell. Default true. */
|
|
10
|
+
readonly enabled?: boolean;
|
|
11
|
+
/** Overrides for the global culling defaults (margins, distances, hysteresis, occlusion flag). */
|
|
12
|
+
readonly culling?: Partial<CullingSettings>;
|
|
13
|
+
/** Overrides for the asset-streaming defaults. */
|
|
14
|
+
readonly streaming?: Partial<StreamingSettings>;
|
|
15
|
+
/** Scene-wide overrides applied to every object. */
|
|
16
|
+
readonly scene?: ObjectVisibilityOverrides;
|
|
17
|
+
/** Overrides keyed by entity kind name. */
|
|
18
|
+
readonly entities?: Record<string, ObjectVisibilityOverrides>;
|
|
19
|
+
/** Overrides keyed by placed-object catalog id. */
|
|
20
|
+
readonly objects?: Record<string, ObjectVisibilityOverrides>;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-frame visibility + streaming counters. The VisibilitySystem fills the culling and
|
|
3
|
+
* timing fields; the AssetStreamingSystem fills the asset fields. Everything is a plain
|
|
4
|
+
* number so a HUD or the devtools hub can read it without adapters.
|
|
5
|
+
*/
|
|
6
|
+
export interface VisibilityStats {
|
|
7
|
+
totalObjects: number;
|
|
8
|
+
consideredForRender: number;
|
|
9
|
+
rejectedByFrustum: number;
|
|
10
|
+
rejectedByDistance: number;
|
|
11
|
+
rejectedByOcclusion: number;
|
|
12
|
+
drawCallsAvoided: number;
|
|
13
|
+
visible: number;
|
|
14
|
+
preloaded: number;
|
|
15
|
+
assetsQueued: number;
|
|
16
|
+
assetsLoading: number;
|
|
17
|
+
assetsLoaded: number;
|
|
18
|
+
assetsUnloaded: number;
|
|
19
|
+
streamedBytes: number;
|
|
20
|
+
spatialQueryMs: number;
|
|
21
|
+
cullingMs: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function createVisibilityStats(): VisibilityStats;
|
|
24
|
+
export declare function resetCullingStats(stats: VisibilityStats): void;
|
|
25
|
+
/** High-resolution clock that degrades to a monotonic-ish fallback when `performance` is absent. */
|
|
26
|
+
export declare function nowMs(): number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function createVisibilityStats() {
|
|
2
|
+
return {
|
|
3
|
+
totalObjects: 0,
|
|
4
|
+
consideredForRender: 0,
|
|
5
|
+
rejectedByFrustum: 0,
|
|
6
|
+
rejectedByDistance: 0,
|
|
7
|
+
rejectedByOcclusion: 0,
|
|
8
|
+
drawCallsAvoided: 0,
|
|
9
|
+
visible: 0,
|
|
10
|
+
preloaded: 0,
|
|
11
|
+
assetsQueued: 0,
|
|
12
|
+
assetsLoading: 0,
|
|
13
|
+
assetsLoaded: 0,
|
|
14
|
+
assetsUnloaded: 0,
|
|
15
|
+
streamedBytes: 0,
|
|
16
|
+
spatialQueryMs: 0,
|
|
17
|
+
cullingMs: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function resetCullingStats(stats) {
|
|
21
|
+
stats.totalObjects = 0;
|
|
22
|
+
stats.consideredForRender = 0;
|
|
23
|
+
stats.rejectedByFrustum = 0;
|
|
24
|
+
stats.rejectedByDistance = 0;
|
|
25
|
+
stats.rejectedByOcclusion = 0;
|
|
26
|
+
stats.drawCallsAvoided = 0;
|
|
27
|
+
stats.visible = 0;
|
|
28
|
+
stats.preloaded = 0;
|
|
29
|
+
stats.spatialQueryMs = 0;
|
|
30
|
+
stats.cullingMs = 0;
|
|
31
|
+
}
|
|
32
|
+
/** High-resolution clock that degrades to a monotonic-ish fallback when `performance` is absent. */
|
|
33
|
+
export function nowMs() {
|
|
34
|
+
const perf = globalThis.performance;
|
|
35
|
+
return perf !== undefined ? perf.now() : 0;
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Vec3 } from "./bounds.js";
|
|
2
|
+
export declare function distanceSquared(ax: number, ay: number, az: number, bx: number, by: number, bz: number): number;
|
|
3
|
+
/**
|
|
4
|
+
* Whether an object at `objectCenter` should be distance-culled from `cameraPos`.
|
|
5
|
+
* Compares squared distances against squared thresholds — no per-object square root.
|
|
6
|
+
* `nearestSurface` subtracts the object radius so a large object is not culled by the
|
|
7
|
+
* distance to its far side. `hysteresis` widens the max band by a fixed world-unit
|
|
8
|
+
* band for objects that were visible last check, so they don't flicker at the boundary.
|
|
9
|
+
*/
|
|
10
|
+
export declare function culledByDistance(cameraX: number, cameraY: number, cameraZ: number, objectX: number, objectY: number, objectZ: number, minDistance: number, maxDistance: number, radius?: number, hysteresis?: number): boolean;
|
|
11
|
+
export declare function distance(a: Vec3, b: Vec3): number;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function distanceSquared(ax, ay, az, bx, by, bz) {
|
|
2
|
+
const dx = bx - ax;
|
|
3
|
+
const dy = by - ay;
|
|
4
|
+
const dz = bz - az;
|
|
5
|
+
return dx * dx + dy * dy + dz * dz;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Whether an object at `objectCenter` should be distance-culled from `cameraPos`.
|
|
9
|
+
* Compares squared distances against squared thresholds — no per-object square root.
|
|
10
|
+
* `nearestSurface` subtracts the object radius so a large object is not culled by the
|
|
11
|
+
* distance to its far side. `hysteresis` widens the max band by a fixed world-unit
|
|
12
|
+
* band for objects that were visible last check, so they don't flicker at the boundary.
|
|
13
|
+
*/
|
|
14
|
+
export function culledByDistance(cameraX, cameraY, cameraZ, objectX, objectY, objectZ, minDistance, maxDistance, radius = 0, hysteresis = 0) {
|
|
15
|
+
const distSq = distanceSquared(cameraX, cameraY, cameraZ, objectX, objectY, objectZ);
|
|
16
|
+
if (minDistance > 0) {
|
|
17
|
+
const nearFar = minDistance + radius;
|
|
18
|
+
if (distSq < nearFar * nearFar)
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (maxDistance !== Infinity && maxDistance !== Number.POSITIVE_INFINITY) {
|
|
22
|
+
const farNear = maxDistance + radius + hysteresis;
|
|
23
|
+
if (distSq > farNear * farNear)
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
export function distance(a, b) {
|
|
29
|
+
return Math.sqrt(distanceSquared(a[0], a[1], a[2], b[0], b[1], b[2]));
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Vec3, RenderBounds } from "./bounds.js";
|
|
2
|
+
/** 3D perspective camera (the default game camera). */
|
|
3
|
+
export interface PerspectiveView {
|
|
4
|
+
readonly kind: "perspective";
|
|
5
|
+
readonly position: Vec3;
|
|
6
|
+
readonly target: Vec3;
|
|
7
|
+
readonly up?: Vec3;
|
|
8
|
+
/** Vertical field of view in degrees. Default 55. */
|
|
9
|
+
readonly fovDeg?: number;
|
|
10
|
+
/** Width / height. Default 16 / 9. */
|
|
11
|
+
readonly aspect?: number;
|
|
12
|
+
readonly near?: number;
|
|
13
|
+
readonly far?: number;
|
|
14
|
+
}
|
|
15
|
+
/** 2D / orthographic camera (top-down, side-scroller, minimap). */
|
|
16
|
+
export interface OrthographicView {
|
|
17
|
+
readonly kind: "orthographic";
|
|
18
|
+
readonly position: Vec3;
|
|
19
|
+
readonly target: Vec3;
|
|
20
|
+
readonly up?: Vec3;
|
|
21
|
+
readonly halfWidth: number;
|
|
22
|
+
readonly halfHeight: number;
|
|
23
|
+
readonly near?: number;
|
|
24
|
+
readonly far?: number;
|
|
25
|
+
}
|
|
26
|
+
export type CameraView = PerspectiveView | OrthographicView;
|
|
27
|
+
export interface Frustum {
|
|
28
|
+
/** 6 inward-facing planes, packed [nx,ny,nz,d] × 6. A point p is inside plane i when nx·px+ny·py+nz·pz+d ≥ 0. */
|
|
29
|
+
readonly planes: Float64Array;
|
|
30
|
+
/** 8 world corners, packed xyz × [ntl,ntr,nbl,nbr,ftl,ftr,fbl,fbr]. */
|
|
31
|
+
readonly corners: Float64Array;
|
|
32
|
+
minX: number;
|
|
33
|
+
minY: number;
|
|
34
|
+
minZ: number;
|
|
35
|
+
maxX: number;
|
|
36
|
+
maxY: number;
|
|
37
|
+
maxZ: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function createFrustum(): Frustum;
|
|
40
|
+
export declare function updateFrustum(out: Frustum, view: CameraView): Frustum;
|
|
41
|
+
/** Conservative AABB-vs-frustum: never a false negative (may keep a corner-case that is truly outside). */
|
|
42
|
+
export declare function aabbInFrustum(f: Frustum, minX: number, minY: number, minZ: number, maxX: number, maxY: number, maxZ: number): boolean;
|
|
43
|
+
export declare function sphereInFrustum(f: Frustum, cx: number, cy: number, cz: number, radius: number): boolean;
|
|
44
|
+
/** Sphere broad-phase reject, then the tight AABB test. Both conservative. */
|
|
45
|
+
export declare function boundsInFrustum(f: Frustum, b: RenderBounds): boolean;
|
|
46
|
+
/** Push every plane outward by `margin` (unit normals ⇒ d += margin) to form a preload/hysteresis region. */
|
|
47
|
+
export declare function dilateFrustum(src: Frustum, margin: number, out: Frustum): Frustum;
|