@jgengine/core 0.8.0 → 0.9.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 +16 -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.js +1 -1
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -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/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +201 -129
- 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 +40 -0
- package/dist/combat/shotOrigin.js +57 -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/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/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +2 -0
- package/dist/game/events.d.ts +10 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +27 -7
- package/dist/game/playableGame.d.ts +50 -1
- 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 +29 -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 +1 -0
- package/dist/index.js +1 -0
- package/dist/input/axisInput.d.ts +26 -3
- package/dist/input/axisInput.js +44 -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/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +23 -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/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/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- 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 +21 -0
- package/dist/runtime/gameContext.js +184 -9
- 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/inputSnapshot.d.ts +5 -0
- package/dist/runtime/inputSnapshot.js +5 -0
- package/dist/runtime/motionIntents.d.ts +14 -6
- package/dist/runtime/motionIntents.js +20 -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/scene/colliders.d.ts +44 -0
- package/dist/scene/colliders.js +77 -0
- package/dist/scene/entityStore.d.ts +22 -5
- package/dist/scene/entityStore.js +59 -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.js +8 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- 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/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/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 +198 -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 +10 -2
- package/dist/world/environmentSummary.js +11 -2
- package/dist/world/features.d.ts +56 -8
- package/dist/world/features.js +24 -3
- 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/terrain.d.ts +42 -3
- package/dist/world/terrain.js +197 -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 +2585 -1704
- package/package.json +12 -2
|
@@ -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;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const WORLD_UP = [0, 1, 0];
|
|
2
|
+
const ALT_UP = [1, 0, 0];
|
|
3
|
+
export function createFrustum() {
|
|
4
|
+
return {
|
|
5
|
+
planes: new Float64Array(24),
|
|
6
|
+
corners: new Float64Array(24),
|
|
7
|
+
minX: 0,
|
|
8
|
+
minY: 0,
|
|
9
|
+
minZ: 0,
|
|
10
|
+
maxX: 0,
|
|
11
|
+
maxY: 0,
|
|
12
|
+
maxZ: 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function setCorner(corners, i, x, y, z) {
|
|
16
|
+
corners[i * 3] = x;
|
|
17
|
+
corners[i * 3 + 1] = y;
|
|
18
|
+
corners[i * 3 + 2] = z;
|
|
19
|
+
}
|
|
20
|
+
function planeFromCorners(planes, slot, corners, a, b, c, centroidX, centroidY, centroidZ) {
|
|
21
|
+
const ax = corners[a * 3], ay = corners[a * 3 + 1], az = corners[a * 3 + 2];
|
|
22
|
+
const bx = corners[b * 3], by = corners[b * 3 + 1], bz = corners[b * 3 + 2];
|
|
23
|
+
const cx = corners[c * 3], cy = corners[c * 3 + 1], cz = corners[c * 3 + 2];
|
|
24
|
+
const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
|
|
25
|
+
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
|
|
26
|
+
let nx = e1y * e2z - e1z * e2y;
|
|
27
|
+
let ny = e1z * e2x - e1x * e2z;
|
|
28
|
+
let nz = e1x * e2y - e1y * e2x;
|
|
29
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
30
|
+
nx /= len;
|
|
31
|
+
ny /= len;
|
|
32
|
+
nz /= len;
|
|
33
|
+
let d = -(nx * ax + ny * ay + nz * az);
|
|
34
|
+
// Orient inward: the centroid of all 8 corners is guaranteed interior.
|
|
35
|
+
if (nx * centroidX + ny * centroidY + nz * centroidZ + d < 0) {
|
|
36
|
+
nx = -nx;
|
|
37
|
+
ny = -ny;
|
|
38
|
+
nz = -nz;
|
|
39
|
+
d = -d;
|
|
40
|
+
}
|
|
41
|
+
planes[slot * 4] = nx;
|
|
42
|
+
planes[slot * 4 + 1] = ny;
|
|
43
|
+
planes[slot * 4 + 2] = nz;
|
|
44
|
+
planes[slot * 4 + 3] = d;
|
|
45
|
+
}
|
|
46
|
+
export function updateFrustum(out, view) {
|
|
47
|
+
const { position, target } = view;
|
|
48
|
+
let fx = target[0] - position[0];
|
|
49
|
+
let fy = target[1] - position[1];
|
|
50
|
+
let fz = target[2] - position[2];
|
|
51
|
+
const fLen = Math.sqrt(fx * fx + fy * fy + fz * fz) || 1;
|
|
52
|
+
fx /= fLen;
|
|
53
|
+
fy /= fLen;
|
|
54
|
+
fz /= fLen;
|
|
55
|
+
const up = view.up ?? WORLD_UP;
|
|
56
|
+
let rx = fy * up[2] - fz * up[1];
|
|
57
|
+
let ry = fz * up[0] - fx * up[2];
|
|
58
|
+
let rz = fx * up[1] - fy * up[0];
|
|
59
|
+
let rLen = Math.sqrt(rx * rx + ry * ry + rz * rz);
|
|
60
|
+
if (rLen < 1e-8) {
|
|
61
|
+
rx = fy * ALT_UP[2] - fz * ALT_UP[1];
|
|
62
|
+
ry = fz * ALT_UP[0] - fx * ALT_UP[2];
|
|
63
|
+
rz = fx * ALT_UP[1] - fy * ALT_UP[0];
|
|
64
|
+
rLen = Math.sqrt(rx * rx + ry * ry + rz * rz) || 1;
|
|
65
|
+
}
|
|
66
|
+
rx /= rLen;
|
|
67
|
+
ry /= rLen;
|
|
68
|
+
rz /= rLen;
|
|
69
|
+
const ux = ry * fz - rz * fy;
|
|
70
|
+
const uy = rz * fx - rx * fz;
|
|
71
|
+
const uz = rx * fy - ry * fx;
|
|
72
|
+
const near = view.near ?? 0.1;
|
|
73
|
+
const far = view.far ?? 1000;
|
|
74
|
+
let halfHNear, halfWNear, halfHFar, halfWFar;
|
|
75
|
+
if (view.kind === "perspective") {
|
|
76
|
+
const fov = ((view.fovDeg ?? 55) * Math.PI) / 180;
|
|
77
|
+
const aspect = view.aspect ?? 16 / 9;
|
|
78
|
+
halfHNear = Math.tan(fov / 2) * near;
|
|
79
|
+
halfWNear = halfHNear * aspect;
|
|
80
|
+
halfHFar = Math.tan(fov / 2) * far;
|
|
81
|
+
halfWFar = halfHFar * aspect;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
halfHNear = view.halfHeight;
|
|
85
|
+
halfWNear = view.halfWidth;
|
|
86
|
+
halfHFar = view.halfHeight;
|
|
87
|
+
halfWFar = view.halfWidth;
|
|
88
|
+
}
|
|
89
|
+
const corners = out.corners;
|
|
90
|
+
const ncx = position[0] + fx * near, ncy = position[1] + fy * near, ncz = position[2] + fz * near;
|
|
91
|
+
const fcx = position[0] + fx * far, fcy = position[1] + fy * far, fcz = position[2] + fz * far;
|
|
92
|
+
// [ntl,ntr,nbl,nbr,ftl,ftr,fbl,fbr]
|
|
93
|
+
setCorner(corners, 0, ncx + ux * halfHNear - rx * halfWNear, ncy + uy * halfHNear - ry * halfWNear, ncz + uz * halfHNear - rz * halfWNear);
|
|
94
|
+
setCorner(corners, 1, ncx + ux * halfHNear + rx * halfWNear, ncy + uy * halfHNear + ry * halfWNear, ncz + uz * halfHNear + rz * halfWNear);
|
|
95
|
+
setCorner(corners, 2, ncx - ux * halfHNear - rx * halfWNear, ncy - uy * halfHNear - ry * halfWNear, ncz - uz * halfHNear - rz * halfWNear);
|
|
96
|
+
setCorner(corners, 3, ncx - ux * halfHNear + rx * halfWNear, ncy - uy * halfHNear + ry * halfWNear, ncz - uz * halfHNear + rz * halfWNear);
|
|
97
|
+
setCorner(corners, 4, fcx + ux * halfHFar - rx * halfWFar, fcy + uy * halfHFar - ry * halfWFar, fcz + uz * halfHFar - rz * halfWFar);
|
|
98
|
+
setCorner(corners, 5, fcx + ux * halfHFar + rx * halfWFar, fcy + uy * halfHFar + ry * halfWFar, fcz + uz * halfHFar + rz * halfWFar);
|
|
99
|
+
setCorner(corners, 6, fcx - ux * halfHFar - rx * halfWFar, fcy - uy * halfHFar - ry * halfWFar, fcz - uz * halfHFar - rz * halfWFar);
|
|
100
|
+
setCorner(corners, 7, fcx - ux * halfHFar + rx * halfWFar, fcy - uy * halfHFar + ry * halfWFar, fcz - uz * halfHFar + rz * halfWFar);
|
|
101
|
+
let cX = 0, cY = 0, cZ = 0;
|
|
102
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
103
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
104
|
+
for (let i = 0; i < 8; i += 1) {
|
|
105
|
+
const x = corners[i * 3], y = corners[i * 3 + 1], z = corners[i * 3 + 2];
|
|
106
|
+
cX += x;
|
|
107
|
+
cY += y;
|
|
108
|
+
cZ += z;
|
|
109
|
+
if (x < minX)
|
|
110
|
+
minX = x;
|
|
111
|
+
if (y < minY)
|
|
112
|
+
minY = y;
|
|
113
|
+
if (z < minZ)
|
|
114
|
+
minZ = z;
|
|
115
|
+
if (x > maxX)
|
|
116
|
+
maxX = x;
|
|
117
|
+
if (y > maxY)
|
|
118
|
+
maxY = y;
|
|
119
|
+
if (z > maxZ)
|
|
120
|
+
maxZ = z;
|
|
121
|
+
}
|
|
122
|
+
cX /= 8;
|
|
123
|
+
cY /= 8;
|
|
124
|
+
cZ /= 8;
|
|
125
|
+
out.minX = minX;
|
|
126
|
+
out.minY = minY;
|
|
127
|
+
out.minZ = minZ;
|
|
128
|
+
out.maxX = maxX;
|
|
129
|
+
out.maxY = maxY;
|
|
130
|
+
out.maxZ = maxZ;
|
|
131
|
+
const planes = out.planes;
|
|
132
|
+
planeFromCorners(planes, 0, corners, 0, 1, 2, cX, cY, cZ); // near
|
|
133
|
+
planeFromCorners(planes, 1, corners, 4, 6, 5, cX, cY, cZ); // far
|
|
134
|
+
planeFromCorners(planes, 2, corners, 0, 2, 4, cX, cY, cZ); // left
|
|
135
|
+
planeFromCorners(planes, 3, corners, 1, 5, 3, cX, cY, cZ); // right
|
|
136
|
+
planeFromCorners(planes, 4, corners, 0, 4, 1, cX, cY, cZ); // top
|
|
137
|
+
planeFromCorners(planes, 5, corners, 2, 3, 6, cX, cY, cZ); // bottom
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/** Conservative AABB-vs-frustum: never a false negative (may keep a corner-case that is truly outside). */
|
|
141
|
+
export function aabbInFrustum(f, minX, minY, minZ, maxX, maxY, maxZ) {
|
|
142
|
+
const planes = f.planes;
|
|
143
|
+
for (let i = 0; i < 6; i += 1) {
|
|
144
|
+
const nx = planes[i * 4], ny = planes[i * 4 + 1], nz = planes[i * 4 + 2], d = planes[i * 4 + 3];
|
|
145
|
+
const px = nx >= 0 ? maxX : minX;
|
|
146
|
+
const py = ny >= 0 ? maxY : minY;
|
|
147
|
+
const pz = nz >= 0 ? maxZ : minZ;
|
|
148
|
+
if (nx * px + ny * py + nz * pz + d < 0)
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
export function sphereInFrustum(f, cx, cy, cz, radius) {
|
|
154
|
+
const planes = f.planes;
|
|
155
|
+
for (let i = 0; i < 6; i += 1) {
|
|
156
|
+
const nx = planes[i * 4], ny = planes[i * 4 + 1], nz = planes[i * 4 + 2], d = planes[i * 4 + 3];
|
|
157
|
+
if (nx * cx + ny * cy + nz * cz + d < -radius)
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/** Sphere broad-phase reject, then the tight AABB test. Both conservative. */
|
|
163
|
+
export function boundsInFrustum(f, b) {
|
|
164
|
+
if (!sphereInFrustum(f, b.centerX, b.centerY, b.centerZ, b.radius))
|
|
165
|
+
return false;
|
|
166
|
+
return aabbInFrustum(f, b.minX, b.minY, b.minZ, b.maxX, b.maxY, b.maxZ);
|
|
167
|
+
}
|
|
168
|
+
/** Push every plane outward by `margin` (unit normals ⇒ d += margin) to form a preload/hysteresis region. */
|
|
169
|
+
export function dilateFrustum(src, margin, out) {
|
|
170
|
+
const sp = src.planes;
|
|
171
|
+
const op = out.planes;
|
|
172
|
+
for (let i = 0; i < 6; i += 1) {
|
|
173
|
+
op[i * 4] = sp[i * 4];
|
|
174
|
+
op[i * 4 + 1] = sp[i * 4 + 1];
|
|
175
|
+
op[i * 4 + 2] = sp[i * 4 + 2];
|
|
176
|
+
op[i * 4 + 3] = sp[i * 4 + 3] + margin;
|
|
177
|
+
}
|
|
178
|
+
out.corners.set(src.corners);
|
|
179
|
+
out.minX = src.minX - margin;
|
|
180
|
+
out.minY = src.minY - margin;
|
|
181
|
+
out.minZ = src.minZ - margin;
|
|
182
|
+
out.maxX = src.maxX + margin;
|
|
183
|
+
out.maxY = src.maxY + margin;
|
|
184
|
+
out.maxZ = src.maxZ + margin;
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RenderBounds } from "./bounds.js";
|
|
2
|
+
import type { CameraView } from "./frustum.js";
|
|
3
|
+
export interface OcclusionInput {
|
|
4
|
+
readonly camera: CameraView;
|
|
5
|
+
readonly bounds: RenderBounds;
|
|
6
|
+
/** Candidate blockers already known to be in front of the object (nearer to the camera). */
|
|
7
|
+
readonly occluders: Iterable<RenderBounds>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Optional occlusion stage. Kept behind a feature flag and disabled by default: a fragile
|
|
11
|
+
* occlusion test that wrongly hides a visible object is worse than no occlusion at all.
|
|
12
|
+
* Any tester here MUST be conservative — it may return false (visible) for a truly hidden
|
|
13
|
+
* object, but must never return true (hidden) for an object the player can actually see.
|
|
14
|
+
*/
|
|
15
|
+
export interface OcclusionTester {
|
|
16
|
+
readonly enabled: boolean;
|
|
17
|
+
isOccluded(input: OcclusionInput): boolean;
|
|
18
|
+
}
|
|
19
|
+
/** The safe default: never occludes anything. */
|
|
20
|
+
export declare const noOcclusion: OcclusionTester;
|
|
21
|
+
export interface AxisOccluderOptions {
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
/** An occluder is only trusted when its bounding sphere is at least this large (world units). Default 8. */
|
|
24
|
+
minOccluderRadius?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A deliberately conservative bounding-volume occlusion tester. It hides an object only when a
|
|
28
|
+
* single large occluder's AABB fully contains the object's AABB on every axis AND sits strictly
|
|
29
|
+
* nearer the camera on the dominant view axis — the object is then provably in the occluder's
|
|
30
|
+
* shadow. Full containment (not just the two perpendicular axes) makes it stricter than it needs
|
|
31
|
+
* to be, which is the safe direction: it never hides a partially-visible object.
|
|
32
|
+
*
|
|
33
|
+
* This is the architecture/interface for occlusion, not a production hierarchical-Z implementation.
|
|
34
|
+
* It stays disabled by default; enable only where the scene has genuine large blockers.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createAxisAlignedOcclusionTester(options?: AxisOccluderOptions): OcclusionTester;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The safe default: never occludes anything. */
|
|
2
|
+
export const noOcclusion = {
|
|
3
|
+
enabled: false,
|
|
4
|
+
isOccluded: () => false,
|
|
5
|
+
};
|
|
6
|
+
function screenlessContains(outer, inner) {
|
|
7
|
+
return (outer.minX <= inner.minX && outer.maxX >= inner.maxX &&
|
|
8
|
+
outer.minZ <= inner.minZ && outer.maxZ >= inner.maxZ &&
|
|
9
|
+
outer.minY <= inner.minY && outer.maxY >= inner.maxY);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A deliberately conservative bounding-volume occlusion tester. It hides an object only when a
|
|
13
|
+
* single large occluder's AABB fully contains the object's AABB on every axis AND sits strictly
|
|
14
|
+
* nearer the camera on the dominant view axis — the object is then provably in the occluder's
|
|
15
|
+
* shadow. Full containment (not just the two perpendicular axes) makes it stricter than it needs
|
|
16
|
+
* to be, which is the safe direction: it never hides a partially-visible object.
|
|
17
|
+
*
|
|
18
|
+
* This is the architecture/interface for occlusion, not a production hierarchical-Z implementation.
|
|
19
|
+
* It stays disabled by default; enable only where the scene has genuine large blockers.
|
|
20
|
+
*/
|
|
21
|
+
export function createAxisAlignedOcclusionTester(options = {}) {
|
|
22
|
+
const minRadius = options.minOccluderRadius ?? 8;
|
|
23
|
+
return {
|
|
24
|
+
enabled: options.enabled ?? false,
|
|
25
|
+
isOccluded({ camera, bounds, occluders }) {
|
|
26
|
+
const dirX = camera.target[0] - camera.position[0];
|
|
27
|
+
const dirZ = camera.target[2] - camera.position[2];
|
|
28
|
+
const horizontal = Math.abs(dirX) >= Math.abs(dirZ);
|
|
29
|
+
const objAxis = horizontal ? camera.position[0] : camera.position[2];
|
|
30
|
+
const objDepth = horizontal ? bounds.minX : bounds.minZ;
|
|
31
|
+
for (const occ of occluders) {
|
|
32
|
+
if (occ.radius < minRadius)
|
|
33
|
+
continue;
|
|
34
|
+
if (!screenlessContains(occ, bounds))
|
|
35
|
+
continue;
|
|
36
|
+
const occNear = horizontal
|
|
37
|
+
? Math.abs(occ.maxX - objAxis) < Math.abs(objDepth - objAxis)
|
|
38
|
+
: Math.abs(occ.maxZ - objAxis) < Math.abs(objDepth - objAxis);
|
|
39
|
+
if (occNear)
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { BoundsSpec } from "./bounds.js";
|
|
2
|
+
import type { CameraVisibilityContext } from "./camera.js";
|
|
3
|
+
/**
|
|
4
|
+
* Global render-culling behavior. Defaults favor correctness and visual stability over
|
|
5
|
+
* aggressive rejection: a generous preload margin, hysteresis, and a large default render
|
|
6
|
+
* distance so upgrading the engine never makes an existing game's objects vanish.
|
|
7
|
+
*/
|
|
8
|
+
export interface CullingSettings {
|
|
9
|
+
readonly enabled: boolean;
|
|
10
|
+
readonly frustumCulling: boolean;
|
|
11
|
+
readonly distanceCulling: boolean;
|
|
12
|
+
/** Occlusion culling is opt-in and off until proven reliable for the renderer. */
|
|
13
|
+
readonly occlusionCulling: boolean;
|
|
14
|
+
readonly defaultMinRenderDistance: number;
|
|
15
|
+
readonly defaultMaxRenderDistance: number;
|
|
16
|
+
/** World units the preload region extends beyond the visible frustum, hiding pop-in. */
|
|
17
|
+
readonly preloadMargin: number;
|
|
18
|
+
/** World-unit band added when re-testing a previously visible object, preventing boundary flicker. */
|
|
19
|
+
readonly hysteresis: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_CULLING_SETTINGS: CullingSettings;
|
|
22
|
+
export interface StreamingSettings {
|
|
23
|
+
readonly enabled: boolean;
|
|
24
|
+
/** Extra world units around the camera whose assets are preloaded ahead of visibility. */
|
|
25
|
+
readonly preloadMargin: number;
|
|
26
|
+
/** Seconds an asset must stay outside the active area before it is eligible for unload. */
|
|
27
|
+
readonly unloadGraceSeconds: number;
|
|
28
|
+
readonly maxLoadsPerFrame: number;
|
|
29
|
+
readonly maxUnloadsPerFrame: number;
|
|
30
|
+
/** Assets at or below this byte size are kept resident once loaded (small shared assets). */
|
|
31
|
+
readonly keepResidentBytes: number;
|
|
32
|
+
}
|
|
33
|
+
export declare const DEFAULT_STREAMING_SETTINGS: StreamingSettings;
|
|
34
|
+
/**
|
|
35
|
+
* Per-object visibility/streaming overrides. Also the shape used at the layer, scene, and
|
|
36
|
+
* global-override levels — resolution merges object → layer → scene → global-override, so a
|
|
37
|
+
* game can, say, mark a whole "hud" layer alwaysVisible without touching each object.
|
|
38
|
+
*/
|
|
39
|
+
export interface ObjectVisibilityOverrides {
|
|
40
|
+
/** Bypass all render culling — always submitted (UI, skybox, camera-attached, marked always-visible). */
|
|
41
|
+
readonly alwaysVisible?: boolean;
|
|
42
|
+
/** Never auto-unload this object's assets. */
|
|
43
|
+
readonly neverUnload?: boolean;
|
|
44
|
+
readonly minRenderDistance?: number;
|
|
45
|
+
readonly maxRenderDistance?: number;
|
|
46
|
+
readonly preloadMargin?: number;
|
|
47
|
+
readonly cullingDisabled?: boolean;
|
|
48
|
+
readonly streamingDisabled?: boolean;
|
|
49
|
+
readonly classification?: "static" | "dynamic";
|
|
50
|
+
readonly bounds?: BoundsSpec;
|
|
51
|
+
/**
|
|
52
|
+
* Final say on render visibility. Return true/false to force, or undefined to defer to the
|
|
53
|
+
* standard frustum+distance+occlusion pipeline. Evaluated per relevant camera.
|
|
54
|
+
*/
|
|
55
|
+
readonly customVisibility?: (camera: CameraVisibilityContext) => boolean | undefined;
|
|
56
|
+
/** Pin the object's assets resident regardless of distance. */
|
|
57
|
+
readonly pinned?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface ResolvedOverrides {
|
|
60
|
+
alwaysVisible: boolean;
|
|
61
|
+
neverUnload: boolean;
|
|
62
|
+
minRenderDistance: number | undefined;
|
|
63
|
+
maxRenderDistance: number | undefined;
|
|
64
|
+
preloadMargin: number | undefined;
|
|
65
|
+
cullingDisabled: boolean;
|
|
66
|
+
streamingDisabled: boolean;
|
|
67
|
+
classification: "static" | "dynamic" | undefined;
|
|
68
|
+
bounds: BoundsSpec | undefined;
|
|
69
|
+
customVisibility: ((camera: CameraVisibilityContext) => boolean | undefined) | undefined;
|
|
70
|
+
pinned: boolean;
|
|
71
|
+
}
|
|
72
|
+
/** Merge override layers by precedence: earlier arguments win. Pass object, then layer, then scene, then global. */
|
|
73
|
+
export declare function resolveOverrides(...layers: readonly (ObjectVisibilityOverrides | undefined)[]): ResolvedOverrides;
|
|
74
|
+
export declare function mergeCullingSettings(base: CullingSettings, patch: Partial<CullingSettings>): CullingSettings;
|
|
75
|
+
export declare function mergeStreamingSettings(base: StreamingSettings, patch: Partial<StreamingSettings>): StreamingSettings;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const DEFAULT_CULLING_SETTINGS = {
|
|
2
|
+
enabled: true,
|
|
3
|
+
frustumCulling: true,
|
|
4
|
+
distanceCulling: true,
|
|
5
|
+
occlusionCulling: false,
|
|
6
|
+
defaultMinRenderDistance: 0,
|
|
7
|
+
defaultMaxRenderDistance: Infinity,
|
|
8
|
+
preloadMargin: 24,
|
|
9
|
+
hysteresis: 2,
|
|
10
|
+
};
|
|
11
|
+
export const DEFAULT_STREAMING_SETTINGS = {
|
|
12
|
+
enabled: true,
|
|
13
|
+
preloadMargin: 32,
|
|
14
|
+
unloadGraceSeconds: 10,
|
|
15
|
+
maxLoadsPerFrame: 4,
|
|
16
|
+
maxUnloadsPerFrame: 2,
|
|
17
|
+
keepResidentBytes: 64 * 1024,
|
|
18
|
+
};
|
|
19
|
+
const EMPTY = {};
|
|
20
|
+
/** Merge override layers by precedence: earlier arguments win. Pass object, then layer, then scene, then global. */
|
|
21
|
+
export function resolveOverrides(...layers) {
|
|
22
|
+
const pick = (key) => {
|
|
23
|
+
for (const layer of layers) {
|
|
24
|
+
const value = (layer ?? EMPTY)[key];
|
|
25
|
+
if (value !== undefined)
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
alwaysVisible: pick("alwaysVisible") ?? false,
|
|
32
|
+
neverUnload: pick("neverUnload") ?? false,
|
|
33
|
+
minRenderDistance: pick("minRenderDistance"),
|
|
34
|
+
maxRenderDistance: pick("maxRenderDistance"),
|
|
35
|
+
preloadMargin: pick("preloadMargin"),
|
|
36
|
+
cullingDisabled: pick("cullingDisabled") ?? false,
|
|
37
|
+
streamingDisabled: pick("streamingDisabled") ?? false,
|
|
38
|
+
classification: pick("classification"),
|
|
39
|
+
bounds: pick("bounds"),
|
|
40
|
+
customVisibility: pick("customVisibility"),
|
|
41
|
+
pinned: pick("pinned") ?? false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function mergeCullingSettings(base, patch) {
|
|
45
|
+
return { ...base, ...patch };
|
|
46
|
+
}
|
|
47
|
+
export function mergeStreamingSettings(base, patch) {
|
|
48
|
+
return { ...base, ...patch };
|
|
49
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LodBand } from "../world/lod.js";
|
|
2
|
+
/**
|
|
3
|
+
* Simulation culling is a SEPARATE, opt-in system from render culling. Render culling only
|
|
4
|
+
* decides what is drawn; this decides whether a low-priority off-screen entity updates this
|
|
5
|
+
* tick. It is disabled by default and never throttles a protected entity — physics-critical,
|
|
6
|
+
* networking-critical, audio-critical, scripted, or explicitly-active entities always update.
|
|
7
|
+
* Gameplay correctness must never depend on an entity being on-screen, so opt in only where
|
|
8
|
+
* skipping updates is provably safe.
|
|
9
|
+
*/
|
|
10
|
+
export interface SimulationCullingOptions {
|
|
11
|
+
/** Off by default. */
|
|
12
|
+
readonly enabled?: boolean;
|
|
13
|
+
/** Distance bands → update interval (seconds). Nearer bands update every tick; far bands throttle. */
|
|
14
|
+
readonly bands?: readonly LodBand[];
|
|
15
|
+
/** Interval for entities beyond the last band. null = never throttle (still update). Default null. */
|
|
16
|
+
readonly beyondInterval?: number | null;
|
|
17
|
+
/** Return true for entities that must never be throttled (physics/net/audio/scripted/active). */
|
|
18
|
+
readonly isProtected?: (id: string) => boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface SimulationDecision {
|
|
21
|
+
/** Whether the entity should run its update this tick. */
|
|
22
|
+
update: boolean;
|
|
23
|
+
/** Seconds of accumulated time delivered when `update` is true (for catch-up integration). */
|
|
24
|
+
elapsed: number;
|
|
25
|
+
}
|
|
26
|
+
export interface SimulationCuller {
|
|
27
|
+
enabled(): boolean;
|
|
28
|
+
setEnabled(value: boolean): void;
|
|
29
|
+
/** Decide whether `id` at `distance` from the nearest camera updates this tick. */
|
|
30
|
+
step(id: string, distance: number, dt: number): SimulationDecision;
|
|
31
|
+
forget(id: string): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
|
34
|
+
export declare function createSimulationCuller(options?: SimulationCullingOptions): SimulationCuller;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createLodScheduler } from "../world/lod.js";
|
|
2
|
+
const DEFAULT_BANDS = [
|
|
3
|
+
{ maxDistance: 40, interval: 0 },
|
|
4
|
+
{ maxDistance: 90, interval: 0.1 },
|
|
5
|
+
{ maxDistance: 160, interval: 0.25 },
|
|
6
|
+
];
|
|
7
|
+
export function createSimulationCuller(options = {}) {
|
|
8
|
+
let enabled = options.enabled ?? false;
|
|
9
|
+
const isProtected = options.isProtected;
|
|
10
|
+
const scheduler = createLodScheduler({
|
|
11
|
+
bands: options.bands ?? DEFAULT_BANDS,
|
|
12
|
+
beyondInterval: options.beyondInterval ?? null,
|
|
13
|
+
stagger: true,
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
enabled() {
|
|
17
|
+
return enabled;
|
|
18
|
+
},
|
|
19
|
+
setEnabled(value) {
|
|
20
|
+
enabled = value;
|
|
21
|
+
},
|
|
22
|
+
step(id, distance, dt) {
|
|
23
|
+
if (!enabled || (isProtected !== undefined && isProtected(id))) {
|
|
24
|
+
return { update: true, elapsed: dt };
|
|
25
|
+
}
|
|
26
|
+
const elapsed = scheduler.step(id, distance, dt);
|
|
27
|
+
return elapsed > 0 ? { update: true, elapsed } : { update: false, elapsed: 0 };
|
|
28
|
+
},
|
|
29
|
+
forget(id) {
|
|
30
|
+
scheduler.remove(id);
|
|
31
|
+
},
|
|
32
|
+
clear() {
|
|
33
|
+
scheduler.clear();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { RenderBounds } from "./bounds.js";
|
|
2
|
+
import { aabbIntersects } from "./bounds.js";
|
|
3
|
+
import type { Frustum } from "./frustum.js";
|
|
4
|
+
export interface SpatialIndexOptions {
|
|
5
|
+
/** Grid cell size in world units. Default 16. Objects are hashed into every cell their AABB overlaps. */
|
|
6
|
+
readonly cellSize?: number;
|
|
7
|
+
/**
|
|
8
|
+
* An object whose AABB spans more cells than this on any axis is parked in an "oversized"
|
|
9
|
+
* bucket that every query visits, rather than smearing across the grid. Default 6.
|
|
10
|
+
*/
|
|
11
|
+
readonly maxCellSpan?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A uniform 3D spatial hash the renderer and streaming system query for potentially-visible
|
|
15
|
+
* objects instead of scanning the whole scene. Objects are keyed by their world AABB into
|
|
16
|
+
* every overlapping cell; a moving object only rewrites the cells that actually changed.
|
|
17
|
+
* Static objects are inserted once and never touched again. Oversized objects (huge terrain
|
|
18
|
+
* chunks, world bounds) are held separately so they are always considered.
|
|
19
|
+
*/
|
|
20
|
+
export interface SpatialIndex {
|
|
21
|
+
insert(id: string, bounds: RenderBounds, dynamic?: boolean): void;
|
|
22
|
+
update(id: string, bounds: RenderBounds): void;
|
|
23
|
+
remove(id: string): void;
|
|
24
|
+
has(id: string): boolean;
|
|
25
|
+
size(): number;
|
|
26
|
+
/** Collect ids whose cells the frustum's world-AABB touches (broad phase). Reuses `out`. */
|
|
27
|
+
queryFrustum(frustum: Frustum, out: string[]): string[];
|
|
28
|
+
queryBox(minX: number, minY: number, minZ: number, maxX: number, maxY: number, maxZ: number, out: string[]): string[];
|
|
29
|
+
querySphere(cx: number, cy: number, cz: number, radius: number, out: string[]): string[];
|
|
30
|
+
cells(): {
|
|
31
|
+
key: string;
|
|
32
|
+
count: number;
|
|
33
|
+
minX: number;
|
|
34
|
+
minY: number;
|
|
35
|
+
minZ: number;
|
|
36
|
+
maxX: number;
|
|
37
|
+
maxY: number;
|
|
38
|
+
maxZ: number;
|
|
39
|
+
}[];
|
|
40
|
+
cellSize(): number;
|
|
41
|
+
clear(): void;
|
|
42
|
+
}
|
|
43
|
+
export declare function createSpatialIndex(options?: SpatialIndexOptions): SpatialIndex;
|
|
44
|
+
/** Precise AABB-vs-AABB test re-exported for callers refining a broad-phase result. */
|
|
45
|
+
export { aabbIntersects };
|