@woosh/meep-engine 2.163.6 → 2.163.8

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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.d.ts +21 -0
  3. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.d.ts.map +1 -0
  4. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +42 -0
  5. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.d.ts +2 -2
  6. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.d.ts.map +1 -1
  7. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +120 -179
  8. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts +9 -10
  9. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
  10. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +20 -14
  11. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.d.ts +17 -0
  12. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.d.ts.map +1 -0
  13. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -0
  14. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.d.ts +40 -0
  15. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.d.ts.map +1 -0
  16. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +84 -0
  17. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -78
  19. package/src/core/geom/vec3/v3_matrix3_rotate.d.ts +16 -0
  20. package/src/core/geom/vec3/v3_matrix3_rotate.d.ts.map +1 -0
  21. package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -0
  22. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.d.ts +2 -2
  23. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.d.ts.map +1 -1
  24. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +46 -46
  25. package/src/engine/graphics/sh3/path_tracer/sampling/getBiasedNormalSample.d.ts.map +1 -1
  26. package/src/engine/graphics/sh3/path_tracer/sampling/getBiasedNormalSample.js +6 -28
  27. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +185 -0
  28. package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts +11 -0
  29. package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts.map +1 -1
  30. package/src/engine/navigation/mesh/bt_mesh_face_find_path.js +623 -100
  31. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts +11 -0
  32. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -0
  33. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +472 -0
  34. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  35. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +36 -39
  36. package/src/engine/navigation/mesh/navmesh_polyanya_find_path.d.ts +17 -0
  37. package/src/engine/navigation/mesh/navmesh_polyanya_find_path.d.ts.map +1 -0
  38. package/src/engine/navigation/mesh/navmesh_polyanya_find_path.js +613 -0
  39. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts +0 -28
  40. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts.map +0 -1
  41. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.js +0 -358
  42. package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts +0 -23
  43. package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts.map +0 -1
  44. package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.js +0 -319
@@ -1,319 +0,0 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { NULL_NODE } from "../../../../core/bvh2/bvh3/BVH.js";
3
- import {
4
- bvh_query_leaves_ray_segment
5
- } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js";
6
- import { NULL_POINTER } from "../../../../core/geom/3d/topology/struct/binary/BinaryTopology.js";
7
- import { compute_triangle_area_3d } from "../../../../core/geom/3d/triangle/compute_triangle_area_3d.js";
8
- import {
9
- computeTriangleRayIntersectionBarycentric
10
- } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js";
11
- import { clamp } from "../../../../core/math/clamp.js";
12
-
13
- /**
14
- * Upper bound on the per-edge subdivision factor, so a single huge triangle cannot explode into an
15
- * unbounded number of sub-triangles. At this cap a triangle is sampled with up to K_MAX^2 points.
16
- * @type {number}
17
- */
18
- const K_MAX = 64;
19
-
20
- /**
21
- * Small offset (in world units) used to lift a ray origin off the surface it sits on, so the upward
22
- * clearance ray does not immediately re-intersect the floor it starts from.
23
- * @type {number}
24
- */
25
- const SURFACE_EPSILON = 1e-4;
26
-
27
- // reused scratch (build-time, but there's no reason to churn allocations per triangle)
28
- const ray_leaf_buffer = [];
29
- const intersection_result = new Float32Array(6);
30
- const tri_a = new Float32Array(3);
31
- const tri_b = new Float32Array(3);
32
- const tri_c = new Float32Array(3);
33
-
34
- /**
35
- * True if there is no source geometry within `agent_height` directly above the point, measured along
36
- * the world up direction. Casts a ray up from the point (nudged off the surface) and checks every
37
- * source face whose AABB the ray crosses with a precise ray-triangle test.
38
- *
39
- * @param {BVH} bvh source-geometry BVH (leaves carry source face IDs)
40
- * @param {BinaryTopology} source source mesh, used to resolve face IDs to triangle vertices
41
- * @param {number} px
42
- * @param {number} py
43
- * @param {number} pz
44
- * @param {number} up_x normalized world up
45
- * @param {number} up_y
46
- * @param {number} up_z
47
- * @param {number} agent_height
48
- * @returns {boolean}
49
- */
50
- function point_has_clearance(bvh, source, px, py, pz, up_x, up_y, up_z, agent_height) {
51
- const origin_x = px + up_x * SURFACE_EPSILON;
52
- const origin_y = py + up_y * SURFACE_EPSILON;
53
- const origin_z = pz + up_z * SURFACE_EPSILON;
54
-
55
- const leaf_count = bvh_query_leaves_ray_segment(
56
- bvh, bvh.root,
57
- ray_leaf_buffer, 0,
58
- origin_x, origin_y, origin_z,
59
- up_x, up_y, up_z,
60
- 0, agent_height
61
- );
62
-
63
- for (let i = 0; i < leaf_count; i++) {
64
- const node = ray_leaf_buffer[i];
65
- const face_id = bvh.node_get_user_data(node);
66
-
67
- const loop_a = source.face_read_loop(face_id);
68
-
69
- if (loop_a === NULL_POINTER) {
70
- continue;
71
- }
72
-
73
- const loop_b = source.loop_read_next(loop_a);
74
- const loop_c = source.loop_read_next(loop_b);
75
-
76
- source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(loop_a));
77
- source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(loop_b));
78
- source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(loop_c));
79
-
80
- const hit = computeTriangleRayIntersectionBarycentric(
81
- intersection_result,
82
- origin_x, origin_y, origin_z,
83
- up_x, up_y, up_z,
84
- tri_a[0], tri_a[1], tri_a[2],
85
- tri_b[0], tri_b[1], tri_b[2],
86
- tri_c[0], tri_c[1], tri_c[2]
87
- );
88
-
89
- if (!hit) {
90
- continue;
91
- }
92
-
93
- const t = intersection_result[0];
94
-
95
- if (t > 0 && t <= agent_height) {
96
- // an overhead obstruction sits within the agent's height
97
- return false;
98
- }
99
- }
100
-
101
- return true;
102
- }
103
-
104
- /**
105
- * Append a triangle (9 floats) to `output`.
106
- */
107
- function emit_triangle(
108
- output,
109
- ax, ay, az,
110
- bx, by, bz,
111
- cx, cy, cz
112
- ) {
113
- output.push(ax, ay, az, bx, by, bz, cx, cy, cz);
114
- }
115
-
116
- /**
117
- * Knock holes in the walkable triangle soup wherever an agent of the given height would not fit under
118
- * overhead geometry.
119
- *
120
- * Approach: subdivide each candidate triangle to a resolution tied to the agent's footprint, cast an
121
- * upward clearance ray from each sub-triangle centroid against the source geometry, and keep only the
122
- * sub-triangles that have clearance. Fully-clear triangles are emitted unchanged (no subdivision), so
123
- * the common case adds no triangles; fully-blocked triangles are dropped; partially-blocked triangles
124
- * are replaced by their clear sub-triangles. Accuracy is bounded by the sampling resolution.
125
- *
126
- * The `triangles` array is rewritten in place with the surviving triangles.
127
- *
128
- * @param {BVH} bvh source-geometry BVH (leaves carry source face IDs)
129
- * @param {BinaryTopology} source source mesh, used to resolve face IDs to triangle vertices
130
- * @param {number} agent_height
131
- * @param {number} agent_radius
132
- * @param {number} triangle_count number of triangles currently in `triangles`
133
- * @param {number[]} triangles flat XYZ soup, 9 floats per triangle
134
- * @param {Vector3} up world up direction
135
- * @returns {number} new triangle count
136
- */
137
- export function enforce_agent_height_clearance({
138
- bvh,
139
- source,
140
- agent_height,
141
- agent_radius,
142
- triangle_count,
143
- triangles,
144
- up,
145
- }) {
146
-
147
- assert.defined(bvh, 'bvh');
148
- assert.defined(source, 'source');
149
- assert.greaterThan(agent_height, 0, 'agent_height');
150
-
151
- if (bvh.root === NULL_NODE) {
152
- // no source geometry to obstruct anything
153
- return triangle_count;
154
- }
155
-
156
- // normalize up so ray-intersection distances are world distances
157
- let up_x = up.x;
158
- let up_y = up.y;
159
- let up_z = up.z;
160
-
161
- const up_length = Math.sqrt(up_x * up_x + up_y * up_y + up_z * up_z);
162
-
163
- assert.greaterThan(up_length, 0, 'up vector length');
164
-
165
- up_x /= up_length;
166
- up_y /= up_length;
167
- up_z /= up_length;
168
-
169
- /**
170
- * Resolution at which clearance is sampled. Tied to the agent footprint so the sampling grid is
171
- * fine enough to resolve gaps the agent could (or could not) fit through.
172
- * @type {number}
173
- */
174
- const sampling_resolution = Math.min(
175
- agent_height / 4,
176
- Math.max(agent_radius / 2, 0.001)
177
- );
178
-
179
- assert.isFinite(sampling_resolution, 'sampling_resolution');
180
- assert.greaterThan(sampling_resolution, 0, 'sampling_resolution');
181
-
182
- /**
183
- * Surviving triangles. Built separately because the count can grow (subdivision), then copied back.
184
- * @type {number[]}
185
- */
186
- const output = [];
187
-
188
- for (let i = 0; i < triangle_count; i++) {
189
- const address = i * 9;
190
-
191
- const ax = triangles[address];
192
- const ay = triangles[address + 1];
193
- const az = triangles[address + 2];
194
-
195
- const bx = triangles[address + 3];
196
- const by = triangles[address + 4];
197
- const bz = triangles[address + 5];
198
-
199
- const cx = triangles[address + 6];
200
- const cy = triangles[address + 7];
201
- const cz = triangles[address + 8];
202
-
203
- const area = compute_triangle_area_3d(ax, ay, az, bx, by, bz, cx, cy, cz);
204
-
205
- if (area === 0) {
206
- // degenerate, nothing meaningful to sample - keep as-is
207
- emit_triangle(output, ax, ay, az, bx, by, bz, cx, cy, cz);
208
- continue;
209
- }
210
-
211
- // edges, used both to size the subdivision and to place sub-triangle vertices
212
- const e0x = bx - ax, e0y = by - ay, e0z = bz - az; // a -> b
213
- const e1x = cx - ax, e1y = cy - ay, e1z = cz - az; // a -> c
214
-
215
- const len_ab = Math.sqrt(e0x * e0x + e0y * e0y + e0z * e0z);
216
- const len_ac = Math.sqrt(e1x * e1x + e1y * e1y + e1z * e1z);
217
- const dbcx = cx - bx, dbcy = cy - by, dbcz = cz - bz;
218
- const len_bc = Math.sqrt(dbcx * dbcx + dbcy * dbcy + dbcz * dbcz);
219
-
220
- const longest_edge = Math.max(len_ab, len_ac, len_bc);
221
-
222
- const k = clamp(Math.ceil(longest_edge / sampling_resolution), 1, K_MAX);
223
-
224
- const inv_k = 1 / k;
225
-
226
- // Sub-triangle vertices are P(i,j) = a + (i/k)*(b-a) + (j/k)*(c-a), for i+j <= k.
227
- // Walk the k^2 sub-triangles (k*(k+... )/...) testing each centroid for clearance.
228
- let any_blocked = false;
229
- let any_clear = false;
230
-
231
- // reuse a per-triangle scratch list of clear sub-triangles only when we actually subdivide
232
- const clear_subtriangles = [];
233
-
234
- for (let row = 0; row < k; row++) {
235
- for (let col = 0; col < k - row; col++) {
236
- // "upward" sub-triangle: P(col,row), P(col+1,row), P(col,row+1)
237
- emit_subtriangle_if_clear(
238
- bvh, source, agent_height, up_x, up_y, up_z,
239
- ax, ay, az, e0x, e0y, e0z, e1x, e1y, e1z, inv_k,
240
- col, row, col + 1, row, col, row + 1,
241
- clear_subtriangles
242
- ) ? (any_clear = true) : (any_blocked = true);
243
-
244
- // "downward" sub-triangle: P(col+1,row), P(col+1,row+1), P(col,row+1)
245
- if (col < k - row - 1) {
246
- emit_subtriangle_if_clear(
247
- bvh, source, agent_height, up_x, up_y, up_z,
248
- ax, ay, az, e0x, e0y, e0z, e1x, e1y, e1z, inv_k,
249
- col + 1, row, col + 1, row + 1, col, row + 1,
250
- clear_subtriangles
251
- ) ? (any_clear = true) : (any_blocked = true);
252
- }
253
- }
254
- }
255
-
256
- if (!any_blocked) {
257
- // fully clear - keep the original triangle, no subdivision, no bloat
258
- emit_triangle(output, ax, ay, az, bx, by, bz, cx, cy, cz);
259
- } else if (any_clear) {
260
- // partially blocked - replace with the surviving sub-triangles
261
- for (let s = 0; s < clear_subtriangles.length; s++) {
262
- output.push(clear_subtriangles[s]);
263
- }
264
- }
265
- // else fully blocked - drop the triangle entirely
266
- }
267
-
268
- // rewrite the soup in place with the survivors
269
- for (let i = 0; i < output.length; i++) {
270
- triangles[i] = output[i];
271
- }
272
- triangles.length = output.length;
273
-
274
- return (output.length / 9) | 0;
275
- }
276
-
277
- /**
278
- * Build sub-triangle vertices P(i,j) = a + (i/k)*e0 + (j/k)*e1, test its centroid for clearance, and
279
- * push its 9 coordinates into `out` when clear. Returns whether it was clear.
280
- */
281
- function emit_subtriangle_if_clear(
282
- bvh, source, agent_height, up_x, up_y, up_z,
283
- ax, ay, az, e0x, e0y, e0z, e1x, e1y, e1z, inv_k,
284
- i0, j0, i1, j1, i2, j2,
285
- out
286
- ) {
287
- const s0 = i0 * inv_k, t0 = j0 * inv_k;
288
- const s1 = i1 * inv_k, t1 = j1 * inv_k;
289
- const s2 = i2 * inv_k, t2 = j2 * inv_k;
290
-
291
- const p0x = ax + s0 * e0x + t0 * e1x;
292
- const p0y = ay + s0 * e0y + t0 * e1y;
293
- const p0z = az + s0 * e0z + t0 * e1z;
294
-
295
- const p1x = ax + s1 * e0x + t1 * e1x;
296
- const p1y = ay + s1 * e0y + t1 * e1y;
297
- const p1z = az + s1 * e0z + t1 * e1z;
298
-
299
- const p2x = ax + s2 * e0x + t2 * e1x;
300
- const p2y = ay + s2 * e0y + t2 * e1y;
301
- const p2z = az + s2 * e0z + t2 * e1z;
302
-
303
- const centroid_x = (p0x + p1x + p2x) / 3;
304
- const centroid_y = (p0y + p1y + p2y) / 3;
305
- const centroid_z = (p0z + p1z + p2z) / 3;
306
-
307
- const clear = point_has_clearance(
308
- bvh, source,
309
- centroid_x, centroid_y, centroid_z,
310
- up_x, up_y, up_z,
311
- agent_height
312
- );
313
-
314
- if (clear) {
315
- out.push(p0x, p0y, p0z, p1x, p1y, p1z, p2x, p2y, p2z);
316
- }
317
-
318
- return clear;
319
- }