@woosh/meep-engine 2.152.0 → 2.154.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/color/Color.d.ts +26 -6
- package/src/core/color/Color.d.ts.map +1 -1
- package/src/core/color/Color.js +38 -6
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +112 -0
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +325 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +4 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.js +21 -0
- package/src/engine/physics/PLAN.md +4 -4
- package/src/engine/physics/body/BodyStorage.d.ts +3 -1
- package/src/engine/physics/body/BodyStorage.d.ts.map +1 -1
- package/src/engine/physics/body/BodyStorage.js +452 -450
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
- package/src/engine/physics/body/SolverBodyState.js +6 -5
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +9 -1
- package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
- package/src/engine/physics/ccd/linear_sweep.js +237 -238
- package/src/engine/physics/computeInterceptPoint.d.ts.map +1 -1
- package/src/engine/physics/computeInterceptPoint.js +8 -3
- package/src/engine/physics/contact/ManifoldStore.d.ts +0 -16
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +1 -38
- package/src/engine/physics/ecs/BodyKind.d.ts +3 -2
- package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
- package/src/engine/physics/ecs/BodyKind.js +25 -24
- package/src/engine/physics/ecs/PhysicsEvents.d.ts +4 -5
- package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsEvents.js +15 -16
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +5 -30
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +13 -45
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts.map +1 -1
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.js +85 -81
- package/src/engine/physics/ecs/is_sensor.d.ts +18 -0
- package/src/engine/physics/ecs/is_sensor.d.ts.map +1 -0
- package/src/engine/physics/ecs/is_sensor.js +27 -0
- package/src/engine/physics/events/ContactEventBuffer.d.ts +2 -1
- package/src/engine/physics/events/ContactEventBuffer.d.ts.map +1 -1
- package/src/engine/physics/events/ContactEventBuffer.js +84 -83
- package/src/engine/physics/gjk/gjk.d.ts +0 -26
- package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
- package/src/engine/physics/gjk/gjk.js +3 -52
- package/src/engine/physics/gjk/gjk_epa_penetration.d.ts +16 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.d.ts.map +1 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.js +255 -0
- package/src/engine/physics/gjk/minkowski_support.d.ts +4 -9
- package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -1
- package/src/engine/physics/gjk/minkowski_support.js +70 -75
- package/src/engine/physics/gjk/mpr.d.ts +1 -1
- package/src/engine/physics/gjk/mpr.d.ts.map +1 -1
- package/src/engine/physics/gjk/mpr.js +362 -344
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +431 -428
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +4 -81
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +4 -39
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +459 -462
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +4 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +83 -0
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +425 -0
- package/src/engine/physics/narrowphase/convex_decomposition.d.ts +32 -0
- package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/convex_decomposition.js +293 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts +41 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.js +106 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +8 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +105 -102
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +29 -0
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +69 -0
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +152 -145
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +132 -123
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
- package/src/engine/physics/queries/overlap_shape.js +16 -17
- package/src/engine/physics/queries/raycast.d.ts +5 -0
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +16 -8
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
- package/src/engine/physics/queries/shape_cast.js +13 -7
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +8 -11
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +339 -333
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +0 -13
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +0 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +0 -399
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TODO: needs to be tested thoroughly, intended to be working with GJK
|
|
3
|
-
* @param {number[]|Float64Array} result
|
|
4
|
-
* @param {number} result_offset
|
|
5
|
-
* @param {number[]|Float64Array} a
|
|
6
|
-
* @param {number[]|Float64Array} b
|
|
7
|
-
* @param {number[]|Float64Array} c
|
|
8
|
-
* @param {number[]|Float64Array} d
|
|
9
|
-
* @param {AbstractShape3D} coll1
|
|
10
|
-
* @param {AbstractShape3D} coll2
|
|
11
|
-
*/
|
|
12
|
-
export function expanding_polytope_algorithm(result: number[] | Float64Array, result_offset: number, a: number[] | Float64Array, b: number[] | Float64Array, c: number[] | Float64Array, d: number[] | Float64Array, coll1: AbstractShape3D, coll2: AbstractShape3D): void;
|
|
13
|
-
//# sourceMappingURL=expanding_polytope_algorithm.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expanding_polytope_algorithm.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/gjk/expanding_polytope_algorithm.js"],"names":[],"mappings":"AAqGA;;;;;;;;;;GAUG;AACH,qDATW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,KACN,MAAM,EAAE,GAAC,YAAY,KACrB,MAAM,EAAE,GAAC,YAAY,KACrB,MAAM,EAAE,GAAC,YAAY,KACrB,MAAM,EAAE,GAAC,YAAY,wDAuQ/B"}
|
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
import { array_copy } from "../../../core/collection/array/array_copy.js";
|
|
2
|
-
import { array_swap } from "../../../core/collection/array/array_swap.js";
|
|
3
|
-
import { v3_compute_triangle_normal } from "../../../core/geom/3d/triangle/v3_compute_triangle_normal.js";
|
|
4
|
-
import { v3_dot } from "../../../core/geom/vec3/v3_dot.js";
|
|
5
|
-
import { v3_dot_array_array } from "../../../core/geom/vec3/v3_dot_array_array.js";
|
|
6
|
-
|
|
7
|
-
import { v3_negate_array } from "../../../core/geom/vec3/v3_negate_array.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Convergence tolerance — relative to the current closest-face distance,
|
|
11
|
-
* floored at an absolute epsilon to prevent zero-distance loops.
|
|
12
|
-
*
|
|
13
|
-
* Why relative, not absolute (P2.2). An absolute `EPA_TOLERANCE = 1e-4`
|
|
14
|
-
* works for mid-scale contacts (a few millimetres deep on metre-sized
|
|
15
|
-
* bodies) but degrades for shallow contacts: when the closest-face
|
|
16
|
-
* distance is ~1e-5 m (sub-mm overlaps that the speculative-margin path
|
|
17
|
-
* handles), `1e-4` is *larger than the depth itself*, so EPA terminates
|
|
18
|
-
* with whatever intermediate face it happens to have, producing a noisy
|
|
19
|
-
* normal direction. Jolt's `EPSILON_REL_DIST_SQ` and similar
|
|
20
|
-
* formulations scale tolerance with the current distance.
|
|
21
|
-
*
|
|
22
|
-
* Note: only the TOLERANCE was made relative as part of P2.2. The
|
|
23
|
-
* convergence-path RESULT magnitude is intentionally left as
|
|
24
|
-
* `dot_p_search_dir` (the new support point's projection) — switching
|
|
25
|
-
* to `min_dist` was tried and caused energy injection in the closed-
|
|
26
|
-
* form-dispatch paths that share the EPA depth metric (e.g.
|
|
27
|
-
* sphere-vs-sphere stack regressions). See REVIEW_001_ACTION_PLAN.md.
|
|
28
|
-
*
|
|
29
|
-
* @type {number}
|
|
30
|
-
*/
|
|
31
|
-
const EPA_TOLERANCE_REL = 1e-4;
|
|
32
|
-
const EPA_TOLERANCE_ABS = 1e-6;
|
|
33
|
-
const EPA_MAX_NUM_FACES = 64;
|
|
34
|
-
const EPA_MAX_NUM_LOOSE_EDGES = 32;
|
|
35
|
-
const EPA_MAX_NUM_ITERATIONS = 64;
|
|
36
|
-
|
|
37
|
-
const FACE_ELEMENT_COUNT = 3 * 4;
|
|
38
|
-
|
|
39
|
-
const EDGE_ELEMENT_COUNT = 2 * 3;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Polytope face buffer. Float64 throughout — EPA's expand-and-recompute
|
|
43
|
-
* step iterates up to {@link EPA_MAX_NUM_ITERATIONS} times, each
|
|
44
|
-
* iteration rebuilds face normals via cross products, and tests them
|
|
45
|
-
* against accumulated support points; the per-iteration error in
|
|
46
|
-
* Float32 compounds and noticeably slows convergence on shapes with
|
|
47
|
-
* many similar-magnitude faces (anything with subtle curvature). The
|
|
48
|
-
* memory footprint of the upgrade is the buffer's size doubling — a
|
|
49
|
-
* few KB at most.
|
|
50
|
-
*
|
|
51
|
-
* Layout per face: 3 vec3 vertices + 1 vec3 normal = 12 floats.
|
|
52
|
-
* @type {Float64Array}
|
|
53
|
-
*/
|
|
54
|
-
const faces = new Float64Array(EPA_MAX_NUM_FACES * FACE_ELEMENT_COUNT);
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Edges we need to fix after removing faces. Same Float64 motivation
|
|
58
|
-
* as {@link faces} — endpoints are vertex coords pulled out of `faces`,
|
|
59
|
-
* and any precision loss here propagates back into the next face when
|
|
60
|
-
* the polytope is rebuilt.
|
|
61
|
-
* @type {Float64Array}
|
|
62
|
-
*/
|
|
63
|
-
const loose_edges = new Float64Array(EPA_MAX_NUM_LOOSE_EDGES * EDGE_ELEMENT_COUNT);
|
|
64
|
-
|
|
65
|
-
const scratch_v3 = new Float64Array(3);
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
69
|
-
* @param {number[]} target
|
|
70
|
-
* @param {number} target_index
|
|
71
|
-
* @param {number[]} v
|
|
72
|
-
*/
|
|
73
|
-
function write_v3(target, target_index, v) {
|
|
74
|
-
array_copy(v, 0, faces, target_index * 3, 3);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
*
|
|
79
|
-
* @param {number[]|Float64Array} faces
|
|
80
|
-
* @param {number} index
|
|
81
|
-
* @param {number[]} a
|
|
82
|
-
* @param {number[]} b
|
|
83
|
-
* @param {number[]} c
|
|
84
|
-
*/
|
|
85
|
-
function write_face(faces, index, a, b, c) {
|
|
86
|
-
const index4 = index * 4;
|
|
87
|
-
|
|
88
|
-
write_v3(faces, index4 + 0, a);
|
|
89
|
-
write_v3(faces, index4 + 1, b);
|
|
90
|
-
write_v3(faces, index4 + 2, c);
|
|
91
|
-
|
|
92
|
-
const normal_offset = (index4 + 3) * 3;
|
|
93
|
-
|
|
94
|
-
v3_compute_triangle_normal(
|
|
95
|
-
faces, normal_offset,
|
|
96
|
-
a[0], a[1], a[2],
|
|
97
|
-
b[0], b[1], b[2],
|
|
98
|
-
c[0], c[1], c[2]
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* TODO: needs to be tested thoroughly, intended to be working with GJK
|
|
104
|
-
* @param {number[]|Float64Array} result
|
|
105
|
-
* @param {number} result_offset
|
|
106
|
-
* @param {number[]|Float64Array} a
|
|
107
|
-
* @param {number[]|Float64Array} b
|
|
108
|
-
* @param {number[]|Float64Array} c
|
|
109
|
-
* @param {number[]|Float64Array} d
|
|
110
|
-
* @param {AbstractShape3D} coll1
|
|
111
|
-
* @param {AbstractShape3D} coll2
|
|
112
|
-
*/
|
|
113
|
-
export function expanding_polytope_algorithm(
|
|
114
|
-
result, result_offset,
|
|
115
|
-
a, b, c, d,
|
|
116
|
-
coll1, coll2
|
|
117
|
-
) {
|
|
118
|
-
/*
|
|
119
|
-
Adapted from https://github.com/kevinmoran/GJK/blob/master/GJK.h
|
|
120
|
-
|
|
121
|
-
"GJK + Expanding Polytope Algorithm - Implementation and Visualization" https://www.youtube.com/watch?v=6rgiPrzqt9w
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
|
-
//Init with final simplex from GJK
|
|
125
|
-
write_face(faces, 0, a, b, c);
|
|
126
|
-
write_face(faces, 1, a, c, d);
|
|
127
|
-
write_face(faces, 2, a, d, b);
|
|
128
|
-
write_face(faces, 3, b, d, c);
|
|
129
|
-
|
|
130
|
-
let num_faces = 4;
|
|
131
|
-
let closest_face;
|
|
132
|
-
|
|
133
|
-
for (let iterations = 0; iterations < EPA_MAX_NUM_ITERATIONS; iterations++) {
|
|
134
|
-
|
|
135
|
-
//Find face that's closest to origin
|
|
136
|
-
|
|
137
|
-
let min_dist = v3_dot_array_array(faces, 0, faces, 3 * 3);
|
|
138
|
-
closest_face = 0;
|
|
139
|
-
|
|
140
|
-
for (let i = 1; i < num_faces; i++) {
|
|
141
|
-
const dist = v3_dot_array_array(faces, i * FACE_ELEMENT_COUNT, faces, i * FACE_ELEMENT_COUNT + 3 * 3);
|
|
142
|
-
|
|
143
|
-
if (dist < min_dist) {
|
|
144
|
-
min_dist = dist;
|
|
145
|
-
closest_face = i;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
//search normal to face that's closest to origin
|
|
151
|
-
const closest_face_normal_offset = closest_face * FACE_ELEMENT_COUNT + 3 * 3;
|
|
152
|
-
|
|
153
|
-
const search_dir_x = faces[closest_face_normal_offset];
|
|
154
|
-
const search_dir_y = faces[closest_face_normal_offset + 1];
|
|
155
|
-
const search_dir_z = faces[closest_face_normal_offset + 2];
|
|
156
|
-
|
|
157
|
-
// build new support point to expand polytope
|
|
158
|
-
|
|
159
|
-
coll2.support(scratch_v3, 0, search_dir_x, search_dir_y, search_dir_z);
|
|
160
|
-
|
|
161
|
-
const support2_x = scratch_v3[0];
|
|
162
|
-
const support2_y = scratch_v3[1];
|
|
163
|
-
const support2_z = scratch_v3[2];
|
|
164
|
-
|
|
165
|
-
coll1.support(scratch_v3, 0, -search_dir_x, -search_dir_y, -search_dir_z);
|
|
166
|
-
|
|
167
|
-
const support1_x = scratch_v3[0];
|
|
168
|
-
const support1_y = scratch_v3[1];
|
|
169
|
-
const support1_z = scratch_v3[2];
|
|
170
|
-
|
|
171
|
-
const p_x = support2_x - support1_x;
|
|
172
|
-
const p_y = support2_y - support1_y;
|
|
173
|
-
const p_z = support2_z - support1_z;
|
|
174
|
-
|
|
175
|
-
const dot_p_search_dir = v3_dot(p_x, p_y, p_z, search_dir_x, search_dir_y, search_dir_z);
|
|
176
|
-
|
|
177
|
-
// Adaptive convergence (P2.2). Terminate when the new support
|
|
178
|
-
// point's projection onto the current search direction is
|
|
179
|
-
// within a RELATIVE tolerance of the current closest-face
|
|
180
|
-
// distance — scales with depth instead of the fixed absolute
|
|
181
|
-
// EPA_TOLERANCE = 1e-4 that was too coarse for sub-mm contacts.
|
|
182
|
-
const min_dist_abs = min_dist < 0 ? -min_dist : min_dist;
|
|
183
|
-
const tol_floor = min_dist_abs > EPA_TOLERANCE_ABS ? min_dist_abs : EPA_TOLERANCE_ABS;
|
|
184
|
-
if (dot_p_search_dir - min_dist < EPA_TOLERANCE_REL * tol_floor) {
|
|
185
|
-
//Convergence (new point is not significantly further from origin)
|
|
186
|
-
|
|
187
|
-
result[result_offset] = search_dir_x * dot_p_search_dir;
|
|
188
|
-
result[result_offset + 1] = search_dir_y * dot_p_search_dir;
|
|
189
|
-
result[result_offset + 2] = search_dir_z * dot_p_search_dir;
|
|
190
|
-
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let num_loose_edges = 0;
|
|
195
|
-
|
|
196
|
-
//Find all triangles that are facing p
|
|
197
|
-
for (let i = 0; i < num_faces; i++) {
|
|
198
|
-
const face_offset = i * FACE_ELEMENT_COUNT;
|
|
199
|
-
|
|
200
|
-
const face_normal_address = face_offset + 3 * 3;
|
|
201
|
-
const face_a_address = face_offset;
|
|
202
|
-
|
|
203
|
-
const face_normal_x = faces[face_normal_address];
|
|
204
|
-
const face_normal_y = faces[face_normal_address + 1];
|
|
205
|
-
const face_normal_z = faces[face_normal_address + 2];
|
|
206
|
-
|
|
207
|
-
const face_a_x = faces[face_a_address];
|
|
208
|
-
const face_a_y = faces[face_a_address + 1];
|
|
209
|
-
const face_a_z = faces[face_a_address + 2];
|
|
210
|
-
|
|
211
|
-
const pa_x = p_x - face_a_x;
|
|
212
|
-
const pa_y = p_y - face_a_y;
|
|
213
|
-
const pa_z = p_z - face_a_z;
|
|
214
|
-
|
|
215
|
-
if (v3_dot(face_normal_x, face_normal_y, face_normal_z, pa_x, pa_y, pa_z) <= 0) {
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// triangle i faces p, remove it
|
|
220
|
-
for (let j = 0; j < 3; j++) //Three edges per face
|
|
221
|
-
{
|
|
222
|
-
const a_offset = j * 3;
|
|
223
|
-
|
|
224
|
-
const current_edge_ax = faces[face_offset + a_offset];
|
|
225
|
-
const current_edge_ay = faces[face_offset + a_offset + 1];
|
|
226
|
-
const current_edge_az = faces[face_offset + a_offset + 2];
|
|
227
|
-
|
|
228
|
-
const b_offset = ((j + 1) % 3) * 3;
|
|
229
|
-
|
|
230
|
-
const current_edge_bx = faces[face_offset + b_offset];
|
|
231
|
-
const current_edge_by = faces[face_offset + b_offset + 1];
|
|
232
|
-
const current_edge_bz = faces[face_offset + b_offset + 2];
|
|
233
|
-
|
|
234
|
-
// vec3 current_edge[2] = { faces[i][j], faces[i][(j + 1) % 3] };
|
|
235
|
-
|
|
236
|
-
let found_edge = false;
|
|
237
|
-
|
|
238
|
-
//Check if current edge is already in list
|
|
239
|
-
for (let k = 0; k < num_loose_edges; k++) {
|
|
240
|
-
const loose_edge_offset = k * EDGE_ELEMENT_COUNT;
|
|
241
|
-
|
|
242
|
-
const loose_edge_ax = loose_edges[loose_edge_offset];
|
|
243
|
-
const loose_edge_ay = loose_edges[loose_edge_offset + 1];
|
|
244
|
-
const loose_edge_az = loose_edges[loose_edge_offset + 2];
|
|
245
|
-
|
|
246
|
-
const loose_edge_bx = loose_edges[loose_edge_offset + 3];
|
|
247
|
-
const loose_edge_by = loose_edges[loose_edge_offset + 4];
|
|
248
|
-
const loose_edge_bz = loose_edges[loose_edge_offset + 5];
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// if (loose_edges[k][1] === current_edge[0] && loose_edges[k][0] === current_edge[1]) {
|
|
252
|
-
if (
|
|
253
|
-
(
|
|
254
|
-
loose_edge_ax === current_edge_bx
|
|
255
|
-
&& loose_edge_ay === current_edge_by
|
|
256
|
-
&& loose_edge_az === current_edge_bz
|
|
257
|
-
)
|
|
258
|
-
&& (
|
|
259
|
-
loose_edge_bx === current_edge_ax
|
|
260
|
-
&& loose_edge_by === current_edge_ay
|
|
261
|
-
&& loose_edge_bz === current_edge_az
|
|
262
|
-
)
|
|
263
|
-
) {
|
|
264
|
-
//Edge is already in the list, remove it
|
|
265
|
-
//THIS ASSUMES EDGE CAN ONLY BE SHARED BY 2 TRIANGLES (which should be true)
|
|
266
|
-
//THIS ALSO ASSUMES SHARED EDGE WILL BE REVERSED IN THE TRIANGLES (which
|
|
267
|
-
//should be true provided every triangle is wound CCW)
|
|
268
|
-
|
|
269
|
-
// Overwrite current edge with last edge in list
|
|
270
|
-
loose_edges.copyWithin(loose_edge_offset, (num_loose_edges - 1) * EDGE_ELEMENT_COUNT, (num_loose_edges) * EDGE_ELEMENT_COUNT);
|
|
271
|
-
|
|
272
|
-
num_loose_edges--;
|
|
273
|
-
found_edge = true;
|
|
274
|
-
|
|
275
|
-
//exit loop because edge can only be shared once
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!found_edge) { //add current edge to list
|
|
281
|
-
if (num_loose_edges >= EPA_MAX_NUM_LOOSE_EDGES) {
|
|
282
|
-
// Polytope degenerated (typically smooth-surface
|
|
283
|
-
// pairs the dispatch should be special-casing — this
|
|
284
|
-
// is the safety net). Bail out and return the
|
|
285
|
-
// closest-face result accumulated so far instead of
|
|
286
|
-
// crashing the simulation step.
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const last_edge_offset = num_loose_edges * EDGE_ELEMENT_COUNT;
|
|
291
|
-
|
|
292
|
-
loose_edges[last_edge_offset] = current_edge_ax;
|
|
293
|
-
loose_edges[last_edge_offset + 1] = current_edge_ay;
|
|
294
|
-
loose_edges[last_edge_offset + 2] = current_edge_az;
|
|
295
|
-
|
|
296
|
-
loose_edges[last_edge_offset + 3] = current_edge_bx;
|
|
297
|
-
loose_edges[last_edge_offset + 4] = current_edge_by;
|
|
298
|
-
loose_edges[last_edge_offset + 5] = current_edge_bz;
|
|
299
|
-
|
|
300
|
-
num_loose_edges++;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// move last face to fill the removed element
|
|
305
|
-
faces.copyWithin(i * FACE_ELEMENT_COUNT, (num_faces - 1) * FACE_ELEMENT_COUNT, num_faces * FACE_ELEMENT_COUNT);
|
|
306
|
-
|
|
307
|
-
num_faces--;
|
|
308
|
-
i--;
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
//Reconstruct polytope with p added
|
|
313
|
-
for (let i = 0; i < num_loose_edges; i++) {
|
|
314
|
-
if (num_faces >= EPA_MAX_NUM_FACES) {
|
|
315
|
-
// Polytope hit the face cap before converging. Same
|
|
316
|
-
// degenerate-surface story as the loose-edge overflow above.
|
|
317
|
-
// Bail out and let the caller use the best face found so far.
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const face_offset = num_faces * FACE_ELEMENT_COUNT;
|
|
322
|
-
|
|
323
|
-
const loose_edge_offset = i * EDGE_ELEMENT_COUNT;
|
|
324
|
-
|
|
325
|
-
array_copy(
|
|
326
|
-
loose_edges, loose_edge_offset,
|
|
327
|
-
faces, face_offset,
|
|
328
|
-
EDGE_ELEMENT_COUNT
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
faces[face_offset + 3 * 2] = p_x;
|
|
332
|
-
faces[face_offset + 3 * 2 + 1] = p_y;
|
|
333
|
-
faces[face_offset + 3 * 2 + 2] = p_z;
|
|
334
|
-
|
|
335
|
-
// construct normal
|
|
336
|
-
const face_normal_offset = face_offset + 3 * 3;
|
|
337
|
-
construct_normal(face_normal_offset, i, p_x, p_y, p_z);
|
|
338
|
-
|
|
339
|
-
//Check for wrong normal to maintain CCW winding
|
|
340
|
-
const bias = 0.000001; //in case dot result is only slightly < 0 (because origin is on face)
|
|
341
|
-
|
|
342
|
-
const dot = v3_dot_array_array(faces, face_offset, faces, face_normal_offset)
|
|
343
|
-
|
|
344
|
-
if (dot + bias < 0) {
|
|
345
|
-
array_swap(faces, face_offset, faces, face_offset + 3, 3);
|
|
346
|
-
// invert normal
|
|
347
|
-
v3_negate_array(faces, face_normal_offset, faces, face_normal_offset);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
num_faces++;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Iteration cap reached without convergence. Return the
|
|
355
|
-
// closest-face approximation rather than warning — this is the
|
|
356
|
-
// expected outcome on smooth / high-complexity shapes (a torus knot
|
|
357
|
-
// collider produces it dozens of times per frame while the player
|
|
358
|
-
// is in contact). The narrowphase consumer treats any depth that
|
|
359
|
-
// isn't strictly positive + finite as a miss, so a degenerate
|
|
360
|
-
// closest-face result is filtered upstream. PLAN.md already lists
|
|
361
|
-
// EPA-on-smooth-shapes as a known limitation; the actionable fix
|
|
362
|
-
// is MPR / hierarchical support, not console spam.
|
|
363
|
-
const closest_face_offset = closest_face * FACE_ELEMENT_COUNT;
|
|
364
|
-
const closest_face_normal_offset = closest_face_offset + 3 * 3;
|
|
365
|
-
|
|
366
|
-
const dot = v3_dot_array_array(faces, closest_face_offset, faces, closest_face_normal_offset);
|
|
367
|
-
|
|
368
|
-
result[result_offset] = faces[closest_face_normal_offset + 0] * dot;
|
|
369
|
-
result[result_offset + 1] = faces[closest_face_normal_offset + 1] * dot;
|
|
370
|
-
result[result_offset + 2] = faces[closest_face_normal_offset + 2] * dot;
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
*
|
|
376
|
-
* @param {number} result_offset
|
|
377
|
-
* @param {number} edge_index
|
|
378
|
-
* @param {number} p_x
|
|
379
|
-
* @param {number} p_y
|
|
380
|
-
* @param {number} p_z
|
|
381
|
-
*/
|
|
382
|
-
function construct_normal(result_offset, edge_index, p_x, p_y, p_z) {
|
|
383
|
-
const edge_offset = edge_index * EDGE_ELEMENT_COUNT;
|
|
384
|
-
|
|
385
|
-
const edge_ax = loose_edges[edge_offset];
|
|
386
|
-
const edge_ay = loose_edges[edge_offset + 1];
|
|
387
|
-
const edge_az = loose_edges[edge_offset + 2];
|
|
388
|
-
|
|
389
|
-
const edge_bx = loose_edges[edge_offset + 3];
|
|
390
|
-
const edge_by = loose_edges[edge_offset + 4];
|
|
391
|
-
const edge_bz = loose_edges[edge_offset + 5];
|
|
392
|
-
|
|
393
|
-
v3_compute_triangle_normal(
|
|
394
|
-
faces, result_offset,
|
|
395
|
-
edge_ax, edge_ay, edge_az,
|
|
396
|
-
edge_bx, edge_by, edge_bz,
|
|
397
|
-
p_x, p_y, p_z
|
|
398
|
-
);
|
|
399
|
-
}
|