@woosh/meep-engine 2.163.1 → 2.163.3

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 (51) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.d.ts +12 -0
  3. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.d.ts.map +1 -0
  4. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -0
  5. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.d.ts +14 -0
  6. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.d.ts.map +1 -0
  7. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -0
  8. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.d.ts.map +1 -1
  9. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +368 -290
  10. package/src/core/geom/vec3/v3_uniform_sample_cone.d.ts +11 -0
  11. package/src/core/geom/vec3/v3_uniform_sample_cone.d.ts.map +1 -0
  12. package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -0
  13. package/src/core/math/physics/brdf/cone_cosine_from_roughness.d.ts +13 -0
  14. package/src/core/math/physics/brdf/cone_cosine_from_roughness.d.ts.map +1 -0
  15. package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -0
  16. package/src/core/math/physics/brdf/reflection_sample_weight.d.ts +18 -0
  17. package/src/core/math/physics/brdf/reflection_sample_weight.d.ts.map +1 -0
  18. package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -0
  19. package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
  20. package/src/engine/graphics/GraphicsEngine.js +52 -0
  21. package/src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.d.ts.map +1 -1
  22. package/src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +306 -226
  23. package/src/engine/graphics/ecs/path/tube/build/make_cap.d.ts.map +1 -1
  24. package/src/engine/graphics/ecs/path/tube/build/make_cap.js +26 -17
  25. package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.d.ts +26 -0
  26. package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.d.ts.map +1 -0
  27. package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.js +49 -0
  28. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.d.ts +26 -0
  29. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.d.ts.map +1 -0
  30. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -0
  31. package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.d.ts +24 -0
  32. package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.d.ts.map +1 -0
  33. package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.js +51 -0
  34. package/src/engine/graphics/texture/EnvironmentTextureProjection.d.ts +9 -0
  35. package/src/engine/graphics/texture/EnvironmentTextureProjection.d.ts.map +1 -0
  36. package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -0
  37. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.d.ts +44 -0
  38. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.d.ts.map +1 -0
  39. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -0
  40. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.d.ts +25 -0
  41. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.d.ts.map +1 -0
  42. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -0
  43. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.d.ts +15 -0
  44. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.d.ts.map +1 -0
  45. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -0
  46. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts +27 -0
  47. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts.map +1 -0
  48. package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.js +323 -0
  49. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  50. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +223 -226
  51. package/src/engine/.fuse_hidden0000001500000001 +0 -581
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Carve holes into a (welded, connected) walkable topology wherever an agent of the given height
3
+ * would not fit under overhead geometry.
4
+ *
5
+ * Operates on the topology rather than a triangle soup so that all subdivision is conformal:
6
+ * {@link bt_edge_split} re-triangulates every face around a split edge, so neighbouring faces can
7
+ * never disagree on a shared edge (no cracks / T-junctions). Refinement is adaptive - only faces whose
8
+ * column actually contains an overhang are subdivided, and only down to `resolution` - so clear areas
9
+ * stay coarse and the subdivision hugs the obstacle contour instead of tessellating whole triangles.
10
+ *
11
+ * @param {object} params
12
+ * @param {BinaryTopology} params.mesh walkable topology to carve (modified in place)
13
+ * @param {BinaryTopology} params.source original source mesh (walkable + overhead geometry)
14
+ * @param {BVH} params.source_bvh BVH over `source` (leaves carry source face IDs)
15
+ * @param {number} params.agent_height
16
+ * @param {number} params.agent_radius
17
+ * @param {Vector3} params.up world up direction
18
+ */
19
+ export function bt_mesh_carve_height_clearance({ mesh, source, source_bvh, agent_height, agent_radius, up, }: {
20
+ mesh: BinaryTopology;
21
+ source: BinaryTopology;
22
+ source_bvh: BVH;
23
+ agent_height: number;
24
+ agent_radius: number;
25
+ up: Vector3;
26
+ }): void;
27
+ //# sourceMappingURL=bt_mesh_carve_height_clearance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_carve_height_clearance.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.js"],"names":[],"mappings":"AA2LA;;;;;;;;;;;;;;;;;GAiBG;AACH;IAPkC,IAAI;IACJ,MAAM;IACjB,UAAU;IACP,YAAY,EAA3B,MAAM;IACS,YAAY,EAA3B,MAAM;IACU,EAAE;SAuH5B"}
@@ -0,0 +1,323 @@
1
+ import { NULL_NODE } from "../../../../core/bvh2/bvh3/BVH.js";
2
+ import {
3
+ bvh_query_leaves_ray_segment
4
+ } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js";
5
+ import {
6
+ bvh_query_user_data_overlaps_aabb
7
+ } from "../../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js";
8
+ import { NULL_POINTER } from "../../../../core/geom/3d/topology/struct/binary/BinaryTopology.js";
9
+ import {
10
+ bt_mesh_cleanup_faceless_references
11
+ } from "../../../../core/geom/3d/topology/struct/binary/io/bt_mesh_cleanup_faceless_references.js";
12
+ import { bt_edge_split } from "../../../../core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js";
13
+ import { bt_face_kill } from "../../../../core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js";
14
+ import {
15
+ computeTriangleRayIntersectionBarycentric
16
+ } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js";
17
+
18
+ /**
19
+ * Lift a ray origin off the surface so the upward clearance ray does not immediately re-hit the floor.
20
+ * @type {number}
21
+ */
22
+ const SURFACE_EPSILON = 1e-4;
23
+
24
+ /**
25
+ * Horizontal inset applied to a face footprint before testing for overhead geometry. Prevents a
26
+ * neighbour that merely *touches* a face along a shared edge (e.g. an adjacent platform at a slightly
27
+ * different height) from being mistaken for an overhang. A real overhang overlaps the interior with
28
+ * positive area and still registers.
29
+ * @type {number}
30
+ */
31
+ const FOOTPRINT_INSET = 1e-2;
32
+
33
+ /**
34
+ * Safety cap on the number of faces the refinement is allowed to grow to, so a pathological input
35
+ * cannot loop forever.
36
+ * @type {number}
37
+ */
38
+ const MAX_FACES = 200000;
39
+
40
+ // reused scratch
41
+ const ray_leaf_buffer = [];
42
+ const intersection_result = new Float32Array(6);
43
+ const tri_a = new Float32Array(3);
44
+ const tri_b = new Float32Array(3);
45
+ const tri_c = new Float32Array(3);
46
+
47
+ const va = new Float32Array(3);
48
+ const vb = new Float32Array(3);
49
+ const vc = new Float32Array(3);
50
+
51
+ const overhead_hits = [];
52
+ const query_aabb = new Float32Array(6);
53
+
54
+ /**
55
+ * True if there is no source geometry within `agent_height` directly above the point along `up`.
56
+ * (Mirrors the clearance ray-cast used by the old soup-based pass.)
57
+ */
58
+ function point_has_clearance(source_bvh, source, px, py, pz, up_x, up_y, up_z, agent_height) {
59
+ const origin_x = px + up_x * SURFACE_EPSILON;
60
+ const origin_y = py + up_y * SURFACE_EPSILON;
61
+ const origin_z = pz + up_z * SURFACE_EPSILON;
62
+
63
+ const leaf_count = bvh_query_leaves_ray_segment(
64
+ source_bvh, source_bvh.root,
65
+ ray_leaf_buffer, 0,
66
+ origin_x, origin_y, origin_z,
67
+ up_x, up_y, up_z,
68
+ 0, agent_height
69
+ );
70
+
71
+ for (let i = 0; i < leaf_count; i++) {
72
+ const node = ray_leaf_buffer[i];
73
+ const face_id = source_bvh.node_get_user_data(node);
74
+
75
+ const loop_a = source.face_read_loop(face_id);
76
+ if (loop_a === NULL_POINTER) {
77
+ continue;
78
+ }
79
+ const loop_b = source.loop_read_next(loop_a);
80
+ const loop_c = source.loop_read_next(loop_b);
81
+
82
+ source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(loop_a));
83
+ source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(loop_b));
84
+ source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(loop_c));
85
+
86
+ const hit = computeTriangleRayIntersectionBarycentric(
87
+ intersection_result,
88
+ origin_x, origin_y, origin_z,
89
+ up_x, up_y, up_z,
90
+ tri_a[0], tri_a[1], tri_a[2],
91
+ tri_b[0], tri_b[1], tri_b[2],
92
+ tri_c[0], tri_c[1], tri_c[2]
93
+ );
94
+ if (!hit) {
95
+ continue;
96
+ }
97
+
98
+ const t = intersection_result[0];
99
+ if (t > 0 && t <= agent_height) {
100
+ // overhead obstruction within the agent's height
101
+ return false;
102
+ }
103
+ }
104
+
105
+ return true;
106
+ }
107
+
108
+ /**
109
+ * Read the three vertices of a triangular face into va/vb/vc and return its loops.
110
+ */
111
+ function read_face_triangle(mesh, face_id) {
112
+ const la = mesh.face_read_loop(face_id);
113
+ const lb = mesh.loop_read_next(la);
114
+ const lc = mesh.loop_read_next(lb);
115
+
116
+ mesh.vertex_read_coordinate(va, 0, mesh.loop_read_vertex(la));
117
+ mesh.vertex_read_coordinate(vb, 0, mesh.loop_read_vertex(lb));
118
+ mesh.vertex_read_coordinate(vc, 0, mesh.loop_read_vertex(lc));
119
+
120
+ return { la, lb, lc };
121
+ }
122
+
123
+ /**
124
+ * Does the upward column (height `agent_height` along `up`) over this face's footprint contain any
125
+ * source geometry that sits *above* the face? Uses a loose axis-aligned box (conservative), inset
126
+ * horizontally so edge-touching neighbours do not count. May report true for a face whose triangle
127
+ * does not actually pass under the overhang, which only costs a little extra refinement, never a
128
+ * wrong cull.
129
+ */
130
+ function column_has_overhead(source_bvh, source, face_max_along_up, up_x, up_y, up_z, agent_height) {
131
+ let min_x = Math.min(va[0], vb[0], vc[0]);
132
+ let min_y = Math.min(va[1], vb[1], vc[1]);
133
+ let min_z = Math.min(va[2], vb[2], vc[2]);
134
+ let max_x = Math.max(va[0], vb[0], vc[0]);
135
+ let max_y = Math.max(va[1], vb[1], vc[1]);
136
+ let max_z = Math.max(va[2], vb[2], vc[2]);
137
+
138
+ // inset the footprint on the axes perpendicular to up (so touching-only neighbours are excluded)
139
+ const ex = FOOTPRINT_INSET * (1 - Math.abs(up_x));
140
+ const ey = FOOTPRINT_INSET * (1 - Math.abs(up_y));
141
+ const ez = FOOTPRINT_INSET * (1 - Math.abs(up_z));
142
+ min_x += ex; max_x -= ex;
143
+ min_y += ey; max_y -= ey;
144
+ min_z += ez; max_z -= ez;
145
+ if (min_x > max_x) { const m = (min_x + max_x) * 0.5; min_x = m; max_x = m; }
146
+ if (min_y > max_y) { const m = (min_y + max_y) * 0.5; min_y = m; max_y = m; }
147
+ if (min_z > max_z) { const m = (min_z + max_z) * 0.5; min_z = m; max_z = m; }
148
+
149
+ // sweep the box by agent_height along the up direction
150
+ const dx = up_x * agent_height, dy = up_y * agent_height, dz = up_z * agent_height;
151
+ if (dx > 0) max_x += dx; else min_x += dx;
152
+ if (dy > 0) max_y += dy; else min_y += dy;
153
+ if (dz > 0) max_z += dz; else min_z += dz;
154
+
155
+ query_aabb[0] = min_x; query_aabb[1] = min_y; query_aabb[2] = min_z;
156
+ query_aabb[3] = max_x; query_aabb[4] = max_y; query_aabb[5] = max_z;
157
+
158
+ const count = bvh_query_user_data_overlaps_aabb(overhead_hits, 0, source_bvh, query_aabb);
159
+
160
+ for (let i = 0; i < count; i++) {
161
+ const fid = overhead_hits[i];
162
+
163
+ const la = source.face_read_loop(fid);
164
+ if (la === NULL_POINTER) continue;
165
+ const lb = source.loop_read_next(la);
166
+ const lc = source.loop_read_next(lb);
167
+
168
+ source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(la));
169
+ source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(lb));
170
+ source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(lc));
171
+
172
+ // lowest extent of the candidate along up
173
+ const cand_min_along_up = Math.min(
174
+ tri_a[0] * up_x + tri_a[1] * up_y + tri_a[2] * up_z,
175
+ tri_b[0] * up_x + tri_b[1] * up_y + tri_b[2] * up_z,
176
+ tri_c[0] * up_x + tri_c[1] * up_y + tri_c[2] * up_z
177
+ );
178
+
179
+ // strictly above the face (the face's own/coplanar floor reads <= face_max_along_up)
180
+ if (cand_min_along_up > face_max_along_up + 1e-3) {
181
+ return true;
182
+ }
183
+ }
184
+
185
+ return false;
186
+ }
187
+
188
+ /**
189
+ * Carve holes into a (welded, connected) walkable topology wherever an agent of the given height
190
+ * would not fit under overhead geometry.
191
+ *
192
+ * Operates on the topology rather than a triangle soup so that all subdivision is conformal:
193
+ * {@link bt_edge_split} re-triangulates every face around a split edge, so neighbouring faces can
194
+ * never disagree on a shared edge (no cracks / T-junctions). Refinement is adaptive - only faces whose
195
+ * column actually contains an overhang are subdivided, and only down to `resolution` - so clear areas
196
+ * stay coarse and the subdivision hugs the obstacle contour instead of tessellating whole triangles.
197
+ *
198
+ * @param {object} params
199
+ * @param {BinaryTopology} params.mesh walkable topology to carve (modified in place)
200
+ * @param {BinaryTopology} params.source original source mesh (walkable + overhead geometry)
201
+ * @param {BVH} params.source_bvh BVH over `source` (leaves carry source face IDs)
202
+ * @param {number} params.agent_height
203
+ * @param {number} params.agent_radius
204
+ * @param {Vector3} params.up world up direction
205
+ */
206
+ export function bt_mesh_carve_height_clearance({
207
+ mesh,
208
+ source,
209
+ source_bvh,
210
+ agent_height,
211
+ agent_radius,
212
+ up,
213
+ }) {
214
+
215
+ if (agent_height <= 0 || source_bvh.root === NULL_NODE) {
216
+ // nothing overhead can obstruct anything
217
+ return;
218
+ }
219
+
220
+ // normalize up
221
+ let up_x = up.x, up_y = up.y, up_z = up.z;
222
+ const up_len = Math.sqrt(up_x * up_x + up_y * up_y + up_z * up_z);
223
+ if (up_len === 0) {
224
+ return;
225
+ }
226
+ up_x /= up_len; up_y /= up_len; up_z /= up_len;
227
+
228
+ // Contour resolution: how finely the obstacle outline is resolved. Tied to the agent footprint;
229
+ // floored so a tiny radius cannot trigger runaway refinement.
230
+ const resolution = Math.max(agent_radius > 0 ? agent_radius : agent_height / 4, 0.25);
231
+ const res_sq = resolution * resolution;
232
+
233
+ // ---- Phase 1: adaptive, conformal refinement around overhangs ----
234
+ // Repeated fixed-point passes: split the longest edge of any face that (a) is still larger than
235
+ // the contour resolution and (b) has overhead geometry in its column. Splitting is conformal, so
236
+ // the mesh stays crack-free throughout.
237
+ let changed = true;
238
+ let guard = 0;
239
+
240
+ while (changed && guard < 64) {
241
+ changed = false;
242
+ guard++;
243
+
244
+ const face_count = mesh.faces.size;
245
+
246
+ for (let f = 0; f < face_count; f++) {
247
+ if (!mesh.faces.is_allocated(f)) {
248
+ continue;
249
+ }
250
+
251
+ read_face_triangle(mesh, f);
252
+
253
+ // longest edge (by squared length); read_face_triangle puts va/vb/vc in loop order
254
+ // la=(a,b), lb=(b,c), lc=(c,a)
255
+ const ab = (va[0] - vb[0]) ** 2 + (va[1] - vb[1]) ** 2 + (va[2] - vb[2]) ** 2;
256
+ const bc = (vb[0] - vc[0]) ** 2 + (vb[1] - vc[1]) ** 2 + (vb[2] - vc[2]) ** 2;
257
+ const ca = (vc[0] - va[0]) ** 2 + (vc[1] - va[1]) ** 2 + (vc[2] - va[2]) ** 2;
258
+
259
+ const longest_sq = Math.max(ab, bc, ca);
260
+
261
+ if (longest_sq <= res_sq) {
262
+ // small enough to resolve the contour
263
+ continue;
264
+ }
265
+
266
+ const face_max_along_up = Math.max(
267
+ va[0] * up_x + va[1] * up_y + va[2] * up_z,
268
+ vb[0] * up_x + vb[1] * up_y + vb[2] * up_z,
269
+ vc[0] * up_x + vc[1] * up_y + vc[2] * up_z
270
+ );
271
+
272
+ if (!column_has_overhead(source_bvh, source, face_max_along_up, up_x, up_y, up_z, agent_height)) {
273
+ // fully clear column - keep this face coarse
274
+ continue;
275
+ }
276
+
277
+ const { la, lb, lc } = read_face_triangle(mesh, f);
278
+ let split_loop;
279
+ if (longest_sq === ab) {
280
+ split_loop = la;
281
+ } else if (longest_sq === bc) {
282
+ split_loop = lb;
283
+ } else {
284
+ split_loop = lc;
285
+ }
286
+
287
+ bt_edge_split(mesh, mesh.loop_read_edge(split_loop), 0.5);
288
+ changed = true;
289
+ }
290
+
291
+ if (mesh.faces.size > MAX_FACES) {
292
+ break;
293
+ }
294
+ }
295
+
296
+ // ---- Phase 2: cull blocked faces (whole-face, conformal) ----
297
+ const faces_to_kill = [];
298
+ const face_count = mesh.faces.size;
299
+
300
+ for (let f = 0; f < face_count; f++) {
301
+ if (!mesh.faces.is_allocated(f)) {
302
+ continue;
303
+ }
304
+
305
+ read_face_triangle(mesh, f);
306
+
307
+ const cx = (va[0] + vb[0] + vc[0]) / 3;
308
+ const cy = (va[1] + vb[1] + vc[1]) / 3;
309
+ const cz = (va[2] + vb[2] + vc[2]) / 3;
310
+
311
+ if (!point_has_clearance(source_bvh, source, cx, cy, cz, up_x, up_y, up_z, agent_height)) {
312
+ faces_to_kill.push(f);
313
+ }
314
+ }
315
+
316
+ for (let i = 0; i < faces_to_kill.length; i++) {
317
+ bt_face_kill(mesh, faces_to_kill[i]);
318
+ }
319
+
320
+ if (faces_to_kill.length > 0) {
321
+ bt_mesh_cleanup_faceless_references(mesh);
322
+ }
323
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"navmesh_build_topology.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/mesh/build/navmesh_build_topology.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;GAUG;AACH,wKATW,cAAc,QAoLxB;+BA/N8B,mEAAmE"}
1
+ {"version":3,"file":"navmesh_build_topology.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/mesh/build/navmesh_build_topology.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;GAUG;AACH,wKATW,cAAc,QAiLxB;+BA5N8B,mEAAmE"}