@woosh/meep-engine 2.148.0 → 2.150.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/package.json +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.d.ts +23 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.js +295 -0
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +18 -8
- package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
- package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +8 -5
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +18 -10
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js +1 -1
- package/src/engine/navigation/mesh/NavigationMesh.d.ts +6 -2
- package/src/engine/navigation/mesh/NavigationMesh.d.ts.map +1 -1
- package/src/engine/navigation/mesh/NavigationMesh.js +234 -212
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts +7 -3
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts.map +1 -1
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.js +67 -73
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts +16 -5
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.js +262 -147
- package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/navmesh_build_topology.js +33 -3
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts +4 -1
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts.map +1 -1
- package/src/engine/navigation/mesh/bvh_query_nearest_face.js +164 -131
- package/src/engine/physics/broadphase/generate_pairs.js +110 -110
- package/src/engine/physics/queries/raycast.js +201 -201
|
@@ -8,19 +8,24 @@ import {
|
|
|
8
8
|
const open = new Uint32Heap();
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
11
|
* @type {Set<number>}
|
|
13
12
|
*/
|
|
14
13
|
const closed = new Set();
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
|
-
*
|
|
16
|
+
* Least known traversal cost (sum of centroid-to-centroid distances) from start to each face.
|
|
18
17
|
* @type {Map<number, number>}
|
|
19
18
|
*/
|
|
20
19
|
const g_score = new Map();
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
|
-
*
|
|
22
|
+
* Reconstruction parent pointers: face -> the face we reached it from on the best path.
|
|
23
|
+
* @type {Map<number, number>}
|
|
24
|
+
*/
|
|
25
|
+
const came_from = new Map();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Note that we limit the supported number of neighbors, a reasonable mesh will fit this criteria
|
|
24
29
|
* @type {Uint32Array}
|
|
25
30
|
*/
|
|
26
31
|
const neighbors = new Uint32Array(256);
|
|
@@ -28,96 +33,80 @@ const neighbors = new Uint32Array(256);
|
|
|
28
33
|
const scratch_array_f32 = new Float32Array(6);
|
|
29
34
|
|
|
30
35
|
/**
|
|
36
|
+
* Straight-line distance between the centroids of two faces.
|
|
37
|
+
*
|
|
38
|
+
* This is used both as the step cost (between adjacent faces) and as the heuristic (from a face to
|
|
39
|
+
* the goal). Because, by the triangle inequality, the straight-line distance from any face to the
|
|
40
|
+
* goal is never greater than the summed centroid-to-centroid distance of an actual face path between
|
|
41
|
+
* them, the heuristic is admissible AND consistent. A* therefore returns a shortest-distance corridor
|
|
42
|
+
* (under the centroid metric) rather than a fewest-faces one.
|
|
31
43
|
*
|
|
32
44
|
* @param {number} a
|
|
33
45
|
* @param {number} b
|
|
34
46
|
* @param {BinaryTopology} topology
|
|
35
47
|
* @returns {number}
|
|
36
48
|
*/
|
|
37
|
-
function
|
|
38
|
-
// compare centroid distances
|
|
49
|
+
function face_centroid_distance(a, b, topology) {
|
|
39
50
|
bt_face_get_centroid(scratch_array_f32, 0, topology, a);
|
|
40
51
|
bt_face_get_centroid(scratch_array_f32, 3, topology, b);
|
|
41
52
|
|
|
42
|
-
// delta
|
|
43
53
|
const dx = scratch_array_f32[0] - scratch_array_f32[3];
|
|
44
54
|
const dy = scratch_array_f32[1] - scratch_array_f32[4];
|
|
45
55
|
const dz = scratch_array_f32[2] - scratch_array_f32[5];
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
return dx * dx + dy * dy + dz * dz;
|
|
57
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
/**
|
|
61
|
+
* Walk parent pointers from goal back to start, writing the face sequence into `output` in
|
|
62
|
+
* START -> GOAL order. Never writes more than `max_path_length` entries: if the corridor is longer
|
|
63
|
+
* than that it does not fit the caller's buffer and 0 is returned (no partial/garbage result is used).
|
|
52
64
|
*
|
|
53
65
|
* @param {number[]|Uint32Array} output
|
|
54
|
-
* @param {number}
|
|
55
|
-
* @param {
|
|
56
|
-
* @param {
|
|
57
|
-
* @
|
|
66
|
+
* @param {number} start_face
|
|
67
|
+
* @param {number} goal_face
|
|
68
|
+
* @param {Map<number, number>} came_from
|
|
69
|
+
* @param {number} max_path_length
|
|
70
|
+
* @returns {number} number of faces written, or 0 if the corridor exceeds `max_path_length`
|
|
58
71
|
*/
|
|
59
|
-
function construct_path(output,
|
|
60
|
-
|
|
61
|
-
let current =
|
|
62
|
-
let path_length = 0;
|
|
72
|
+
function construct_path(output, start_face, goal_face, came_from, max_path_length) {
|
|
73
|
+
let length = 0;
|
|
74
|
+
let current = goal_face;
|
|
63
75
|
|
|
64
|
-
//
|
|
76
|
+
// walk back to the start, writing in reverse (goal -> start)
|
|
65
77
|
while (true) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// If we reached the start node (g_score === 0), we're done traversing
|
|
71
|
-
if (current_g === 0) {
|
|
72
|
-
break;
|
|
78
|
+
if (length >= max_path_length) {
|
|
79
|
+
// corridor does not fit the output buffer; refuse rather than writing out of bounds
|
|
80
|
+
return 0;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
output[length] = current;
|
|
84
|
+
length++;
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
let lowest_g = Infinity;
|
|
79
|
-
|
|
80
|
-
// Find the neighbor with the lowest g_score
|
|
81
|
-
for (let i = 0; i < neighbor_count; i++) {
|
|
82
|
-
const neighbor = neighbors[i];
|
|
83
|
-
|
|
84
|
-
if (g_score.has(neighbor)) {
|
|
85
|
-
const g = g_score.get(neighbor);
|
|
86
|
-
|
|
87
|
-
if (g < lowest_g) {
|
|
88
|
-
lowest_g = g;
|
|
89
|
-
best_neighbor = neighbor;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Safeguard: Break if no valid neighbor is found or if we aren't strictly descending.
|
|
96
|
-
// (Since your traversal_cost is currently 1.0, lowest_g should be current_g - 1.0)
|
|
97
|
-
if (best_neighbor === -1 || lowest_g >= current_g) {
|
|
86
|
+
if (current === start_face) {
|
|
98
87
|
break;
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
current =
|
|
90
|
+
current = came_from.get(current);
|
|
102
91
|
}
|
|
103
92
|
|
|
104
|
-
//
|
|
105
|
-
const half_length =
|
|
93
|
+
// reverse in place to get START -> GOAL order
|
|
94
|
+
const half_length = length >> 1;
|
|
106
95
|
|
|
107
96
|
for (let i = 0; i < half_length; i++) {
|
|
108
|
-
const mirror_i =
|
|
97
|
+
const mirror_i = length - 1 - i;
|
|
109
98
|
const temp = output[i];
|
|
110
99
|
|
|
111
100
|
output[i] = output[mirror_i];
|
|
112
101
|
output[mirror_i] = temp;
|
|
113
102
|
}
|
|
114
103
|
|
|
115
|
-
return
|
|
104
|
+
return length;
|
|
116
105
|
}
|
|
117
106
|
|
|
118
107
|
|
|
119
108
|
/**
|
|
120
|
-
* Find a path through topology faces.
|
|
109
|
+
* Find a shortest path through topology faces.
|
|
121
110
|
* If a path is found - the result will contain start and goal faces.
|
|
122
111
|
*
|
|
123
112
|
* NOTE: if either start or goal faces are not part of the topology - an empty path will be produced.
|
|
@@ -126,13 +115,18 @@ function construct_path(output, goal, g_score, topology) {
|
|
|
126
115
|
* @param {number} start_face_id
|
|
127
116
|
* @param {number} goal_face_id
|
|
128
117
|
* @param {BinaryTopology} topology
|
|
129
|
-
* @
|
|
118
|
+
* @param {number} [max_path_length=Infinity] cap on the number of faces written to `output`. If the
|
|
119
|
+
* shortest corridor is longer than this it does not fit the buffer and 0 is returned. Pass the
|
|
120
|
+
* capacity of `output` to guarantee no out-of-bounds writes.
|
|
121
|
+
* @returns {number} number of faces that make up the path. This will be 0 if no path is found (or the
|
|
122
|
+
* path does not fit `max_path_length`).
|
|
130
123
|
*/
|
|
131
124
|
export function bt_mesh_face_find_path(
|
|
132
125
|
output,
|
|
133
126
|
start_face_id,
|
|
134
127
|
goal_face_id,
|
|
135
128
|
topology,
|
|
129
|
+
max_path_length = Infinity,
|
|
136
130
|
) {
|
|
137
131
|
assert.isArrayLike(output, 'output');
|
|
138
132
|
assert.isNonNegativeInteger(start_face_id, 'start_face_id');
|
|
@@ -141,35 +135,35 @@ export function bt_mesh_face_find_path(
|
|
|
141
135
|
assert.defined(topology, 'topology');
|
|
142
136
|
assert.equal(topology.isBinaryTopology, true, 'topology.isBinaryTopology !== true');
|
|
143
137
|
|
|
144
|
-
// TODO we can switch traversal to edges instead, that way we can get much better distance heuristics
|
|
145
|
-
// we don't know where exactly we're going to cross through the triangle, but we know we're going to cross the edge at least, which narrows the domain
|
|
146
|
-
|
|
147
138
|
open.clear();
|
|
148
139
|
closed.clear();
|
|
149
140
|
g_score.clear();
|
|
141
|
+
came_from.clear();
|
|
150
142
|
|
|
151
143
|
g_score.set(start_face_id, 0);
|
|
152
144
|
|
|
153
|
-
open.insert(start_face_id,
|
|
145
|
+
open.insert(start_face_id, face_centroid_distance(start_face_id, goal_face_id, topology));
|
|
154
146
|
|
|
155
147
|
while (!open.is_empty()) {
|
|
156
148
|
|
|
157
149
|
const current_node = open.pop_min();
|
|
158
150
|
|
|
159
|
-
// Lazy
|
|
160
|
-
// push. The lowest-
|
|
161
|
-
//
|
|
151
|
+
// Lazy deletion: a face may sit in the heap multiple times if its g-score improved after an
|
|
152
|
+
// earlier push. The lowest-f entry is popped first; later popped duplicates land on an
|
|
153
|
+
// already-closed face and must be skipped.
|
|
162
154
|
if (closed.has(current_node)) {
|
|
163
155
|
continue;
|
|
164
156
|
}
|
|
165
157
|
|
|
166
158
|
if (current_node === goal_face_id) {
|
|
167
159
|
// Reached the goal
|
|
168
|
-
return construct_path(output, goal_face_id,
|
|
160
|
+
return construct_path(output, start_face_id, goal_face_id, came_from, max_path_length);
|
|
169
161
|
}
|
|
170
162
|
|
|
171
163
|
closed.add(current_node);
|
|
172
164
|
|
|
165
|
+
const current_g_score = g_score.get(current_node);
|
|
166
|
+
|
|
173
167
|
const neighbor_count = bt_face_get_neighbour_faces(neighbors, 0, topology, current_node);
|
|
174
168
|
|
|
175
169
|
for (let i = 0; i < neighbor_count; i++) {
|
|
@@ -181,26 +175,26 @@ export function bt_mesh_face_find_path(
|
|
|
181
175
|
continue;
|
|
182
176
|
}
|
|
183
177
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const current_g_score = g_score.get(current_node);
|
|
178
|
+
// step cost is the real geometric distance between adjacent face centroids, so the
|
|
179
|
+
// search minimises corridor length rather than the number of triangles crossed
|
|
180
|
+
const traversal_cost = face_centroid_distance(current_node, neighbor, topology);
|
|
188
181
|
|
|
189
182
|
const cost_so_far = current_g_score + traversal_cost;
|
|
190
183
|
|
|
191
184
|
if (!g_score.has(neighbor) || cost_so_far < g_score.get(neighbor)) {
|
|
192
|
-
// better path
|
|
185
|
+
// better path to this neighbour
|
|
193
186
|
|
|
194
187
|
g_score.set(neighbor, cost_so_far);
|
|
188
|
+
came_from.set(neighbor, current_node);
|
|
195
189
|
|
|
196
|
-
const remaining_heuristic =
|
|
190
|
+
const remaining_heuristic = face_centroid_distance(neighbor, goal_face_id, topology);
|
|
197
191
|
|
|
198
|
-
const
|
|
192
|
+
const f_score = cost_so_far + remaining_heuristic;
|
|
199
193
|
|
|
200
|
-
// Lazy push: always insert. If neighbor was already on the open list, the older
|
|
201
|
-
// entry will be skipped when popped (see closed-check at the top of the
|
|
202
|
-
// an O(n) find_index_by_id scan per relaxation.
|
|
203
|
-
open.insert(neighbor,
|
|
194
|
+
// Lazy push: always insert. If neighbor was already on the open list, the older
|
|
195
|
+
// (worse-f) entry will be skipped when popped (see closed-check at the top of the
|
|
196
|
+
// loop). This avoids an O(n) find_index_by_id scan per relaxation.
|
|
197
|
+
open.insert(neighbor, f_score);
|
|
204
198
|
|
|
205
199
|
}
|
|
206
200
|
|
|
@@ -210,4 +204,4 @@ export function bt_mesh_face_find_path(
|
|
|
210
204
|
|
|
211
205
|
// No result found
|
|
212
206
|
return 0;
|
|
213
|
-
}
|
|
207
|
+
}
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Knock holes in the walkable triangle soup wherever an agent of the given height would not fit under
|
|
3
|
+
* overhead geometry.
|
|
2
4
|
*
|
|
3
|
-
*
|
|
5
|
+
* Approach: subdivide each candidate triangle to a resolution tied to the agent's footprint, cast an
|
|
6
|
+
* upward clearance ray from each sub-triangle centroid against the source geometry, and keep only the
|
|
7
|
+
* sub-triangles that have clearance. Fully-clear triangles are emitted unchanged (no subdivision), so
|
|
8
|
+
* the common case adds no triangles; fully-blocked triangles are dropped; partially-blocked triangles
|
|
9
|
+
* are replaced by their clear sub-triangles. Accuracy is bounded by the sampling resolution.
|
|
10
|
+
*
|
|
11
|
+
* The `triangles` array is rewritten in place with the surviving triangles.
|
|
12
|
+
*
|
|
13
|
+
* @param {BVH} bvh source-geometry BVH (leaves carry source face IDs)
|
|
14
|
+
* @param {BinaryTopology} source source mesh, used to resolve face IDs to triangle vertices
|
|
4
15
|
* @param {number} agent_height
|
|
5
16
|
* @param {number} agent_radius
|
|
6
|
-
* @param {number} triangle_count
|
|
7
|
-
* @param {number[]} triangles
|
|
8
|
-
* @param {
|
|
17
|
+
* @param {number} triangle_count number of triangles currently in `triangles`
|
|
18
|
+
* @param {number[]} triangles flat XYZ soup, 9 floats per triangle
|
|
19
|
+
* @param {Vector3} up world up direction
|
|
9
20
|
* @returns {number} new triangle count
|
|
10
21
|
*/
|
|
11
|
-
export function enforce_agent_height_clearance({ bvh, agent_height, agent_radius, triangle_count, triangles,
|
|
22
|
+
export function enforce_agent_height_clearance({ bvh, source, agent_height, agent_radius, triangle_count, triangles, up, }: BVH): number;
|
|
12
23
|
//# sourceMappingURL=enforce_agent_height_clearance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enforce_agent_height_clearance.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/mesh/build/enforce_agent_height_clearance.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"enforce_agent_height_clearance.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/mesh/build/enforce_agent_height_clearance.js"],"names":[],"mappings":"AAmHA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,kIAFa,MAAM,CA4IlB"}
|