@woosh/meep-engine 2.163.2 → 2.163.4
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/equirectangular/equirectangular_direction_to_uv.d.ts +12 -0
- package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.d.ts.map +1 -0
- package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -0
- package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.d.ts +14 -0
- package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.d.ts.map +1 -0
- package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +368 -290
- package/src/core/geom/vec3/v3_uniform_sample_cone.d.ts +11 -0
- package/src/core/geom/vec3/v3_uniform_sample_cone.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -0
- package/src/core/math/physics/brdf/cone_cosine_from_roughness.d.ts +13 -0
- package/src/core/math/physics/brdf/cone_cosine_from_roughness.d.ts.map +1 -0
- package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -0
- package/src/core/math/physics/brdf/reflection_sample_weight.d.ts +18 -0
- package/src/core/math/physics/brdf/reflection_sample_weight.d.ts.map +1 -0
- package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -0
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +52 -0
- package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.d.ts +26 -0
- package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.d.ts.map +1 -0
- package/src/engine/graphics/sh3/sky/hosek/make_environment_sky_hosek.js +49 -0
- package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.d.ts +26 -0
- package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.d.ts.map +1 -0
- package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -0
- package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.d.ts +24 -0
- package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.d.ts.map +1 -0
- package/src/engine/graphics/sh3/sky/hosek/setup_environment_sky_from_ecd.js +51 -0
- package/src/engine/graphics/texture/EnvironmentTextureProjection.d.ts +9 -0
- package/src/engine/graphics/texture/EnvironmentTextureProjection.d.ts.map +1 -0
- package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -0
- package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.d.ts +44 -0
- package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.d.ts.map +1 -0
- package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -0
- package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.d.ts +25 -0
- package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.d.ts.map +1 -0
- package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -0
- package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.d.ts +15 -0
- package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -0
- package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts +28 -0
- package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.d.ts.map +1 -0
- package/src/engine/navigation/mesh/build/bt_mesh_carve_height_clearance.js +359 -0
- package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/navmesh_build_topology.js +224 -226
- package/src/engine/.fuse_hidden0000001500000001 +0 -581
|
@@ -0,0 +1,359 @@
|
|
|
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
|
+
/** Lift a ray origin off the surface so the upward clearance ray does not re-hit the floor. */
|
|
19
|
+
const SURFACE_EPSILON = 1e-4;
|
|
20
|
+
|
|
21
|
+
/** Safety cap on the face count during refinement. */
|
|
22
|
+
const MAX_FACES = 200000;
|
|
23
|
+
|
|
24
|
+
// reused scratch
|
|
25
|
+
const ray_leaf_buffer = [];
|
|
26
|
+
const intersection_result = new Float32Array(6);
|
|
27
|
+
const tri_a = new Float32Array(3);
|
|
28
|
+
const tri_b = new Float32Array(3);
|
|
29
|
+
const tri_c = new Float32Array(3);
|
|
30
|
+
|
|
31
|
+
const va = new Float32Array(3);
|
|
32
|
+
const vb = new Float32Array(3);
|
|
33
|
+
const vc = new Float32Array(3);
|
|
34
|
+
|
|
35
|
+
const overhead_hits = [];
|
|
36
|
+
const query_aabb = new Float32Array(6);
|
|
37
|
+
const scratch_normal = new Float32Array(3);
|
|
38
|
+
|
|
39
|
+
/** True if nothing in `source` sits directly above the point within `agent_height` (exact ray test). */
|
|
40
|
+
function point_has_clearance(source_bvh, source, px, py, pz, up_x, up_y, up_z, agent_height) {
|
|
41
|
+
const origin_x = px + up_x * SURFACE_EPSILON;
|
|
42
|
+
const origin_y = py + up_y * SURFACE_EPSILON;
|
|
43
|
+
const origin_z = pz + up_z * SURFACE_EPSILON;
|
|
44
|
+
|
|
45
|
+
const leaf_count = bvh_query_leaves_ray_segment(
|
|
46
|
+
source_bvh, source_bvh.root,
|
|
47
|
+
ray_leaf_buffer, 0,
|
|
48
|
+
origin_x, origin_y, origin_z,
|
|
49
|
+
up_x, up_y, up_z,
|
|
50
|
+
0, agent_height
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < leaf_count; i++) {
|
|
54
|
+
const node = ray_leaf_buffer[i];
|
|
55
|
+
const face_id = source_bvh.node_get_user_data(node);
|
|
56
|
+
|
|
57
|
+
const loop_a = source.face_read_loop(face_id);
|
|
58
|
+
if (loop_a === NULL_POINTER) continue;
|
|
59
|
+
|
|
60
|
+
// Only DOWNWARD-facing geometry (a ceiling / overhang underside) can obstruct head clearance.
|
|
61
|
+
// Up-facing walkable floors above (a higher tier, a step the agent climbs) must NOT block.
|
|
62
|
+
source.face_read_normal(scratch_normal, 0, face_id);
|
|
63
|
+
if (scratch_normal[0] * up_x + scratch_normal[1] * up_y + scratch_normal[2] * up_z >= -1e-3) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const loop_b = source.loop_read_next(loop_a);
|
|
68
|
+
const loop_c = source.loop_read_next(loop_b);
|
|
69
|
+
|
|
70
|
+
source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(loop_a));
|
|
71
|
+
source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(loop_b));
|
|
72
|
+
source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(loop_c));
|
|
73
|
+
|
|
74
|
+
const hit = computeTriangleRayIntersectionBarycentric(
|
|
75
|
+
intersection_result,
|
|
76
|
+
origin_x, origin_y, origin_z,
|
|
77
|
+
up_x, up_y, up_z,
|
|
78
|
+
tri_a[0], tri_a[1], tri_a[2],
|
|
79
|
+
tri_b[0], tri_b[1], tri_b[2],
|
|
80
|
+
tri_c[0], tri_c[1], tri_c[2]
|
|
81
|
+
);
|
|
82
|
+
if (!hit) continue;
|
|
83
|
+
|
|
84
|
+
const t = intersection_result[0];
|
|
85
|
+
if (t > 0 && t <= agent_height) return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* True if any source geometry sits above the point within `agent_height`, anywhere inside a horizontal
|
|
93
|
+
* disc of radius `r` (approximated by an axis-aligned box). Used to dilate the obstacle footprint by
|
|
94
|
+
* the agent radius - the agent's body cannot occupy a spot whose `r`-neighbourhood is overhung.
|
|
95
|
+
*/
|
|
96
|
+
function overhead_within_radius(source_bvh, source, px, py, pz, up_x, up_y, up_z, agent_height, r) {
|
|
97
|
+
const p_along_up = px * up_x + py * up_y + pz * up_z;
|
|
98
|
+
|
|
99
|
+
// expand on the axes perpendicular to up by r, then sweep up by agent_height
|
|
100
|
+
const ex = r * (1 - Math.abs(up_x));
|
|
101
|
+
const ey = r * (1 - Math.abs(up_y));
|
|
102
|
+
const ez = r * (1 - Math.abs(up_z));
|
|
103
|
+
|
|
104
|
+
let min_x = px - ex, min_y = py - ey, min_z = pz - ez;
|
|
105
|
+
let max_x = px + ex, max_y = py + ey, max_z = pz + ez;
|
|
106
|
+
|
|
107
|
+
const dx = up_x * agent_height, dy = up_y * agent_height, dz = up_z * agent_height;
|
|
108
|
+
if (dx > 0) max_x += dx; else min_x += dx;
|
|
109
|
+
if (dy > 0) max_y += dy; else min_y += dy;
|
|
110
|
+
if (dz > 0) max_z += dz; else min_z += dz;
|
|
111
|
+
|
|
112
|
+
query_aabb[0] = min_x; query_aabb[1] = min_y; query_aabb[2] = min_z;
|
|
113
|
+
query_aabb[3] = max_x; query_aabb[4] = max_y; query_aabb[5] = max_z;
|
|
114
|
+
|
|
115
|
+
const count = bvh_query_user_data_overlaps_aabb(overhead_hits, 0, source_bvh, query_aabb);
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < count; i++) {
|
|
118
|
+
const fid = overhead_hits[i];
|
|
119
|
+
const la = source.face_read_loop(fid);
|
|
120
|
+
if (la === NULL_POINTER) continue;
|
|
121
|
+
|
|
122
|
+
// only downward-facing geometry counts as an overhang (see point_has_clearance)
|
|
123
|
+
source.face_read_normal(scratch_normal, 0, fid);
|
|
124
|
+
if (scratch_normal[0] * up_x + scratch_normal[1] * up_y + scratch_normal[2] * up_z >= -1e-3) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const lb = source.loop_read_next(la);
|
|
129
|
+
const lc = source.loop_read_next(lb);
|
|
130
|
+
|
|
131
|
+
source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(la));
|
|
132
|
+
source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(lb));
|
|
133
|
+
source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(lc));
|
|
134
|
+
|
|
135
|
+
const cand_min_along_up = Math.min(
|
|
136
|
+
tri_a[0] * up_x + tri_a[1] * up_y + tri_a[2] * up_z,
|
|
137
|
+
tri_b[0] * up_x + tri_b[1] * up_y + tri_b[2] * up_z,
|
|
138
|
+
tri_c[0] * up_x + tri_c[1] * up_y + tri_c[2] * up_z
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (cand_min_along_up > p_along_up + 1e-3 && cand_min_along_up <= p_along_up + agent_height + 1e-3) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Does the column over this face's footprint (inflated horizontally by `pad`) contain any DOWNWARD-
|
|
151
|
+
* facing geometry above it within `agent_height`? Footprint-wide (not just the centroid), so an
|
|
152
|
+
* obstacle sitting anywhere under a large face is detected and the face is refined toward it.
|
|
153
|
+
*/
|
|
154
|
+
function footprint_has_overhead(source_bvh, source, up_x, up_y, up_z, agent_height, pad) {
|
|
155
|
+
const face_max_along_up = Math.max(
|
|
156
|
+
va[0] * up_x + va[1] * up_y + va[2] * up_z,
|
|
157
|
+
vb[0] * up_x + vb[1] * up_y + vb[2] * up_z,
|
|
158
|
+
vc[0] * up_x + vc[1] * up_y + vc[2] * up_z
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
let min_x = Math.min(va[0], vb[0], vc[0]) - pad;
|
|
162
|
+
let min_y = Math.min(va[1], vb[1], vc[1]) - pad;
|
|
163
|
+
let min_z = Math.min(va[2], vb[2], vc[2]) - pad;
|
|
164
|
+
let max_x = Math.max(va[0], vb[0], vc[0]) + pad;
|
|
165
|
+
let max_y = Math.max(va[1], vb[1], vc[1]) + pad;
|
|
166
|
+
let max_z = Math.max(va[2], vb[2], vc[2]) + pad;
|
|
167
|
+
|
|
168
|
+
const dx = up_x * agent_height, dy = up_y * agent_height, dz = up_z * agent_height;
|
|
169
|
+
if (dx > 0) max_x += dx; else min_x += dx;
|
|
170
|
+
if (dy > 0) max_y += dy; else min_y += dy;
|
|
171
|
+
if (dz > 0) max_z += dz; else min_z += dz;
|
|
172
|
+
|
|
173
|
+
query_aabb[0] = min_x; query_aabb[1] = min_y; query_aabb[2] = min_z;
|
|
174
|
+
query_aabb[3] = max_x; query_aabb[4] = max_y; query_aabb[5] = max_z;
|
|
175
|
+
|
|
176
|
+
const count = bvh_query_user_data_overlaps_aabb(overhead_hits, 0, source_bvh, query_aabb);
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < count; i++) {
|
|
179
|
+
const fid = overhead_hits[i];
|
|
180
|
+
const la = source.face_read_loop(fid);
|
|
181
|
+
if (la === NULL_POINTER) continue;
|
|
182
|
+
|
|
183
|
+
source.face_read_normal(scratch_normal, 0, fid);
|
|
184
|
+
if (scratch_normal[0] * up_x + scratch_normal[1] * up_y + scratch_normal[2] * up_z >= -1e-3) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const lb = source.loop_read_next(la);
|
|
189
|
+
const lc = source.loop_read_next(lb);
|
|
190
|
+
source.vertex_read_coordinate(tri_a, 0, source.loop_read_vertex(la));
|
|
191
|
+
source.vertex_read_coordinate(tri_b, 0, source.loop_read_vertex(lb));
|
|
192
|
+
source.vertex_read_coordinate(tri_c, 0, source.loop_read_vertex(lc));
|
|
193
|
+
|
|
194
|
+
const cand_min_along_up = Math.min(
|
|
195
|
+
tri_a[0] * up_x + tri_a[1] * up_y + tri_a[2] * up_z,
|
|
196
|
+
tri_b[0] * up_x + tri_b[1] * up_y + tri_b[2] * up_z,
|
|
197
|
+
tri_c[0] * up_x + tri_c[1] * up_y + tri_c[2] * up_z
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (cand_min_along_up > face_max_along_up + 1e-3) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function read_face_triangle(mesh, face_id) {
|
|
209
|
+
const la = mesh.face_read_loop(face_id);
|
|
210
|
+
const lb = mesh.loop_read_next(la);
|
|
211
|
+
const lc = mesh.loop_read_next(lb);
|
|
212
|
+
|
|
213
|
+
mesh.vertex_read_coordinate(va, 0, mesh.loop_read_vertex(la));
|
|
214
|
+
mesh.vertex_read_coordinate(vb, 0, mesh.loop_read_vertex(lb));
|
|
215
|
+
mesh.vertex_read_coordinate(vc, 0, mesh.loop_read_vertex(lc));
|
|
216
|
+
|
|
217
|
+
return { la, lb, lc };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Carve holes into a (welded) walkable topology wherever an agent of the given height+radius would not
|
|
222
|
+
* fit under overhead geometry. INTENDED TO RUN AFTER the agent-radius boundary erosion: the boundary is
|
|
223
|
+
* already handled, so this only removes the obstacle footprints (dilated by the agent radius).
|
|
224
|
+
*
|
|
225
|
+
* All subdivision is conformal ({@link bt_edge_split} re-triangulates every face around a split edge),
|
|
226
|
+
* and culling whole faces ({@link bt_face_kill}) never cracks neighbours - so this cannot disconnect a
|
|
227
|
+
* passable region. Refinement is tight: faces straddling the (dilated) obstacle outline are refined to
|
|
228
|
+
* `resolution`; a still-large all-clear face whose column has overhead is refined down to a coarse
|
|
229
|
+
* guard so a small obstacle cannot hide unsampled; everything else stays coarse.
|
|
230
|
+
*
|
|
231
|
+
* @param {object} params
|
|
232
|
+
* @param {BinaryTopology} params.mesh walkable topology to carve (modified in place)
|
|
233
|
+
* @param {BinaryTopology} params.source original source mesh (walkable + overhead geometry)
|
|
234
|
+
* @param {BVH} params.source_bvh BVH over `source`
|
|
235
|
+
* @param {number} params.agent_height
|
|
236
|
+
* @param {number} params.agent_radius
|
|
237
|
+
* @param {Vector3} params.up world up direction
|
|
238
|
+
*/
|
|
239
|
+
export function bt_mesh_carve_height_clearance({
|
|
240
|
+
mesh,
|
|
241
|
+
source,
|
|
242
|
+
source_bvh,
|
|
243
|
+
agent_height,
|
|
244
|
+
agent_radius,
|
|
245
|
+
up,
|
|
246
|
+
}) {
|
|
247
|
+
|
|
248
|
+
if (agent_height <= 0 || source_bvh.root === NULL_NODE) return;
|
|
249
|
+
|
|
250
|
+
let up_x = up.x, up_y = up.y, up_z = up.z;
|
|
251
|
+
const up_len = Math.sqrt(up_x * up_x + up_y * up_y + up_z * up_z);
|
|
252
|
+
if (up_len === 0) return;
|
|
253
|
+
up_x /= up_len; up_y /= up_len; up_z /= up_len;
|
|
254
|
+
|
|
255
|
+
const r = Math.max(agent_radius, 0);
|
|
256
|
+
const resolution = Math.max(agent_radius > 0 ? agent_radius : agent_height / 4, 0.3);
|
|
257
|
+
const coarse_guard = Math.max(2 * agent_radius, agent_height / 2, 0.8);
|
|
258
|
+
const res_sq = resolution * resolution;
|
|
259
|
+
const guard_sq = coarse_guard * coarse_guard;
|
|
260
|
+
|
|
261
|
+
// The agent fits at p iff nothing overhangs directly above it AND nothing overhangs within its body
|
|
262
|
+
// radius. (For r == 0 this is just the exact overhead ray.)
|
|
263
|
+
function agent_fits(px, py, pz) {
|
|
264
|
+
if (!point_has_clearance(source_bvh, source, px, py, pz, up_x, up_y, up_z, agent_height)) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
if (r > 0 && overhead_within_radius(source_bvh, source, px, py, pz, up_x, up_y, up_z, agent_height, r)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---- Phase 1: tight, conformal refinement around the (dilated) obstacle outline ----
|
|
274
|
+
let changed = true;
|
|
275
|
+
let guard = 0;
|
|
276
|
+
|
|
277
|
+
while (changed && guard < 64) {
|
|
278
|
+
changed = false;
|
|
279
|
+
guard++;
|
|
280
|
+
|
|
281
|
+
const face_count = mesh.faces.size;
|
|
282
|
+
|
|
283
|
+
for (let f = 0; f < face_count; f++) {
|
|
284
|
+
if (!mesh.faces.is_allocated(f)) continue;
|
|
285
|
+
|
|
286
|
+
read_face_triangle(mesh, f);
|
|
287
|
+
|
|
288
|
+
const ab = (va[0] - vb[0]) ** 2 + (va[1] - vb[1]) ** 2 + (va[2] - vb[2]) ** 2;
|
|
289
|
+
const bc = (vb[0] - vc[0]) ** 2 + (vb[1] - vc[1]) ** 2 + (vb[2] - vc[2]) ** 2;
|
|
290
|
+
const ca = (vc[0] - va[0]) ** 2 + (vc[1] - va[1]) ** 2 + (vc[2] - va[2]) ** 2;
|
|
291
|
+
const longest_sq = Math.max(ab, bc, ca);
|
|
292
|
+
|
|
293
|
+
if (longest_sq <= res_sq) continue;
|
|
294
|
+
|
|
295
|
+
const mab_x = (va[0] + vb[0]) * 0.5, mab_y = (va[1] + vb[1]) * 0.5, mab_z = (va[2] + vb[2]) * 0.5;
|
|
296
|
+
const mbc_x = (vb[0] + vc[0]) * 0.5, mbc_y = (vb[1] + vc[1]) * 0.5, mbc_z = (vb[2] + vc[2]) * 0.5;
|
|
297
|
+
const mca_x = (vc[0] + va[0]) * 0.5, mca_y = (vc[1] + va[1]) * 0.5, mca_z = (vc[2] + va[2]) * 0.5;
|
|
298
|
+
const cx = (va[0] + vb[0] + vc[0]) / 3, cy = (va[1] + vb[1] + vc[1]) / 3, cz = (va[2] + vb[2] + vc[2]) / 3;
|
|
299
|
+
|
|
300
|
+
const samples = [
|
|
301
|
+
va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], vc[2],
|
|
302
|
+
mab_x, mab_y, mab_z, mbc_x, mbc_y, mbc_z, mca_x, mca_y, mca_z,
|
|
303
|
+
cx, cy, cz,
|
|
304
|
+
];
|
|
305
|
+
let n_clear = 0, n_block = 0;
|
|
306
|
+
for (let s = 0; s < samples.length; s += 3) {
|
|
307
|
+
if (agent_fits(samples[s], samples[s + 1], samples[s + 2])) n_clear++; else n_block++;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let refine = false;
|
|
311
|
+
if (n_block > 0 && n_clear > 0) {
|
|
312
|
+
refine = true; // (dilated) outline crosses this face -> hug it at `resolution`
|
|
313
|
+
} else if (n_block === 0 && longest_sq > guard_sq) {
|
|
314
|
+
// all clear by sampling, but still large enough to hide an obstacle between samples:
|
|
315
|
+
// refine if any downward-facing overhang sits under the whole face footprint (+ radius)
|
|
316
|
+
if (footprint_has_overhead(source_bvh, source, up_x, up_y, up_z, agent_height, r)) {
|
|
317
|
+
refine = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// n_clear === 0 (fully blocked): leave it for the cull phase
|
|
321
|
+
|
|
322
|
+
if (!refine) continue;
|
|
323
|
+
|
|
324
|
+
const { la, lb, lc } = read_face_triangle(mesh, f);
|
|
325
|
+
let split_loop;
|
|
326
|
+
if (longest_sq === ab) split_loop = la;
|
|
327
|
+
else if (longest_sq === bc) split_loop = lb;
|
|
328
|
+
else split_loop = lc;
|
|
329
|
+
|
|
330
|
+
bt_edge_split(mesh, mesh.loop_read_edge(split_loop), 0.5);
|
|
331
|
+
changed = true;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (mesh.faces.size > MAX_FACES) break;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---- Phase 2: cull blocked faces (whole-face, conformal) ----
|
|
338
|
+
const faces_to_kill = [];
|
|
339
|
+
const face_count = mesh.faces.size;
|
|
340
|
+
|
|
341
|
+
for (let f = 0; f < face_count; f++) {
|
|
342
|
+
if (!mesh.faces.is_allocated(f)) continue;
|
|
343
|
+
|
|
344
|
+
read_face_triangle(mesh, f);
|
|
345
|
+
const cx = (va[0] + vb[0] + vc[0]) / 3;
|
|
346
|
+
const cy = (va[1] + vb[1] + vc[1]) / 3;
|
|
347
|
+
const cz = (va[2] + vb[2] + vc[2]) / 3;
|
|
348
|
+
|
|
349
|
+
if (!agent_fits(cx, cy, cz)) faces_to_kill.push(f);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < faces_to_kill.length; i++) {
|
|
353
|
+
bt_face_kill(mesh, faces_to_kill[i]);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (faces_to_kill.length > 0) {
|
|
357
|
+
bt_mesh_cleanup_faceless_references(mesh);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -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,
|
|
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,QAkLxB;+BA7N8B,mEAAmE"}
|