@woosh/meep-engine 2.153.0 → 2.155.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/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/core/geom/vec3/v3_array_copy.d.ts +3 -3
- package/src/core/geom/vec3/v3_array_copy.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_array_copy.js +2 -2
- package/src/core/geom/vec3/v3_cross.d.ts +17 -0
- package/src/core/geom/vec3/v3_cross.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_cross.js +20 -0
- package/src/core/geom/vec3/v3_subtract.d.ts +16 -0
- package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_subtract.js +19 -0
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +8 -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 +20 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.d.ts.map +1 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.js +548 -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,145 +1,152 @@
|
|
|
1
|
-
import { v3_quat3_apply } from "../../../core/geom/vec3/v3_quat3_apply.js";
|
|
2
|
-
import { TRIANGLE_FLOAT_STRIDE } from "./decomposition/triangle_buffer_layout.js";
|
|
3
|
-
import { mesh_enumerate_triangles } from "./decomposition/mesh_enumerate_triangles.js";
|
|
4
|
-
import { heightmap_enumerate_triangles } from "./decomposition/heightmap_enumerate_triangles.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* # Ray vs a concave shape (mesh / heightmap)
|
|
8
|
-
*
|
|
9
|
-
* Refines a ray hit against a non-convex collider by triangle decomposition,
|
|
10
|
-
* mirroring the contact narrowphase's concave path. Working in the shape's
|
|
11
|
-
* local frame:
|
|
12
|
-
* 1. clip the ray to the shape's local bounding box (a quick reject, and a
|
|
13
|
-
* finite query segment even when `tMax` is unbounded);
|
|
14
|
-
* 2. enumerate the triangles overlapping that segment's AABB
|
|
15
|
-
* (`mesh_enumerate_triangles` / `heightmap_enumerate_triangles`);
|
|
16
|
-
* 3. two-sided Möller–Trumbore each, keeping the nearest crossing;
|
|
17
|
-
* 4. return its distance and the world surface normal (the triangle's
|
|
18
|
-
* geometric normal, oriented to face the ray).
|
|
19
|
-
*
|
|
20
|
-
* The triangle scratch grows on saturation and is bounded by {@link MAX_TRIS}
|
|
21
|
-
* (a ray crossing more triangles than that in one mesh is not a realistic
|
|
22
|
-
* raycast — it would mean skewering a 65k-triangle mesh lengthwise).
|
|
23
|
-
*
|
|
24
|
-
* @author Alex Goldring
|
|
25
|
-
* @copyright Company Named Limited (c) 2026
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
const lo = new Float64Array(3); // ray origin, shape-local
|
|
29
|
-
const ld = new Float64Array(3); // ray direction, shape-local
|
|
30
|
-
const bounds = new Float64Array(6); // shape local AABB
|
|
31
|
-
|
|
32
|
-
const INITIAL_TRIS = 1024;
|
|
33
|
-
const MAX_TRIS = 1 << 16;
|
|
34
|
-
let tri_buf = new Float64Array(INITIAL_TRIS * TRIANGLE_FLOAT_STRIDE);
|
|
35
|
-
|
|
36
|
-
const EPS = 1e-12;
|
|
37
|
-
const AABB_FATTEN = 1e-5; // guards triangles whose AABB just abuts the segment
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @param {AbstractShape3D} shape a concave shape (`is_convex === false`)
|
|
41
|
-
* @param {{x:number,y:number,z:number}} position
|
|
42
|
-
* @param {ArrayLike<number>} rotation body rotation quaternion (x,y,z,w)
|
|
43
|
-
* @param {number} ox @param {number} oy @param {number} oz ray origin (world)
|
|
44
|
-
* @param {number} dx @param {number} dy @param {number} dz ray dir (world, unit)
|
|
45
|
-
* @param {number} tMax
|
|
46
|
-
* @param {Float64Array|Vector3} outNormal world surface normal, written on hit
|
|
47
|
-
* @returns {number} hit distance, or `Infinity` on miss
|
|
48
|
-
*/
|
|
49
|
-
export function refine_ray_concave(shape, position, rotation, ox, oy, oz, dx, dy, dz, tMax, outNormal) {
|
|
50
|
-
const qx = rotation[0], qy = rotation[1], qz = rotation[2], qw = rotation[3];
|
|
51
|
-
// World → local (inverse body rotation; unit dir stays unit → t preserved).
|
|
52
|
-
v3_quat3_apply(lo, 0, ox - position.x, oy - position.y, oz - position.z, -qx, -qy, -qz, qw);
|
|
53
|
-
v3_quat3_apply(ld, 0, dx, dy, dz, -qx, -qy, -qz, qw);
|
|
54
|
-
const lox = lo[0], loy = lo[1], loz = lo[2];
|
|
55
|
-
const ldx = ld[0], ldy = ld[1], ldz = ld[2];
|
|
56
|
-
|
|
57
|
-
// Clip the local ray to the shape's local AABB → segment [tEnter, tExit].
|
|
58
|
-
shape.compute_bounding_box(bounds);
|
|
59
|
-
let tEnter = 0, tExit = tMax;
|
|
60
|
-
if (ldx > EPS || ldx < -EPS) {
|
|
61
|
-
const inv = 1 / ldx;
|
|
62
|
-
let t1 = (bounds[0] - lox) * inv, t2 = (bounds[3] - lox) * inv;
|
|
63
|
-
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
64
|
-
if (t1 > tEnter) tEnter = t1;
|
|
65
|
-
if (t2 < tExit) tExit = t2;
|
|
66
|
-
} else if (lox < bounds[0] || lox > bounds[3]) return Infinity;
|
|
67
|
-
if (ldy > EPS || ldy < -EPS) {
|
|
68
|
-
const inv = 1 / ldy;
|
|
69
|
-
let t1 = (bounds[1] - loy) * inv, t2 = (bounds[4] - loy) * inv;
|
|
70
|
-
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
71
|
-
if (t1 > tEnter) tEnter = t1;
|
|
72
|
-
if (t2 < tExit) tExit = t2;
|
|
73
|
-
} else if (loy < bounds[1] || loy > bounds[4]) return Infinity;
|
|
74
|
-
if (ldz > EPS || ldz < -EPS) {
|
|
75
|
-
const inv = 1 / ldz;
|
|
76
|
-
let t1 = (bounds[2] - loz) * inv, t2 = (bounds[5] - loz) * inv;
|
|
77
|
-
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
78
|
-
if (t1 > tEnter) tEnter = t1;
|
|
79
|
-
if (t2 < tExit) tExit = t2;
|
|
80
|
-
} else if (loz < bounds[2] || loz > bounds[5]) return Infinity;
|
|
81
|
-
if (tExit < tEnter) return Infinity; // ray misses the shape's AABB entirely
|
|
82
|
-
|
|
83
|
-
// Query AABB = bbox of the clipped local segment.
|
|
84
|
-
const sax = lox + ldx * tEnter, say = loy + ldy * tEnter, saz = loz + ldz * tEnter;
|
|
85
|
-
const sbx = lox + ldx * tExit, sby = loy + ldy * tExit, sbz = loz + ldz * tExit;
|
|
86
|
-
const qminx = (sax < sbx ? sax : sbx) - AABB_FATTEN;
|
|
87
|
-
const qminy = (say < sby ? say : sby) - AABB_FATTEN;
|
|
88
|
-
const qminz = (saz < sbz ? saz : sbz) - AABB_FATTEN;
|
|
89
|
-
const qmaxx = (sax > sbx ? sax : sbx) + AABB_FATTEN;
|
|
90
|
-
const qmaxy = (say > sby ? say : sby) + AABB_FATTEN;
|
|
91
|
-
const qmaxz = (saz > sbz ? saz : sbz) + AABB_FATTEN;
|
|
92
|
-
|
|
93
|
-
const enumerate = shape.isHeightMapShape3D === true ? heightmap_enumerate_triangles : mesh_enumerate_triangles;
|
|
94
|
-
let cap = (tri_buf.length / TRIANGLE_FLOAT_STRIDE) | 0;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1
|
+
import { v3_quat3_apply } from "../../../core/geom/vec3/v3_quat3_apply.js";
|
|
2
|
+
import { TRIANGLE_FLOAT_STRIDE } from "./decomposition/triangle_buffer_layout.js";
|
|
3
|
+
import { mesh_enumerate_triangles } from "./decomposition/mesh_enumerate_triangles.js";
|
|
4
|
+
import { heightmap_enumerate_triangles } from "./decomposition/heightmap_enumerate_triangles.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # Ray vs a concave shape (mesh / heightmap)
|
|
8
|
+
*
|
|
9
|
+
* Refines a ray hit against a non-convex collider by triangle decomposition,
|
|
10
|
+
* mirroring the contact narrowphase's concave path. Working in the shape's
|
|
11
|
+
* local frame:
|
|
12
|
+
* 1. clip the ray to the shape's local bounding box (a quick reject, and a
|
|
13
|
+
* finite query segment even when `tMax` is unbounded);
|
|
14
|
+
* 2. enumerate the triangles overlapping that segment's AABB
|
|
15
|
+
* (`mesh_enumerate_triangles` / `heightmap_enumerate_triangles`);
|
|
16
|
+
* 3. two-sided Möller–Trumbore each, keeping the nearest crossing;
|
|
17
|
+
* 4. return its distance and the world surface normal (the triangle's
|
|
18
|
+
* geometric normal, oriented to face the ray).
|
|
19
|
+
*
|
|
20
|
+
* The triangle scratch grows on saturation and is bounded by {@link MAX_TRIS}
|
|
21
|
+
* (a ray crossing more triangles than that in one mesh is not a realistic
|
|
22
|
+
* raycast — it would mean skewering a 65k-triangle mesh lengthwise).
|
|
23
|
+
*
|
|
24
|
+
* @author Alex Goldring
|
|
25
|
+
* @copyright Company Named Limited (c) 2026
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const lo = new Float64Array(3); // ray origin, shape-local
|
|
29
|
+
const ld = new Float64Array(3); // ray direction, shape-local
|
|
30
|
+
const bounds = new Float64Array(6); // shape local AABB
|
|
31
|
+
|
|
32
|
+
const INITIAL_TRIS = 1024;
|
|
33
|
+
const MAX_TRIS = 1 << 16;
|
|
34
|
+
let tri_buf = new Float64Array(INITIAL_TRIS * TRIANGLE_FLOAT_STRIDE);
|
|
35
|
+
|
|
36
|
+
const EPS = 1e-12;
|
|
37
|
+
const AABB_FATTEN = 1e-5; // guards triangles whose AABB just abuts the segment
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {AbstractShape3D} shape a concave shape (`is_convex === false`)
|
|
41
|
+
* @param {{x:number,y:number,z:number}} position
|
|
42
|
+
* @param {ArrayLike<number>} rotation body rotation quaternion (x,y,z,w)
|
|
43
|
+
* @param {number} ox @param {number} oy @param {number} oz ray origin (world)
|
|
44
|
+
* @param {number} dx @param {number} dy @param {number} dz ray dir (world, unit)
|
|
45
|
+
* @param {number} tMax
|
|
46
|
+
* @param {Float64Array|Vector3} outNormal world surface normal, written on hit
|
|
47
|
+
* @returns {number} hit distance, or `Infinity` on miss
|
|
48
|
+
*/
|
|
49
|
+
export function refine_ray_concave(shape, position, rotation, ox, oy, oz, dx, dy, dz, tMax, outNormal) {
|
|
50
|
+
const qx = rotation[0], qy = rotation[1], qz = rotation[2], qw = rotation[3];
|
|
51
|
+
// World → local (inverse body rotation; unit dir stays unit → t preserved).
|
|
52
|
+
v3_quat3_apply(lo, 0, ox - position.x, oy - position.y, oz - position.z, -qx, -qy, -qz, qw);
|
|
53
|
+
v3_quat3_apply(ld, 0, dx, dy, dz, -qx, -qy, -qz, qw);
|
|
54
|
+
const lox = lo[0], loy = lo[1], loz = lo[2];
|
|
55
|
+
const ldx = ld[0], ldy = ld[1], ldz = ld[2];
|
|
56
|
+
|
|
57
|
+
// Clip the local ray to the shape's local AABB → segment [tEnter, tExit].
|
|
58
|
+
shape.compute_bounding_box(bounds);
|
|
59
|
+
let tEnter = 0, tExit = tMax;
|
|
60
|
+
if (ldx > EPS || ldx < -EPS) {
|
|
61
|
+
const inv = 1 / ldx;
|
|
62
|
+
let t1 = (bounds[0] - lox) * inv, t2 = (bounds[3] - lox) * inv;
|
|
63
|
+
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
64
|
+
if (t1 > tEnter) tEnter = t1;
|
|
65
|
+
if (t2 < tExit) tExit = t2;
|
|
66
|
+
} else if (lox < bounds[0] || lox > bounds[3]) return Infinity;
|
|
67
|
+
if (ldy > EPS || ldy < -EPS) {
|
|
68
|
+
const inv = 1 / ldy;
|
|
69
|
+
let t1 = (bounds[1] - loy) * inv, t2 = (bounds[4] - loy) * inv;
|
|
70
|
+
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
71
|
+
if (t1 > tEnter) tEnter = t1;
|
|
72
|
+
if (t2 < tExit) tExit = t2;
|
|
73
|
+
} else if (loy < bounds[1] || loy > bounds[4]) return Infinity;
|
|
74
|
+
if (ldz > EPS || ldz < -EPS) {
|
|
75
|
+
const inv = 1 / ldz;
|
|
76
|
+
let t1 = (bounds[2] - loz) * inv, t2 = (bounds[5] - loz) * inv;
|
|
77
|
+
if (t1 > t2) { const s = t1; t1 = t2; t2 = s; }
|
|
78
|
+
if (t1 > tEnter) tEnter = t1;
|
|
79
|
+
if (t2 < tExit) tExit = t2;
|
|
80
|
+
} else if (loz < bounds[2] || loz > bounds[5]) return Infinity;
|
|
81
|
+
if (tExit < tEnter) return Infinity; // ray misses the shape's AABB entirely
|
|
82
|
+
|
|
83
|
+
// Query AABB = bbox of the clipped local segment.
|
|
84
|
+
const sax = lox + ldx * tEnter, say = loy + ldy * tEnter, saz = loz + ldz * tEnter;
|
|
85
|
+
const sbx = lox + ldx * tExit, sby = loy + ldy * tExit, sbz = loz + ldz * tExit;
|
|
86
|
+
const qminx = (sax < sbx ? sax : sbx) - AABB_FATTEN;
|
|
87
|
+
const qminy = (say < sby ? say : sby) - AABB_FATTEN;
|
|
88
|
+
const qminz = (saz < sbz ? saz : sbz) - AABB_FATTEN;
|
|
89
|
+
const qmaxx = (sax > sbx ? sax : sbx) + AABB_FATTEN;
|
|
90
|
+
const qmaxy = (say > sby ? say : sby) + AABB_FATTEN;
|
|
91
|
+
const qmaxz = (saz > sbz ? saz : sbz) + AABB_FATTEN;
|
|
92
|
+
|
|
93
|
+
const enumerate = shape.isHeightMapShape3D === true ? heightmap_enumerate_triangles : mesh_enumerate_triangles;
|
|
94
|
+
let cap = (tri_buf.length / TRIANGLE_FLOAT_STRIDE) | 0;
|
|
95
|
+
// The enumerators return the TRUE overlap count, which can EXCEED the buffer
|
|
96
|
+
// capacity (writes past the end are silently dropped). So the grow trigger is
|
|
97
|
+
// `count > cap` (not `=== cap`): regrow whenever the true count overflowed.
|
|
98
|
+
let count = enumerate(tri_buf, 0, shape, qminx, qminy, qminz, qmaxx, qmaxy, qmaxz);
|
|
99
|
+
while (count > cap && cap < MAX_TRIS) {
|
|
100
|
+
cap = cap * 2 < MAX_TRIS ? cap * 2 : MAX_TRIS;
|
|
101
|
+
tri_buf = new Float64Array(cap * TRIANGLE_FLOAT_STRIDE);
|
|
102
|
+
count = enumerate(tri_buf, 0, shape, qminx, qminy, qminz, qmaxx, qmaxy, qmaxz);
|
|
103
|
+
}
|
|
104
|
+
// If the true count still exceeds MAX_TRIS, only `cap` triangles were written;
|
|
105
|
+
// clamp so the consume loop never reads undefined past the buffer (which would
|
|
106
|
+
// poison `best` with NaN). Dropping the overflow is the documented MAX_TRIS bound.
|
|
107
|
+
const n_tris = count < cap ? count : cap;
|
|
108
|
+
|
|
109
|
+
// Two-sided Möller–Trumbore over the candidates; keep the nearest.
|
|
110
|
+
let best = tMax;
|
|
111
|
+
let found = false;
|
|
112
|
+
let nx = 0, ny = 0, nz = 0;
|
|
113
|
+
for (let i = 0; i < n_tris; i++) {
|
|
114
|
+
const o = i * TRIANGLE_FLOAT_STRIDE;
|
|
115
|
+
const ax = tri_buf[o], ay = tri_buf[o + 1], az = tri_buf[o + 2];
|
|
116
|
+
const e1x = tri_buf[o + 3] - ax, e1y = tri_buf[o + 4] - ay, e1z = tri_buf[o + 5] - az;
|
|
117
|
+
const e2x = tri_buf[o + 6] - ax, e2y = tri_buf[o + 7] - ay, e2z = tri_buf[o + 8] - az;
|
|
118
|
+
|
|
119
|
+
const px = ldy * e2z - ldz * e2y, py = ldz * e2x - ldx * e2z, pz = ldx * e2y - ldy * e2x;
|
|
120
|
+
const det = e1x * px + e1y * py + e1z * pz;
|
|
121
|
+
if (det < EPS && det > -EPS) continue; // ray parallel to the triangle
|
|
122
|
+
const invDet = 1 / det;
|
|
123
|
+
|
|
124
|
+
const tvx = lox - ax, tvy = loy - ay, tvz = loz - az;
|
|
125
|
+
const u = (tvx * px + tvy * py + tvz * pz) * invDet;
|
|
126
|
+
if (u < 0 || u > 1) continue;
|
|
127
|
+
|
|
128
|
+
const wx = tvy * e1z - tvz * e1y, wy = tvz * e1x - tvx * e1z, wz = tvx * e1y - tvy * e1x;
|
|
129
|
+
const v = (ldx * wx + ldy * wy + ldz * wz) * invDet;
|
|
130
|
+
if (v < 0 || u + v > 1) continue;
|
|
131
|
+
|
|
132
|
+
const t = (e2x * wx + e2y * wy + e2z * wz) * invDet;
|
|
133
|
+
if (t < 0 || t >= best) continue;
|
|
134
|
+
|
|
135
|
+
best = t;
|
|
136
|
+
found = true;
|
|
137
|
+
// Geometric normal e1 × e2 (local, unnormalised).
|
|
138
|
+
nx = e1y * e2z - e1z * e2y;
|
|
139
|
+
ny = e1z * e2x - e1x * e2z;
|
|
140
|
+
nz = e1x * e2y - e1y * e2x;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!found) return Infinity;
|
|
144
|
+
|
|
145
|
+
// Normalise, orient to face the ray, then rotate the local normal to world.
|
|
146
|
+
let nl = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
147
|
+
if (nl === 0) nl = 1;
|
|
148
|
+
nx /= nl; ny /= nl; nz /= nl;
|
|
149
|
+
if (nx * ldx + ny * ldy + nz * ldz > 0) { nx = -nx; ny = -ny; nz = -nz; }
|
|
150
|
+
v3_quat3_apply(outNormal, 0, nx, ny, nz, qx, qy, qz, qw);
|
|
151
|
+
return best;
|
|
152
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sphere_box_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/sphere_box_contact.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wCAjBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,UACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,
|
|
1
|
+
{"version":3,"file":"sphere_box_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/sphere_box_contact.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wCAjBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,UACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CAuFnB"}
|
|
@@ -1,123 +1,132 @@
|
|
|
1
|
-
import { v3_quat3_apply } from "../../../core/geom/vec3/v3_quat3_apply.js";
|
|
2
|
-
import { v3_quat3_apply_inverse } from "../../../core/geom/vec3/v3_quat3_apply_inverse.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Scratch for the world→box-local rotation of the sphere centre at the
|
|
6
|
-
* top of {@link sphere_box_contact}. 3 floats; allocation-free.
|
|
7
|
-
* @type {Float64Array}
|
|
8
|
-
*/
|
|
9
|
-
const scratch_local = new Float64Array(3);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Closed-form contact generation for a unit sphere vs. an oriented box.
|
|
13
|
-
*
|
|
14
|
-
* Algorithm: transform the sphere centre into the box's body frame, clamp it
|
|
15
|
-
* to the box's half-extents to get the closest surface point in body space,
|
|
16
|
-
* measure the distance, and (when overlapping) rotate the resulting normal
|
|
17
|
-
* and surface point back into world.
|
|
18
|
-
*
|
|
19
|
-
* Singular cases:
|
|
20
|
-
* - If the sphere centre is inside the box (distance == 0 in body frame),
|
|
21
|
-
* the closest-face vector is undefined; we pick the smallest-overlap face
|
|
22
|
-
* direction as a deterministic tie-break.
|
|
23
|
-
*
|
|
24
|
-
* Output convention (mirrors {@link sphere_sphere_contact}): the normal in
|
|
25
|
-
* `out[0..2]` points from the box surface toward the sphere centre; `out[3]`
|
|
26
|
-
* is the (positive) penetration depth; `out[4..6]` is the world-space contact
|
|
27
|
-
* point on the sphere's surface; `out[7..9]` is the world-space contact point
|
|
28
|
-
* on the box's surface.
|
|
29
|
-
*
|
|
30
|
-
* @param {number[]|Float64Array} out length >= 10
|
|
31
|
-
* @param {number} sx sphere centre x
|
|
32
|
-
* @param {number} sy
|
|
33
|
-
* @param {number} sz
|
|
34
|
-
* @param {number} radius
|
|
35
|
-
* @param {number} bx box centre x
|
|
36
|
-
* @param {number} by
|
|
37
|
-
* @param {number} bz
|
|
38
|
-
* @param {number} bqx box rotation quaternion x
|
|
39
|
-
* @param {number} bqy
|
|
40
|
-
* @param {number} bqz
|
|
41
|
-
* @param {number} bqw
|
|
42
|
-
* @param {number} hx box half-extent x (body frame)
|
|
43
|
-
* @param {number} hy
|
|
44
|
-
* @param {number} hz
|
|
45
|
-
* @returns {boolean} true if overlap
|
|
46
|
-
*/
|
|
47
|
-
export function sphere_box_contact(
|
|
48
|
-
out,
|
|
49
|
-
sx, sy, sz, radius,
|
|
50
|
-
bx, by, bz,
|
|
51
|
-
bqx, bqy, bqz, bqw,
|
|
52
|
-
hx, hy, hz
|
|
53
|
-
) {
|
|
54
|
-
// Step 1: bring the sphere centre into box-local space via the conjugate
|
|
55
|
-
// quaternion. v_local = q* · (s - b) · q
|
|
56
|
-
v3_quat3_apply_inverse(scratch_local, 0, sx - bx, sy - by, sz - bz, bqx, bqy, bqz, bqw);
|
|
57
|
-
const lx = scratch_local[0];
|
|
58
|
-
const ly = scratch_local[1];
|
|
59
|
-
const lz = scratch_local[2];
|
|
60
|
-
|
|
61
|
-
// Step 2: closest point on the box to the sphere centre, in body frame.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
nlx = 0; nly = 0; nlz =
|
|
100
|
-
dist = -
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
out[
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
1
|
+
import { v3_quat3_apply } from "../../../core/geom/vec3/v3_quat3_apply.js";
|
|
2
|
+
import { v3_quat3_apply_inverse } from "../../../core/geom/vec3/v3_quat3_apply_inverse.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scratch for the world→box-local rotation of the sphere centre at the
|
|
6
|
+
* top of {@link sphere_box_contact}. 3 floats; allocation-free.
|
|
7
|
+
* @type {Float64Array}
|
|
8
|
+
*/
|
|
9
|
+
const scratch_local = new Float64Array(3);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Closed-form contact generation for a unit sphere vs. an oriented box.
|
|
13
|
+
*
|
|
14
|
+
* Algorithm: transform the sphere centre into the box's body frame, clamp it
|
|
15
|
+
* to the box's half-extents to get the closest surface point in body space,
|
|
16
|
+
* measure the distance, and (when overlapping) rotate the resulting normal
|
|
17
|
+
* and surface point back into world.
|
|
18
|
+
*
|
|
19
|
+
* Singular cases:
|
|
20
|
+
* - If the sphere centre is inside the box (distance == 0 in body frame),
|
|
21
|
+
* the closest-face vector is undefined; we pick the smallest-overlap face
|
|
22
|
+
* direction as a deterministic tie-break.
|
|
23
|
+
*
|
|
24
|
+
* Output convention (mirrors {@link sphere_sphere_contact}): the normal in
|
|
25
|
+
* `out[0..2]` points from the box surface toward the sphere centre; `out[3]`
|
|
26
|
+
* is the (positive) penetration depth; `out[4..6]` is the world-space contact
|
|
27
|
+
* point on the sphere's surface; `out[7..9]` is the world-space contact point
|
|
28
|
+
* on the box's surface.
|
|
29
|
+
*
|
|
30
|
+
* @param {number[]|Float64Array} out length >= 10
|
|
31
|
+
* @param {number} sx sphere centre x
|
|
32
|
+
* @param {number} sy
|
|
33
|
+
* @param {number} sz
|
|
34
|
+
* @param {number} radius
|
|
35
|
+
* @param {number} bx box centre x
|
|
36
|
+
* @param {number} by
|
|
37
|
+
* @param {number} bz
|
|
38
|
+
* @param {number} bqx box rotation quaternion x
|
|
39
|
+
* @param {number} bqy
|
|
40
|
+
* @param {number} bqz
|
|
41
|
+
* @param {number} bqw
|
|
42
|
+
* @param {number} hx box half-extent x (body frame)
|
|
43
|
+
* @param {number} hy
|
|
44
|
+
* @param {number} hz
|
|
45
|
+
* @returns {boolean} true if overlap
|
|
46
|
+
*/
|
|
47
|
+
export function sphere_box_contact(
|
|
48
|
+
out,
|
|
49
|
+
sx, sy, sz, radius,
|
|
50
|
+
bx, by, bz,
|
|
51
|
+
bqx, bqy, bqz, bqw,
|
|
52
|
+
hx, hy, hz
|
|
53
|
+
) {
|
|
54
|
+
// Step 1: bring the sphere centre into box-local space via the conjugate
|
|
55
|
+
// quaternion. v_local = q* · (s - b) · q
|
|
56
|
+
v3_quat3_apply_inverse(scratch_local, 0, sx - bx, sy - by, sz - bz, bqx, bqy, bqz, bqw);
|
|
57
|
+
const lx = scratch_local[0];
|
|
58
|
+
const ly = scratch_local[1];
|
|
59
|
+
const lz = scratch_local[2];
|
|
60
|
+
|
|
61
|
+
// Step 2: closest point on the box to the sphere centre, in body frame.
|
|
62
|
+
// `let` because the interior branch snaps the point onto the chosen face
|
|
63
|
+
// (when the centre is inside the box the clamp leaves it AT the centre).
|
|
64
|
+
let clx = lx < -hx ? -hx : (lx > hx ? hx : lx);
|
|
65
|
+
let cly = ly < -hy ? -hy : (ly > hy ? hy : ly);
|
|
66
|
+
let clz = lz < -hz ? -hz : (lz > hz ? hz : lz);
|
|
67
|
+
|
|
68
|
+
const inside = clx === lx && cly === ly && clz === lz;
|
|
69
|
+
|
|
70
|
+
let nlx, nly, nlz, dist;
|
|
71
|
+
if (!inside) {
|
|
72
|
+
// Centre is outside the box: normal is from clamped point toward centre.
|
|
73
|
+
const ex = lx - clx, ey = ly - cly, ez = lz - clz;
|
|
74
|
+
const dist_sqr = ex * ex + ey * ey + ez * ez;
|
|
75
|
+
if (dist_sqr >= radius * radius) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
dist = Math.sqrt(dist_sqr);
|
|
79
|
+
if (dist > 0) {
|
|
80
|
+
const inv = 1 / dist;
|
|
81
|
+
nlx = ex * inv; nly = ey * inv; nlz = ez * inv;
|
|
82
|
+
} else {
|
|
83
|
+
// Centre lies exactly on a face — pick +X as a tie-break.
|
|
84
|
+
nlx = 1; nly = 0; nlz = 0;
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// Centre is inside the box. Closest face is the one we're nearest to.
|
|
88
|
+
// Compute the per-axis "distance to face" (positive); pick min.
|
|
89
|
+
const dx_face = hx - Math.abs(lx);
|
|
90
|
+
const dy_face = hy - Math.abs(ly);
|
|
91
|
+
const dz_face = hz - Math.abs(lz);
|
|
92
|
+
|
|
93
|
+
// Pick smallest depth-to-face; ties broken X > Y > Z (deterministic).
|
|
94
|
+
// Snap the clamped point onto the chosen face so the world box contact
|
|
95
|
+
// point (out[7..9], written below) lies on the box SURFACE and is
|
|
96
|
+
// consistent with the reported normal/depth — not left at the interior
|
|
97
|
+
// sphere centre (which the clamp produced for an inside centre).
|
|
98
|
+
if (dx_face <= dy_face && dx_face <= dz_face) {
|
|
99
|
+
nlx = lx >= 0 ? 1 : -1; nly = 0; nlz = 0;
|
|
100
|
+
dist = -dx_face;
|
|
101
|
+
clx = lx >= 0 ? hx : -hx;
|
|
102
|
+
} else if (dy_face <= dz_face) {
|
|
103
|
+
nlx = 0; nly = ly >= 0 ? 1 : -1; nlz = 0;
|
|
104
|
+
dist = -dy_face;
|
|
105
|
+
cly = ly >= 0 ? hy : -hy;
|
|
106
|
+
} else {
|
|
107
|
+
nlx = 0; nly = 0; nlz = lz >= 0 ? 1 : -1;
|
|
108
|
+
dist = -dz_face;
|
|
109
|
+
clz = lz >= 0 ? hz : -hz;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Step 3: rotate normal and surface point back to world. v_world = q · v · q*
|
|
114
|
+
|
|
115
|
+
// World normal (box → sphere).
|
|
116
|
+
v3_quat3_apply(out, 0, nlx, nly, nlz, bqx, bqy, bqz, bqw);
|
|
117
|
+
const nx = out[0], ny = out[1], nz = out[2];
|
|
118
|
+
out[3] = radius - dist;
|
|
119
|
+
|
|
120
|
+
// World contact on sphere = sphere_center - normal * radius.
|
|
121
|
+
out[4] = sx - nx * radius;
|
|
122
|
+
out[5] = sy - ny * radius;
|
|
123
|
+
out[6] = sz - nz * radius;
|
|
124
|
+
|
|
125
|
+
// World contact on box: rotate the local-space clamped point to world, plus box centre.
|
|
126
|
+
v3_quat3_apply(out, 7, clx, cly, clz, bqx, bqy, bqz, bqw);
|
|
127
|
+
out[7] += bx;
|
|
128
|
+
out[8] += by;
|
|
129
|
+
out[9] += bz;
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlap_shape.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/overlap_shape.js"],"names":[],"mappings":"AA8CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,uFAZW;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,YAE5B;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,UAErC,WAAW,GAAC,MAAM,EAAE,iBAEpB,MAAM,oBACE,MAAM,yBAAsB,OAAO,GAEzC,MAAM,
|
|
1
|
+
{"version":3,"file":"overlap_shape.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/overlap_shape.js"],"names":[],"mappings":"AA8CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,uFAZW;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,YAE5B;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,UAErC,WAAW,GAAC,MAAM,EAAE,iBAEpB,MAAM,oBACE,MAAM,yBAAsB,OAAO,GAEzC,MAAM,CAoGlB"}
|
|
@@ -100,13 +100,19 @@ export function overlap_shape(system, shape, position, rotation, output, output_
|
|
|
100
100
|
);
|
|
101
101
|
|
|
102
102
|
// ── 2. Gather broadphase candidates ─────────────────────────────
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
// The BVH query returns the TRUE overlap count and silently drops writes past
|
|
104
|
+
// the buffer end, so an undersized buffer would leave the read loop pulling
|
|
105
|
+
// `undefined` (garbage body ids) and silently MISS real overlaps. Grow by
|
|
106
|
+
// doubling and re-query until both static + dynamic fit (queries are
|
|
107
|
+
// deterministic, so this converges in one resize).
|
|
108
|
+
let n_static, n_dynamic, n_total;
|
|
109
|
+
for (;;) {
|
|
110
|
+
n_static = bvh_query_user_data_overlaps_aabb(scratch_candidates, 0, system.staticBvh, world_aabb);
|
|
111
|
+
n_dynamic = bvh_query_user_data_overlaps_aabb(scratch_candidates, n_static, system.dynamicBvh, world_aabb);
|
|
112
|
+
n_total = n_static + n_dynamic;
|
|
113
|
+
if (n_total <= scratch_candidates.length) break;
|
|
114
|
+
scratch_candidates = new Uint32Array(Math.max(n_total, scratch_candidates.length * 2));
|
|
115
|
+
}
|
|
110
116
|
if (n_total === 0) return 0;
|
|
111
117
|
|
|
112
118
|
// ── 3. Set up query PosedShape (constant across candidates) ─────
|
|
@@ -154,16 +160,9 @@ export function overlap_shape(system, shape, position, rotation, output, output_
|
|
|
154
160
|
concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
|
|
155
161
|
);
|
|
156
162
|
|
|
157
|
-
// Re-pose candidate as the concave body
|
|
158
|
-
//
|
|
159
|
-
candidate_posed.
|
|
160
|
-
candidate_posed.px = candidate_tr.position.x;
|
|
161
|
-
candidate_posed.py = candidate_tr.position.y;
|
|
162
|
-
candidate_posed.pz = candidate_tr.position.z;
|
|
163
|
-
candidate_posed.qx = candidate_tr.rotation.x;
|
|
164
|
-
candidate_posed.qy = candidate_tr.rotation.y;
|
|
165
|
-
candidate_posed.qz = candidate_tr.rotation.z;
|
|
166
|
-
candidate_posed.qw = candidate_tr.rotation.w;
|
|
163
|
+
// Re-pose candidate as the concave body (the flyweight triangle is
|
|
164
|
+
// rebound per iteration below; setup only stores the shape reference).
|
|
165
|
+
candidate_posed.setup(triangle_shape, candidate_tr.position, candidate_tr.rotation);
|
|
167
166
|
|
|
168
167
|
for (let t = 0; t < tri_count; t++) {
|
|
169
168
|
triangle_shape.bind(triangle_buffer, t * TRIANGLE_FLOAT_STRIDE);
|