@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
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Pure JavaScript game engine. Fully featured and production ready.",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"author": "Alexander Goldring",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.150.0",
|
|
10
10
|
"main": "build/meep.module.js",
|
|
11
11
|
"module": "build/meep.module.js",
|
|
12
12
|
"exports": {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect islands that are not topologically joined but lie within a small step (vertical) and/or gap
|
|
3
|
+
* (lateral) of one another, so an agent can path across stairs and small breaks in the floor.
|
|
4
|
+
*
|
|
5
|
+
* The two unused params on the build path (`agent_max_step_height`, `agent_max_step_distance`) drive this.
|
|
6
|
+
* Boundary edges of distinct islands whose endpoints can be paired within the thresholds are stitched
|
|
7
|
+
* with a bridge quad (two triangles). Connectivity is realised by rebuilding the mesh from its triangle
|
|
8
|
+
* soup plus the bridge quads and re-running the same vertex-merge / edge-fuse the build already uses -
|
|
9
|
+
* the bridge corners are copied verbatim from existing boundary vertices, so they fuse onto the islands.
|
|
10
|
+
*
|
|
11
|
+
* Mutates `mesh` in place. Triangulated input is assumed (as produced by the navmesh build pipeline).
|
|
12
|
+
*
|
|
13
|
+
* @param {BinaryTopology} mesh
|
|
14
|
+
* @param {{max_step_height:number, max_step_distance:number, up:Vector3}} options
|
|
15
|
+
* @returns {number} number of bridges added
|
|
16
|
+
*/
|
|
17
|
+
export function bt_mesh_bridge_islands(mesh: BinaryTopology, { max_step_height, max_step_distance, up }: {
|
|
18
|
+
max_step_height: number;
|
|
19
|
+
max_step_distance: number;
|
|
20
|
+
up: Vector3;
|
|
21
|
+
}): number;
|
|
22
|
+
import { BinaryTopology } from "../BinaryTopology.js";
|
|
23
|
+
//# sourceMappingURL=bt_mesh_bridge_islands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bt_mesh_bridge_islands.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.js"],"names":[],"mappings":"AAoHA;;;;;;;;;;;;;;;GAeG;AACH,6CAJW,cAAc,8CACd;IAAC,eAAe,EAAC,MAAM,CAAC;IAAC,iBAAiB,EAAC,MAAM,CAAC;IAAC,EAAE,UAAQ;CAAC,GAC5D,MAAM,CAoKlB;+BAtS8B,sBAAsB"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { BinaryTopology } from "../BinaryTopology.js";
|
|
2
|
+
import { bt_mesh_compute_face_islands } from "../query/bt_mesh_compute_face_islands.js";
|
|
3
|
+
import { bt_query_edge_is_boundary } from "../query/bt_query_edge_is_boundary.js";
|
|
4
|
+
import { bt_mesh_from_unindexed_geometry } from "./bt_mesh_from_unindexed_geometry.js";
|
|
5
|
+
import { bt_mesh_fuse_duplicate_edges } from "./edge/bt_mesh_fuse_duplicate_edges.js";
|
|
6
|
+
import { bt_merge_verts_by_distance } from "./vertex/bt_merge_verts_by_distance.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Distance under which rebuilt vertices are fused, matching the navmesh build pipeline. Bridge corner
|
|
10
|
+
* coordinates are copied verbatim from existing boundary vertices, so they fuse exactly.
|
|
11
|
+
* @type {number}
|
|
12
|
+
*/
|
|
13
|
+
const VERTEX_MERGE_DISTANCE = 1e-6;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tolerance added to the step thresholds so exactly-on-the-limit connections (e.g. a perfectly aligned
|
|
17
|
+
* step with zero lateral gap, tested against max_step_distance = 0) are accepted.
|
|
18
|
+
* @type {number}
|
|
19
|
+
*/
|
|
20
|
+
const THRESHOLD_EPSILON = 1e-6;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Decompose the vector (px,py,pz) - (qx,qy,qz) into components along and perpendicular to `up` and,
|
|
24
|
+
* if it is within the step thresholds, return the perpendicular (lateral) distance; otherwise -1.
|
|
25
|
+
*/
|
|
26
|
+
function connection_lateral_distance(
|
|
27
|
+
px, py, pz,
|
|
28
|
+
qx, qy, qz,
|
|
29
|
+
up_x, up_y, up_z,
|
|
30
|
+
max_step_height, max_step_distance
|
|
31
|
+
) {
|
|
32
|
+
const dx = px - qx;
|
|
33
|
+
const dy = py - qy;
|
|
34
|
+
const dz = pz - qz;
|
|
35
|
+
|
|
36
|
+
const along_up = dx * up_x + dy * up_y + dz * up_z;
|
|
37
|
+
const vertical = Math.abs(along_up);
|
|
38
|
+
const lateral = Math.sqrt(Math.max(0, (dx * dx + dy * dy + dz * dz) - along_up * along_up));
|
|
39
|
+
|
|
40
|
+
if (vertical <= max_step_height + THRESHOLD_EPSILON && lateral <= max_step_distance + THRESHOLD_EPSILON) {
|
|
41
|
+
return lateral;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return -1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Evaluate whether two boundary edges can be bridged and, if so, which endpoint pairing to use.
|
|
49
|
+
* Writes the cheaper valid pairing into `out_best` as `[pairing, cost]` (pairing 1 = a1<->b1 / a2<->b2,
|
|
50
|
+
* pairing 2 = a1<->b2 / a2<->b1) and returns true; returns false (leaving `out_best` untouched) when
|
|
51
|
+
* neither pairing fits the thresholds. `out_best` is supplied by the caller to avoid per-pair allocation.
|
|
52
|
+
*
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
function evaluate_pair(a, b, up_x, up_y, up_z, max_step_height, max_step_distance, out_best) {
|
|
56
|
+
// pairing 1: a.p1 <-> b.p1, a.p2 <-> b.p2
|
|
57
|
+
const c1_1 = connection_lateral_distance(b.x1, b.y1, b.z1, a.x1, a.y1, a.z1, up_x, up_y, up_z, max_step_height, max_step_distance);
|
|
58
|
+
const c1_2 = connection_lateral_distance(b.x2, b.y2, b.z2, a.x2, a.y2, a.z2, up_x, up_y, up_z, max_step_height, max_step_distance);
|
|
59
|
+
|
|
60
|
+
// pairing 2: a.p1 <-> b.p2, a.p2 <-> b.p1
|
|
61
|
+
const c2_1 = connection_lateral_distance(b.x2, b.y2, b.z2, a.x1, a.y1, a.z1, up_x, up_y, up_z, max_step_height, max_step_distance);
|
|
62
|
+
const c2_2 = connection_lateral_distance(b.x1, b.y1, b.z1, a.x2, a.y2, a.z2, up_x, up_y, up_z, max_step_height, max_step_distance);
|
|
63
|
+
|
|
64
|
+
let found = false;
|
|
65
|
+
let pairing = 0;
|
|
66
|
+
let cost = Infinity;
|
|
67
|
+
|
|
68
|
+
if (c1_1 >= 0 && c1_2 >= 0) {
|
|
69
|
+
pairing = 1;
|
|
70
|
+
cost = c1_1 + c1_2;
|
|
71
|
+
found = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (c2_1 >= 0 && c2_2 >= 0) {
|
|
75
|
+
const cost_2 = c2_1 + c2_2;
|
|
76
|
+
if (!found || cost_2 < cost) {
|
|
77
|
+
pairing = 2;
|
|
78
|
+
cost = cost_2;
|
|
79
|
+
found = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (found) {
|
|
84
|
+
out_best[0] = pairing;
|
|
85
|
+
out_best[1] = cost;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return found;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Append a triangle to the soup, choosing the winding so its normal points along `up` (keeps bridge
|
|
93
|
+
* faces walkable / consistently oriented with the floor islands they connect).
|
|
94
|
+
*/
|
|
95
|
+
function push_triangle_facing_up(
|
|
96
|
+
soup,
|
|
97
|
+
p0x, p0y, p0z,
|
|
98
|
+
p1x, p1y, p1z,
|
|
99
|
+
p2x, p2y, p2z,
|
|
100
|
+
up_x, up_y, up_z
|
|
101
|
+
) {
|
|
102
|
+
const e1x = p1x - p0x, e1y = p1y - p0y, e1z = p1z - p0z;
|
|
103
|
+
const e2x = p2x - p0x, e2y = p2y - p0y, e2z = p2z - p0z;
|
|
104
|
+
|
|
105
|
+
const nx = e1y * e2z - e1z * e2y;
|
|
106
|
+
const ny = e1z * e2x - e1x * e2z;
|
|
107
|
+
const nz = e1x * e2y - e1y * e2x;
|
|
108
|
+
|
|
109
|
+
if (nx * up_x + ny * up_y + nz * up_z >= 0) {
|
|
110
|
+
soup.push(p0x, p0y, p0z, p1x, p1y, p1z, p2x, p2y, p2z);
|
|
111
|
+
} else {
|
|
112
|
+
// flip winding so the normal faces up (preserves the undirected edges, only reverses direction)
|
|
113
|
+
soup.push(p0x, p0y, p0z, p2x, p2y, p2z, p1x, p1y, p1z);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Connect islands that are not topologically joined but lie within a small step (vertical) and/or gap
|
|
119
|
+
* (lateral) of one another, so an agent can path across stairs and small breaks in the floor.
|
|
120
|
+
*
|
|
121
|
+
* The two unused params on the build path (`agent_max_step_height`, `agent_max_step_distance`) drive this.
|
|
122
|
+
* Boundary edges of distinct islands whose endpoints can be paired within the thresholds are stitched
|
|
123
|
+
* with a bridge quad (two triangles). Connectivity is realised by rebuilding the mesh from its triangle
|
|
124
|
+
* soup plus the bridge quads and re-running the same vertex-merge / edge-fuse the build already uses -
|
|
125
|
+
* the bridge corners are copied verbatim from existing boundary vertices, so they fuse onto the islands.
|
|
126
|
+
*
|
|
127
|
+
* Mutates `mesh` in place. Triangulated input is assumed (as produced by the navmesh build pipeline).
|
|
128
|
+
*
|
|
129
|
+
* @param {BinaryTopology} mesh
|
|
130
|
+
* @param {{max_step_height:number, max_step_distance:number, up:Vector3}} options
|
|
131
|
+
* @returns {number} number of bridges added
|
|
132
|
+
*/
|
|
133
|
+
export function bt_mesh_bridge_islands(mesh, { max_step_height, max_step_distance, up }) {
|
|
134
|
+
const islands = bt_mesh_compute_face_islands(mesh);
|
|
135
|
+
|
|
136
|
+
if (islands.length < 2) {
|
|
137
|
+
// a single (or empty) connected component - nothing to bridge
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// normalize up so the along/perpendicular split uses world distances
|
|
142
|
+
let up_x = up.x;
|
|
143
|
+
let up_y = up.y;
|
|
144
|
+
let up_z = up.z;
|
|
145
|
+
|
|
146
|
+
const up_length = Math.sqrt(up_x * up_x + up_y * up_y + up_z * up_z);
|
|
147
|
+
up_x /= up_length;
|
|
148
|
+
up_y /= up_length;
|
|
149
|
+
up_z /= up_length;
|
|
150
|
+
|
|
151
|
+
// face -> island index
|
|
152
|
+
const island_of_face = new Map();
|
|
153
|
+
for (let i = 0; i < islands.length; i++) {
|
|
154
|
+
const faces = islands[i];
|
|
155
|
+
for (let k = 0; k < faces.length; k++) {
|
|
156
|
+
island_of_face.set(faces[k], i);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// collect boundary edges, tagged with their island and endpoint coordinates
|
|
161
|
+
const boundary = [];
|
|
162
|
+
const edge_count = mesh.edges.size;
|
|
163
|
+
const coord_1 = [0, 0, 0];
|
|
164
|
+
const coord_2 = [0, 0, 0];
|
|
165
|
+
|
|
166
|
+
for (let e = 0; e < edge_count; e++) {
|
|
167
|
+
if (!mesh.edges.is_allocated(e)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!bt_query_edge_is_boundary(mesh, e)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const loop = mesh.edge_read_loop(e);
|
|
176
|
+
const face = mesh.loop_read_face(loop);
|
|
177
|
+
const island = island_of_face.get(face);
|
|
178
|
+
|
|
179
|
+
mesh.vertex_read_coordinate(coord_1, 0, mesh.edge_read_vertex1(e));
|
|
180
|
+
mesh.vertex_read_coordinate(coord_2, 0, mesh.edge_read_vertex2(e));
|
|
181
|
+
|
|
182
|
+
boundary.push({
|
|
183
|
+
island,
|
|
184
|
+
x1: coord_1[0], y1: coord_1[1], z1: coord_1[2],
|
|
185
|
+
x2: coord_2[0], y2: coord_2[1], z2: coord_2[2],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// for each boundary edge, find its cheapest bridgeable partner in another island
|
|
190
|
+
const bridges = [];
|
|
191
|
+
const bridged_pairs = new Set();
|
|
192
|
+
|
|
193
|
+
// reused [pairing, cost] scratch, written by evaluate_pair, so the inner loop allocates nothing
|
|
194
|
+
const pair_result = [0, 0];
|
|
195
|
+
|
|
196
|
+
for (let i = 0; i < boundary.length; i++) {
|
|
197
|
+
let best_j = -1;
|
|
198
|
+
let best_cost = Infinity;
|
|
199
|
+
let best_pairing = 0;
|
|
200
|
+
|
|
201
|
+
for (let j = 0; j < boundary.length; j++) {
|
|
202
|
+
if (boundary[j].island === boundary[i].island) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (evaluate_pair(boundary[i], boundary[j], up_x, up_y, up_z, max_step_height, max_step_distance, pair_result)) {
|
|
207
|
+
const cost = pair_result[1];
|
|
208
|
+
|
|
209
|
+
if (cost < best_cost) {
|
|
210
|
+
best_cost = cost;
|
|
211
|
+
best_j = j;
|
|
212
|
+
best_pairing = pair_result[0];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (best_j === -1) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const key = i < best_j ? `${i}_${best_j}` : `${best_j}_${i}`;
|
|
222
|
+
if (bridged_pairs.has(key)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
bridged_pairs.add(key);
|
|
226
|
+
|
|
227
|
+
bridges.push({ a: boundary[i], b: boundary[best_j], pairing: best_pairing });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (bridges.length === 0) {
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// extract the current mesh as a triangle soup
|
|
235
|
+
const soup = [];
|
|
236
|
+
const face_count = mesh.faces.size;
|
|
237
|
+
const coord = [0, 0, 0];
|
|
238
|
+
|
|
239
|
+
for (let f = 0; f < face_count; f++) {
|
|
240
|
+
if (!mesh.faces.is_allocated(f)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const loop_start = mesh.face_read_loop(f);
|
|
245
|
+
let loop = loop_start;
|
|
246
|
+
|
|
247
|
+
do {
|
|
248
|
+
mesh.vertex_read_coordinate(coord, 0, mesh.loop_read_vertex(loop));
|
|
249
|
+
soup.push(coord[0], coord[1], coord[2]);
|
|
250
|
+
loop = mesh.loop_read_next(loop);
|
|
251
|
+
} while (loop !== loop_start);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// append the bridge quads (two triangles each)
|
|
255
|
+
for (let i = 0; i < bridges.length; i++) {
|
|
256
|
+
const { a, b, pairing } = bridges[i];
|
|
257
|
+
|
|
258
|
+
// corner0/corner1 are A's boundary edge; corner2/corner3 are B's, ordered so that the quad's
|
|
259
|
+
// first triangle carries A's edge and the second carries B's edge (so both fuse onto their island)
|
|
260
|
+
const b2x = pairing === 1 ? b.x2 : b.x1;
|
|
261
|
+
const b2y = pairing === 1 ? b.y2 : b.y1;
|
|
262
|
+
const b2z = pairing === 1 ? b.z2 : b.z1;
|
|
263
|
+
|
|
264
|
+
const b3x = pairing === 1 ? b.x1 : b.x2;
|
|
265
|
+
const b3y = pairing === 1 ? b.y1 : b.y2;
|
|
266
|
+
const b3z = pairing === 1 ? b.z1 : b.z2;
|
|
267
|
+
|
|
268
|
+
push_triangle_facing_up(
|
|
269
|
+
soup,
|
|
270
|
+
a.x1, a.y1, a.z1,
|
|
271
|
+
a.x2, a.y2, a.z2,
|
|
272
|
+
b2x, b2y, b2z,
|
|
273
|
+
up_x, up_y, up_z
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
push_triangle_facing_up(
|
|
277
|
+
soup,
|
|
278
|
+
a.x1, a.y1, a.z1,
|
|
279
|
+
b2x, b2y, b2z,
|
|
280
|
+
b3x, b3y, b3z,
|
|
281
|
+
up_x, up_y, up_z
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// rebuild topology from the augmented soup; the same merge/fuse the initial build uses re-establishes
|
|
286
|
+
// connectivity, joining the bridge triangles onto the island boundaries whose vertices they share
|
|
287
|
+
const rebuilt = new BinaryTopology();
|
|
288
|
+
bt_mesh_from_unindexed_geometry(rebuilt, soup);
|
|
289
|
+
bt_merge_verts_by_distance(rebuilt, VERTEX_MERGE_DISTANCE);
|
|
290
|
+
bt_mesh_fuse_duplicate_edges(rebuilt);
|
|
291
|
+
|
|
292
|
+
mesh.copy(rebuilt);
|
|
293
|
+
|
|
294
|
+
return bridges.length;
|
|
295
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphicsEngine.d.ts","sourceRoot":"","sources":["../../../../src/engine/graphics/GraphicsEngine.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GraphicsEngine.d.ts","sourceRoot":"","sources":["../../../../src/engine/graphics/GraphicsEngine.js"],"names":[],"mappings":"AA4DA;IAOI;;;;;OAKG;IACH,+BAJW,MAAM,EAgLhB;IAtLD,gCAEC;IAgBG;;;;OAIG;IACH,2BAA+C;IAK/C;;;;;QAOI;;WAEG;;QAEH;;WAEG;;;;;MAMN;IAED;;OAEG;IACH,YAFU,OAAO,CAEe;IAEhC;;;;;;OAMG;IACH,4BAFU,OAAO,CAEyB;IAE1C;;;OAGG;IACH,QAFU,kBAAkB,CAEU;IAWtC;;;OAGG;IACH,aAAkB;IAGlB;;;OAGG;IACH,cAFU,KAAK,CAEgB;IAK/B;;;OAGG;IACH,QAFU,MAAM,CAEI;IAEpB;;;OAGG;IACH,UAFU,aAAa,CAEH;IAGpB,+BAA0C;IAU1C;;OAEG;IACH,eAA+B;IAI/B;;;OAGG;IACH,uBAFU,kBAAkB,CAEgB;IAE5C;;;OAGG;IACH,oBAFU,iBAAiB,CAEsB;IAEjD;;;OAGG;IACH,gBAFU,iBAAiB,CAES;IAEpC;;;OAGG;IACH,sBAAoC;IAGpC;;;;OAIG;IACH,UAFU,OAAO,CAEG;IAEpB;;;OAGG;IACH,UAFU,OAAO,CAEG;IAEpB;;;OAGG;IACH,YAFU,MAAM,CAEG;IACnB,kKAcI;IAGR;;;OAGG;IACH,sBAFa,eAAe,CAI3B;IAED;;;OAGG;IACH,eAFa,aAAa,CAIzB;IAED,mBA8BC;IAED;;;;OAIG;IACH,iCAEC;IAED;;;OAGG;IACH,0BAFa,MAAM,CAIlB;IAED,+BAcC;IAED,cAgEC;IAzBsB,8BAA0C;IA2BjE;;;OAGG;IACH,cAFa,MAAM,CAYlB;IAED;;OAEG;IACH,aAQC;IAED;;OAEG;IACH,yBAQC;IAED;;;;;;OAMG;IACH,yBALW,MAAM,KACN,MAAM,6CAMhB;IAED;;;;OAIG;IACH,8BAHW,OAAO,UAAQ,UACf,OAAO,UAAQ,QAczB;IAED;;;;;OAKG;IACH,8BAQC;IAED,0BAEC;IAED;;;OAGG;IACH,4BAFW,cAAc,QA8BxB;IAED;;OAEG;IACH,qBAqBC;IAED;;OAEG;IACH,0BAeC;IAED;;OAEG;IACH,eA8CC;;CACJ;mBA5lBkB,oCAAoC;oBACnC,4BAA4B;wBACxB,4BAA4B;mCAajB,uCAAuC;sBAlBnE,OAAO;uBAaS,wBAAwB;8BAbxC,OAAO;4BAYc,+BAA+B;mCAKxB,uCAAuC;kCAKxC,gCAAgC;kCADhC,oCAAoC;gCAPtC,uCAAuC;+BAKxC,4BAA4B"}
|
|
@@ -12,6 +12,7 @@ import { assert } from "../../core/assert.js";
|
|
|
12
12
|
|
|
13
13
|
import Signal from "../../core/events/signal/Signal.js";
|
|
14
14
|
import Vector1 from "../../core/geom/Vector1.js";
|
|
15
|
+
import { Vector2 } from "../../core/geom/Vector2.js";
|
|
15
16
|
import { max2 } from "../../core/math/max2.js";
|
|
16
17
|
import EmptyView from "../../view/elements/EmptyView.js";
|
|
17
18
|
import { globalMetrics } from "../metrics/GlobalMetrics.js";
|
|
@@ -114,6 +115,15 @@ export class GraphicsEngine {
|
|
|
114
115
|
*/
|
|
115
116
|
this.pixelRatio = new Vector1(1);
|
|
116
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Final render-target resolution, in device pixels: `viewport.size × pixelRatio × devicePixelRatio`.
|
|
120
|
+
* Kept in sync by {@link GraphicsEngine#updateSize}. Subscribe to `output_resolution.onChanged` to
|
|
121
|
+
* react to any change in the actual rendered resolution (viewport resize, supersample, or DPR change).
|
|
122
|
+
* @readonly
|
|
123
|
+
* @type {Vector2}
|
|
124
|
+
*/
|
|
125
|
+
this.output_resolution = new Vector2(0, 0);
|
|
126
|
+
|
|
117
127
|
/**
|
|
118
128
|
*
|
|
119
129
|
* @type {RenderLayerManager}
|
|
@@ -277,19 +287,19 @@ export class GraphicsEngine {
|
|
|
277
287
|
|
|
278
288
|
this.layerComposer.setSize(_w, _h);
|
|
279
289
|
this.frameBuffers.setSize(_w, _h);
|
|
290
|
+
|
|
291
|
+
// publish the final device-pixel render resolution; `_w/_h` already fold in the engine pixelRatio,
|
|
292
|
+
// so this matches the framebuffers/composit layers and what getResolution historically computed
|
|
293
|
+
this.output_resolution.set(_w * devicePixelRatio, _h * devicePixelRatio);
|
|
280
294
|
}
|
|
281
295
|
|
|
282
296
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
297
|
+
* @deprecated Read {@link GraphicsEngine#output_resolution} directly (and subscribe to its
|
|
298
|
+
* `onChanged` signal) instead of polling through this method.
|
|
299
|
+
* @param {Vector2|{copy:function(v:Vector2)}} target
|
|
285
300
|
*/
|
|
286
301
|
getResolution(target) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
target.set(
|
|
290
|
-
this.viewport.size.x * ar,
|
|
291
|
-
this.viewport.size.y * ar
|
|
292
|
-
);
|
|
302
|
+
target.copy(this.output_resolution);
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
/**
|
package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AmbientOcclusionPostProcessEffect.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js"],"names":[],"mappings":"AAoBA;IAIQ,WAA0C;IAI1C,2BAQE;IAyBF;;;;OAIG;IACH,sBAA0B;IAE1B;;;;OAIG;IACH,2BAA6B;IAG7B;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,wBAA2B;IAE3B,8CAUE;IAEF;;;;OAIG;IACH,uBAAwB;IAExB;;;;;OAKG;IACH,2BASE;IAKF;;;;OAIG;IACH,0BAAwD;IAK5D;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,wBAEC;IAED;;;;OAIG;IACH,oBAeC;IAED,iBAEC;IAED,2BAoCC;IAED,uBASC;IAED;;;OAGG;IACH,iBAwBC;IAED;;;OAGG;IACH,4BAFW,OAAO,QAiCjB;IAED;;;;OAIG;IACH,gCASC;IAED;;;;OAIG;IACH,mCAUC;IAGD,
|
|
1
|
+
{"version":3,"file":"AmbientOcclusionPostProcessEffect.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js"],"names":[],"mappings":"AAoBA;IAIQ,WAA0C;IAI1C,2BAQE;IAyBF;;;;OAIG;IACH,sBAA0B;IAE1B;;;;OAIG;IACH,2BAA6B;IAG7B;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,wBAA2B;IAE3B,8CAUE;IAEF;;;;OAIG;IACH,uBAAwB;IAExB;;;;;OAKG;IACH,2BASE;IAKF;;;;OAIG;IACH,0BAAwD;IAK5D;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,wBAEC;IAED;;;;OAIG;IACH,oBAeC;IAED,iBAEC;IAED,2BAoCC;IAED,uBASC;IAED;;;OAGG;IACH,iBAwBC;IAED;;;OAGG;IACH,4BAFW,OAAO,QAiCjB;IAED;;;;OAIG;IACH,gCASC;IAED;;;;OAIG;IACH,mCAUC;IAGD,oCAiBC;IAED,wBA2CC;IAED,yBAiBC;CAEJ;6BApZ4B,uCAAuC;+BAD7D,OAAO;6CAAP,OAAO"}
|
|
@@ -187,8 +187,8 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
187
187
|
|
|
188
188
|
const camera = this.__render_camera;
|
|
189
189
|
|
|
190
|
-
// AO renders at half-res but samples the full-res depth buffer, so `size` is the full (depth)
|
|
191
|
-
// resolution -- all texel math then lands on depth texel centres rather than texel boundaries
|
|
190
|
+
// AO renders at half-res but samples the full-res depth buffer, so `size` is the full (depth)
|
|
191
|
+
// resolution -- all texel math then lands on depth texel centres rather than texel boundaries
|
|
192
192
|
uniforms.size.value.set(this.__composit_layer.renderTarget.width, this.__composit_layer.renderTarget.height);
|
|
193
193
|
|
|
194
194
|
// setup camera
|
|
@@ -333,7 +333,10 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
333
333
|
const engine = this.engine;
|
|
334
334
|
const graphics = engine.graphics;
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
// scale relative to the actual render resolution (device pixels), the same basis as the `size`
|
|
337
|
+
// uniform and the full-res depth buffer the AO samples -- so the half-res grid stays a clean
|
|
338
|
+
// fraction of the full-res grid at any devicePixelRatio / supersample factor
|
|
339
|
+
const size = graphics.output_resolution;
|
|
337
340
|
|
|
338
341
|
// clamp to 1: a 0-sized attachment trips GL_INVALID_FRAMEBUFFER_OPERATION on clear/draw
|
|
339
342
|
const target_resolution_x = Math.max(1, Math.ceil(size.x * this.__resolution_scale));
|
|
@@ -378,7 +381,7 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
378
381
|
}
|
|
379
382
|
|
|
380
383
|
this.__composit_layer.on.preRender.add(this.__render, this);
|
|
381
|
-
graphics.
|
|
384
|
+
graphics.output_resolution.onChanged.add(this.__update_render_target_size, this);
|
|
382
385
|
|
|
383
386
|
this.__update_render_target_size();
|
|
384
387
|
|
|
@@ -399,7 +402,7 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
399
402
|
graphics.layerComposer.remove(this.__composit_layer);
|
|
400
403
|
|
|
401
404
|
this.__composit_layer.on.preRender.remove(this.__render, this);
|
|
402
|
-
graphics.
|
|
405
|
+
graphics.output_resolution.onChanged.remove(this.__update_render_target_size, this);
|
|
403
406
|
|
|
404
407
|
// release memory
|
|
405
408
|
this.__upscale_material.dispose();
|
|
@@ -42,7 +42,9 @@ const SAOShader = {
|
|
|
42
42
|
// which a tapped surface counts as an occluder
|
|
43
43
|
'kernelRadius': { value: 0.5 }
|
|
44
44
|
},
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
//language=GLSL
|
|
47
|
+
vertexShader:`
|
|
46
48
|
|
|
47
49
|
varying vec2 vUv;
|
|
48
50
|
|
|
@@ -51,7 +53,8 @@ const SAOShader = {
|
|
|
51
53
|
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
52
54
|
}`,
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
//language=GLSL
|
|
57
|
+
fragmentShader: `
|
|
55
58
|
|
|
56
59
|
#include <common>
|
|
57
60
|
|
|
@@ -298,19 +301,24 @@ const SAOShader = {
|
|
|
298
301
|
|
|
299
302
|
float sceneViewZ = getViewZ( sampleDepth );
|
|
300
303
|
|
|
301
|
-
// the tapped surface must lie in front of (closer than) the probe to occlude it; if the
|
|
302
|
-
// probe is in front of the surface it sits in open space and nothing is occluded
|
|
303
|
-
// (view Z grows more negative away from the camera)
|
|
304
|
-
if ( samplePositionVS.z >= sceneViewZ ) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
304
|
// ...and the tapped surface must actually be within the sampling radius, otherwise we
|
|
309
305
|
// have hit distant geometry through the depth buffer that does not occlude this point
|
|
310
306
|
vec3 occluderVS = getViewPosition( sampleUv, sampleDepth, sceneViewZ );
|
|
311
|
-
|
|
307
|
+
vec3 to_hit = occluderVS - centerViewPosition;
|
|
308
|
+
|
|
309
|
+
float distance_to_hit = length( to_hit );
|
|
310
|
+
|
|
311
|
+
if ( distance_to_hit > kernelRadius ) {
|
|
312
312
|
continue;
|
|
313
313
|
}
|
|
314
|
+
|
|
315
|
+
vec3 direction_to_hit = normalize(occluderVS - sampleOrigin);
|
|
316
|
+
|
|
317
|
+
if( dot( direction_to_hit, centerViewNormal) < 0.01 ){
|
|
318
|
+
// unable to occlude
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
314
322
|
|
|
315
323
|
// occluded: add this sample's weight to the numerator
|
|
316
324
|
occlusion += weight;
|
|
@@ -10,13 +10,17 @@ export class NavigationMesh {
|
|
|
10
10
|
* @param {BinaryTopology} source
|
|
11
11
|
* @param {number} [agent_radius]
|
|
12
12
|
* @param {number} [agent_height]
|
|
13
|
+
* @param {number} [agent_max_step_height] agent can bridge vertical gaps in topology, such as stepping up a stair
|
|
14
|
+
* @param {number} [agent_max_step_distance] agent can bridge lateral gaps in topology, such as stepping over a hole in the floor
|
|
13
15
|
* @param {number} [agent_max_climb_angle] In radians, how steep of an angle can the agent go up by
|
|
14
16
|
* @param {Vector3} [up] Defines world's "UP" direction, this is what the agent will respect for climbing constraint
|
|
15
17
|
*/
|
|
16
|
-
build({ source, agent_radius, agent_height, agent_max_climb_angle, up, }: BinaryTopology): void;
|
|
18
|
+
build({ source, agent_radius, agent_height, agent_max_step_height, agent_max_step_distance, agent_max_climb_angle, up, }: BinaryTopology): void;
|
|
17
19
|
/**
|
|
18
20
|
* Compute a walkable path between the two points.
|
|
19
|
-
* The result is a sequence of 3d points written into `output`.
|
|
21
|
+
* The result is a sequence of 3d points written into `output`. The first and last points are the
|
|
22
|
+
* start and goal snapped onto the mesh surface (the closest walkable point to each query position),
|
|
23
|
+
* so they may differ from the raw `start_*`/`goal_*` inputs when those lie off the mesh.
|
|
20
24
|
*
|
|
21
25
|
* @param {Float32Array} output packed XYZ triples
|
|
22
26
|
* @param {number} start_x
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationMesh.d.ts","sourceRoot":"","sources":["../../../../../src/engine/navigation/mesh/NavigationMesh.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NavigationMesh.d.ts","sourceRoot":"","sources":["../../../../../src/engine/navigation/mesh/NavigationMesh.js"],"names":[],"mappings":"AAoCA;IAEI,yBAAgC;IAEhC;;;OAGG;IACH,KAFU,GAAG,CAEG;IAEhB;;;;;;;;;OASG;IACH,0HARW,cAAc,QA8BxB;IAGD;;;;;;;;;;;;;;OAcG;IACH,kBATW,YAAY,WACZ,MAAM,WACN,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,UACN,MAAM,GACJ,MAAM,CAyIlB;CAEJ;+BAxO4C,gEAAgE;oBADzF,gCAAgC"}
|