@aethervtt/shard-nav 0.0.1
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/agents.d.ts +48 -0
- package/dist/agents.js +703 -0
- package/dist/bake.d.ts +26 -0
- package/dist/bake.js +710 -0
- package/dist/cache.d.ts +31 -0
- package/dist/cache.js +90 -0
- package/dist/components.d.ts +88 -0
- package/dist/components.js +279 -0
- package/dist/geometry.d.ts +33 -0
- package/dist/geometry.js +176 -0
- package/dist/grid.d.ts +96 -0
- package/dist/grid.js +463 -0
- package/dist/hash.d.ts +20 -0
- package/dist/hash.js +61 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +105 -0
- package/dist/methods.d.ts +97 -0
- package/dist/methods.js +179 -0
- package/dist/navmesh.d.ts +101 -0
- package/dist/navmesh.js +194 -0
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.js +166 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.js +79 -0
- package/dist/query.d.ts +83 -0
- package/dist/query.js +391 -0
- package/dist/recast.d.ts +39 -0
- package/dist/recast.js +161 -0
- package/dist/state.d.ts +119 -0
- package/dist/state.js +49 -0
- package/dist/test-level.d.ts +19 -0
- package/dist/test-level.js +55 -0
- package/dist/test-maze.d.ts +8 -0
- package/dist/test-maze.js +56 -0
- package/package.json +42 -0
package/dist/geometry.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
class TriangleSoup {
|
|
2
|
+
positions = new Float32Array(9 * 256);
|
|
3
|
+
areas = new Uint8Array(256);
|
|
4
|
+
count = 0;
|
|
5
|
+
min = new Float64Array([Infinity, Infinity, Infinity]);
|
|
6
|
+
max = new Float64Array([-Infinity, -Infinity, -Infinity]);
|
|
7
|
+
clear() {
|
|
8
|
+
this.count = 0;
|
|
9
|
+
this.min.fill(Infinity);
|
|
10
|
+
this.max.fill(-Infinity);
|
|
11
|
+
}
|
|
12
|
+
grow() {
|
|
13
|
+
const p = new Float32Array(this.positions.length * 2);
|
|
14
|
+
p.set(this.positions);
|
|
15
|
+
this.positions = p;
|
|
16
|
+
const a = new Uint8Array(this.areas.length * 2);
|
|
17
|
+
a.set(this.areas);
|
|
18
|
+
this.areas = a;
|
|
19
|
+
}
|
|
20
|
+
/** Adds a triangle given in world space. */
|
|
21
|
+
push(ax, ay, az, bx, by, bz, cx, cy, cz, area) {
|
|
22
|
+
if (this.count === this.areas.length) this.grow();
|
|
23
|
+
const o = this.count * 9;
|
|
24
|
+
const p = this.positions;
|
|
25
|
+
p[o] = ax;
|
|
26
|
+
p[o + 1] = ay;
|
|
27
|
+
p[o + 2] = az;
|
|
28
|
+
p[o + 3] = bx;
|
|
29
|
+
p[o + 4] = by;
|
|
30
|
+
p[o + 5] = bz;
|
|
31
|
+
p[o + 6] = cx;
|
|
32
|
+
p[o + 7] = cy;
|
|
33
|
+
p[o + 8] = cz;
|
|
34
|
+
this.areas[this.count++] = area;
|
|
35
|
+
for (let k = 0; k < 9; k += 3) {
|
|
36
|
+
const x = p[o + k];
|
|
37
|
+
const y = p[o + k + 1];
|
|
38
|
+
const z = p[o + k + 2];
|
|
39
|
+
if (x < this.min[0]) this.min[0] = x;
|
|
40
|
+
if (y < this.min[1]) this.min[1] = y;
|
|
41
|
+
if (z < this.min[2]) this.min[2] = z;
|
|
42
|
+
if (x > this.max[0]) this.max[0] = x;
|
|
43
|
+
if (y > this.max[1]) this.max[1] = y;
|
|
44
|
+
if (z > this.max[2]) this.max[2] = z;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function pushTransformed(soup, local, indices, triangles, m, area) {
|
|
49
|
+
const det = m[0] * (m[5] * m[10] - m[6] * m[9]) - m[1] * (m[4] * m[10] - m[6] * m[8]) + m[2] * (m[4] * m[9] - m[5] * m[8]);
|
|
50
|
+
const flip = det < 0;
|
|
51
|
+
const tx = (i) => m[0] * local[i] + m[1] * local[i + 1] + m[2] * local[i + 2] + m[3];
|
|
52
|
+
const ty = (i) => m[4] * local[i] + m[5] * local[i + 1] + m[6] * local[i + 2] + m[7];
|
|
53
|
+
const tz = (i) => m[8] * local[i] + m[9] * local[i + 1] + m[10] * local[i + 2] + m[11];
|
|
54
|
+
for (let t = 0; t < triangles; t++) {
|
|
55
|
+
const a = 3 * (indices ? indices[3 * t] : 3 * t);
|
|
56
|
+
let b = 3 * (indices ? indices[3 * t + 1] : 3 * t + 1);
|
|
57
|
+
let c = 3 * (indices ? indices[3 * t + 2] : 3 * t + 2);
|
|
58
|
+
if (flip) {
|
|
59
|
+
const s = b;
|
|
60
|
+
b = c;
|
|
61
|
+
c = s;
|
|
62
|
+
}
|
|
63
|
+
soup.push(tx(a), ty(a), tz(a), tx(b), ty(b), tz(b), tx(c), ty(c), tz(c), area);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function outward(tris) {
|
|
67
|
+
for (let o = 0; o < tris.length; o += 9) {
|
|
68
|
+
const e0x = tris[o + 3] - tris[o];
|
|
69
|
+
const e0y = tris[o + 4] - tris[o + 1];
|
|
70
|
+
const e0z = tris[o + 5] - tris[o + 2];
|
|
71
|
+
const e1x = tris[o + 6] - tris[o];
|
|
72
|
+
const e1y = tris[o + 7] - tris[o + 1];
|
|
73
|
+
const e1z = tris[o + 8] - tris[o + 2];
|
|
74
|
+
const nx = e0y * e1z - e0z * e1y;
|
|
75
|
+
const ny = e0z * e1x - e0x * e1z;
|
|
76
|
+
const nz = e0x * e1y - e0y * e1x;
|
|
77
|
+
const cx = tris[o] + tris[o + 3] + tris[o + 6];
|
|
78
|
+
const cy = tris[o + 1] + tris[o + 4] + tris[o + 7];
|
|
79
|
+
const cz = tris[o + 2] + tris[o + 5] + tris[o + 8];
|
|
80
|
+
if (nx * cx + ny * cy + nz * cz < 0) {
|
|
81
|
+
for (let k = 0; k < 3; k++) {
|
|
82
|
+
const s = tris[o + 3 + k];
|
|
83
|
+
tris[o + 3 + k] = tris[o + 6 + k];
|
|
84
|
+
tris[o + 6 + k] = s;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return tris;
|
|
89
|
+
}
|
|
90
|
+
function boxTriangles(hx, hy, hz) {
|
|
91
|
+
const tris = [];
|
|
92
|
+
const quad = (a, b, c, d) => {
|
|
93
|
+
tris.push(...a, ...b, ...c, ...a, ...c, ...d);
|
|
94
|
+
};
|
|
95
|
+
const p = (x, y, z) => [x * hx, y * hy, z * hz];
|
|
96
|
+
quad(p(-1, 1, -1), p(-1, 1, 1), p(1, 1, 1), p(1, 1, -1));
|
|
97
|
+
quad(p(-1, -1, -1), p(1, -1, -1), p(1, -1, 1), p(-1, -1, 1));
|
|
98
|
+
quad(p(-1, -1, 1), p(1, -1, 1), p(1, 1, 1), p(-1, 1, 1));
|
|
99
|
+
quad(p(-1, -1, -1), p(-1, 1, -1), p(1, 1, -1), p(1, -1, -1));
|
|
100
|
+
quad(p(1, -1, -1), p(1, 1, -1), p(1, 1, 1), p(1, -1, 1));
|
|
101
|
+
quad(p(-1, -1, -1), p(-1, -1, 1), p(-1, 1, 1), p(-1, 1, -1));
|
|
102
|
+
return outward(tris);
|
|
103
|
+
}
|
|
104
|
+
function latheTriangles(profile, segments = 12) {
|
|
105
|
+
const tris = [];
|
|
106
|
+
const at = (ring, k) => {
|
|
107
|
+
const [r, y] = profile[ring];
|
|
108
|
+
const a = 2 * Math.PI * k / segments;
|
|
109
|
+
return [r * Math.cos(a), y, r * Math.sin(a)];
|
|
110
|
+
};
|
|
111
|
+
for (let i = 0; i + 1 < profile.length; i++) {
|
|
112
|
+
for (let k = 0; k < segments; k++) {
|
|
113
|
+
const a = at(i, k);
|
|
114
|
+
const b = at(i, k + 1);
|
|
115
|
+
const c = at(i + 1, k + 1);
|
|
116
|
+
const d = at(i + 1, k);
|
|
117
|
+
if (profile[i][0] > 0) tris.push(...a, ...b, ...c);
|
|
118
|
+
if (profile[i + 1][0] > 0) tris.push(...a, ...c, ...d);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const ring of [0, profile.length - 1]) {
|
|
122
|
+
const [r, y] = profile[ring];
|
|
123
|
+
if (r <= 0) continue;
|
|
124
|
+
for (let k = 0; k < segments; k++) tris.push(0, y, 0, ...at(ring, k), ...at(ring, k + 1));
|
|
125
|
+
}
|
|
126
|
+
const mid = (profile[0][1] + profile[profile.length - 1][1]) / 2;
|
|
127
|
+
for (let o = 1; o < tris.length; o += 3) tris[o] = tris[o] - mid;
|
|
128
|
+
outward(tris);
|
|
129
|
+
for (let o = 1; o < tris.length; o += 3) tris[o] = tris[o] + mid;
|
|
130
|
+
return tris;
|
|
131
|
+
}
|
|
132
|
+
function sphereProfile(radius, center = 0, from = -1, to = 1, rings = 6) {
|
|
133
|
+
const out = [];
|
|
134
|
+
for (let i = 0; i <= rings; i++) {
|
|
135
|
+
const a = Math.PI / 2 * (from + (to - from) * i / rings);
|
|
136
|
+
out.push([radius * Math.cos(a), center + radius * Math.sin(a)]);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
function capsuleProfile(radius, halfHeight) {
|
|
141
|
+
return [
|
|
142
|
+
...sphereProfile(radius, -halfHeight, -1, 0, 3),
|
|
143
|
+
...sphereProfile(radius, halfHeight, 0, 1, 3)
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
function heightfieldTriangles(rows, cols, heights, hx, hy, hz) {
|
|
147
|
+
const tris = [];
|
|
148
|
+
if (rows < 2 || cols < 2 || heights.length < rows * cols) return tris;
|
|
149
|
+
const p = (r, c) => [
|
|
150
|
+
-hx + 2 * hx * c / (cols - 1),
|
|
151
|
+
heights[r * cols + c] * hy,
|
|
152
|
+
-hz + 2 * hz * r / (rows - 1)
|
|
153
|
+
];
|
|
154
|
+
for (let r = 0; r + 1 < rows; r++) {
|
|
155
|
+
for (let c = 0; c + 1 < cols; c++) {
|
|
156
|
+
tris.push(...p(r, c), ...p(r + 1, c), ...p(r + 1, c + 1));
|
|
157
|
+
tris.push(...p(r, c), ...p(r + 1, c + 1), ...p(r, c + 1));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return tris;
|
|
161
|
+
}
|
|
162
|
+
function pushMesh(soup, mesh, m, area) {
|
|
163
|
+
const indices = mesh.indices;
|
|
164
|
+
const triangles = indices ? Math.floor(indices.length / 3) : Math.floor(mesh.vertexCount / 3);
|
|
165
|
+
pushTransformed(soup, mesh.positions, indices, triangles, m, area);
|
|
166
|
+
}
|
|
167
|
+
export {
|
|
168
|
+
TriangleSoup,
|
|
169
|
+
boxTriangles,
|
|
170
|
+
capsuleProfile,
|
|
171
|
+
heightfieldTriangles,
|
|
172
|
+
latheTriangles,
|
|
173
|
+
pushMesh,
|
|
174
|
+
pushTransformed,
|
|
175
|
+
sphereProfile
|
|
176
|
+
};
|
package/dist/grid.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export declare const DIAGONAL_MODES: readonly ['no-corners', 'never', 'always'];
|
|
2
|
+
export type DiagonalMode = (typeof DIAGONAL_MODES)[number];
|
|
3
|
+
/** Diagonal rule as a number, for hot code: 0 no-corners, 1 never, 2 always. */
|
|
4
|
+
export declare const DIAGONAL_NO_CORNERS = 0;
|
|
5
|
+
export declare const DIAGONAL_NEVER = 1;
|
|
6
|
+
export declare const DIAGONAL_ALWAYS = 2;
|
|
7
|
+
/**
|
|
8
|
+
* Walkable cells and their costs, row by row from y = 0 (the bottom row, lowest world y).
|
|
9
|
+
* 0 is blocked; 1 is normal ground; higher values cost that many times more to cross.
|
|
10
|
+
*/
|
|
11
|
+
export declare class NavGridData {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
costs: Uint8Array;
|
|
15
|
+
/**
|
|
16
|
+
* Bumps on every edit, so grids and agents built from it know to refresh. `set` bumps it; code
|
|
17
|
+
* that writes `costs` directly must bump it too.
|
|
18
|
+
*/
|
|
19
|
+
version: number;
|
|
20
|
+
private masks;
|
|
21
|
+
private maskVersions;
|
|
22
|
+
constructor(width: number, height: number, costs?: Uint8Array);
|
|
23
|
+
get(x: number, y: number): number;
|
|
24
|
+
/** Sets a cell's cost (0 blocks it). Out-of-range cells are `nav/out-of-bounds`. */
|
|
25
|
+
set(x: number, y: number, cost: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Per cell, a bit per direction (see `DX`, `DY`) that A* may step in under the diagonal rule:
|
|
28
|
+
* in bounds, walkable, and not cutting a blocked corner when that's not allowed. Cached until
|
|
29
|
+
* `version` changes, so searches skip walls without testing them.
|
|
30
|
+
*/
|
|
31
|
+
neighbors(diagonal: number): Uint8Array;
|
|
32
|
+
copyFrom(other: NavGridData): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reusable A* state for grids up to a size: a binary heap over TypedArrays and a generation
|
|
36
|
+
* counter per search, so a search neither allocates nor clears arrays once it has grown.
|
|
37
|
+
*/
|
|
38
|
+
export declare class GridSearch {
|
|
39
|
+
private capacity;
|
|
40
|
+
private g;
|
|
41
|
+
private parent;
|
|
42
|
+
/** `gen`: seen this search (open). `gen + 1`: closed. Anything else: untouched. */
|
|
43
|
+
private stamp;
|
|
44
|
+
private heap;
|
|
45
|
+
private heapF;
|
|
46
|
+
private heapG;
|
|
47
|
+
private heapPos;
|
|
48
|
+
private stack;
|
|
49
|
+
private gen;
|
|
50
|
+
/** The last path's cells (y · width + x), start first. */
|
|
51
|
+
cells: Int32Array<ArrayBuffer>;
|
|
52
|
+
/** How many cells `cells` holds. */
|
|
53
|
+
count: number;
|
|
54
|
+
/** Path cost of the last search (world distance × cell cost). */
|
|
55
|
+
cost: number;
|
|
56
|
+
/** Whether the last search reached the goal; false means `cells` ends at the closest cell. */
|
|
57
|
+
reached: boolean;
|
|
58
|
+
/** Cells expanded by the last search. */
|
|
59
|
+
expanded: number;
|
|
60
|
+
private ensure;
|
|
61
|
+
/**
|
|
62
|
+
* Finds the cheapest path from cell (sx, sy) to (gx, gy). Moving costs the world distance
|
|
63
|
+
* (cell size `csx` × `csy`) times the entered cell's cost. Returns true when the goal was
|
|
64
|
+
* reached; otherwise `cells` leads to the reachable cell closest to the goal. Both cells must
|
|
65
|
+
* be walkable (see `nearestWalkable`).
|
|
66
|
+
*/
|
|
67
|
+
search(grid: NavGridData, sx: number, sy: number, gx: number, gy: number, diagonal: number, csx?: number, csy?: number): boolean;
|
|
68
|
+
}
|
|
69
|
+
/** Scratch for `gridRaycast`: where the ray stopped, in cell units. */
|
|
70
|
+
export interface GridHit {
|
|
71
|
+
/** 0..1 along the segment; 1 means it reached the end. */
|
|
72
|
+
t: number;
|
|
73
|
+
x: number;
|
|
74
|
+
y: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Walks the segment (ax, ay) → (bx, by), in cell units, through the cells it touches
|
|
78
|
+
* (Amanatides–Woo). Stops at the first blocked cell, a cell costing more than `maxCost`, or a
|
|
79
|
+
* corner squeezed between two blocked cells (unless `always`). Returns true when the whole
|
|
80
|
+
* segment is clear; writes the stop point and fraction reached into `hit` when given.
|
|
81
|
+
*/
|
|
82
|
+
export declare function gridTrace(grid: NavGridData, ax: number, ay: number, bx: number, by: number, diagonal: number, maxCost: number, hit?: GridHit): boolean;
|
|
83
|
+
/** `gridTrace` with the segment in `seg` (ax, ay, bx, by), for per-frame callers. */
|
|
84
|
+
export declare function traceSegment(grid: NavGridData, seg: Float64Array, diagonal: number, maxCost: number, hit?: GridHit): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* String-pulls a cell path into corners: from the start point, the path runs straight to the
|
|
87
|
+
* farthest cell center it can see, and on from there. Points are in cell units (cell (x, y) spans
|
|
88
|
+
* [x, x + 1] × [y, y + 1]); `ends` holds the start and end points (sx, sy, ex, ey). Writes
|
|
89
|
+
* `stride`-spaced x, y pairs into `out` and returns the corner count (start and end included).
|
|
90
|
+
*/
|
|
91
|
+
export declare function smoothGridPath(grid: NavGridData, cells: Int32Array, count: number, ends: Float64Array, diagonal: number, out: Float32Array, stride: number, maxCorners: number): number;
|
|
92
|
+
/**
|
|
93
|
+
* The walkable cell nearest to (x, y) within `radius` cells, as y · width + x, or -1. Searches
|
|
94
|
+
* rings outward and picks the closest center in the first ring that has one.
|
|
95
|
+
*/
|
|
96
|
+
export declare function nearestWalkable(grid: NavGridData, x: number, y: number, radius: number): number;
|