@woosh/meep-engine 2.139.0 → 2.140.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/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts +3 -3
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js +4 -4
- package/src/{engine/physics/broadphase/aabb_transform_oriented.d.ts → core/geom/3d/aabb/aabb3_transform_oriented.d.ts} +2 -2
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
- package/src/{engine/physics/broadphase/aabb_transform_oriented.js → core/geom/3d/aabb/aabb3_transform_oriented.js} +1 -1
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +54 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
- package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
- package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/Triangle3D.js +318 -0
- package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +9 -11
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +40 -18
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +9 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +38 -10
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +14 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +47 -5
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +75 -13
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
- package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_negate_array.js +2 -2
- package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
- package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +16 -3
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -211
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +72 -8
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +37 -5
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +101 -3
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -1416
- package/src/engine/control/first-person/TODO.md +173 -127
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +9 -1
- package/src/engine/control/first-person/prototype_first_person_controller.js +88 -2
- package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -1
- package/src/engine/control/first-person/test/buildTestPlayer.js +9 -1
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
- package/src/engine/physics/BULLET_REVIEW.md +945 -0
- package/src/engine/physics/CANNON_REVIEW.md +1300 -0
- package/src/engine/physics/JOLT_REVIEW.md +913 -0
- package/src/engine/physics/PLAN.md +461 -236
- package/src/engine/physics/RAPIER_REVIEW.md +934 -0
- package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +2 -2
- package/src/engine/physics/contact/ManifoldStore.d.ts +83 -10
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +608 -499
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +2 -2
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +128 -20
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +1301 -1159
- package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidSimulator.js +2 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +28 -6
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +39 -17
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +68 -22
- package/src/engine/physics/gjk/gjk.d.ts +28 -2
- package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
- package/src/engine/physics/gjk/gjk.js +421 -378
- package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
- package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
- package/src/engine/physics/gjk/minkowski_support.js +75 -0
- package/src/engine/physics/gjk/mpr.d.ts +56 -0
- package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
- package/src/engine/physics/gjk/mpr.js +344 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +20 -5
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +36 -38
- package/src/engine/physics/integration/integrate_position.d.ts +25 -7
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +43 -12
- package/src/engine/physics/integration/integrate_velocity.d.ts +30 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +82 -1
- package/src/engine/physics/narrowphase/PosedShape.d.ts +0 -8
- package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/PosedShape.js +28 -30
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +113 -17
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +10 -56
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +8 -2
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +1422 -382
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +16 -23
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
- package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
- package/src/engine/physics/queries/overlap_shape.js +183 -0
- package/src/engine/physics/queries/shape_cast.d.ts +56 -0
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
- package/src/engine/physics/queries/shape_cast.js +387 -0
- package/src/engine/physics/solver/solve_contacts.d.ts +116 -30
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +641 -223
- package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts +0 -20
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +0 -83
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { assert } from "../../../assert.js";
|
|
2
|
+
import { clamp } from "../../../math/clamp.js";
|
|
3
|
+
import { v3_length } from "../../vec3/v3_length.js";
|
|
4
|
+
import { Vector3 } from "../../Vector3.js";
|
|
5
|
+
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Heightmap shape, intended primarily for terrain.
|
|
9
|
+
*
|
|
10
|
+
* The shape is a closed solid bounded below by the plane perpendicular to
|
|
11
|
+
* {@link orientation} (the "floor") and above by a height-field surface
|
|
12
|
+
* defined by a {@link Sampler2D}. Heights are sampled with Catmull-Rom
|
|
13
|
+
* filtering ({@link Sampler2D#sampleChannelCatmullRomUV}), matching what
|
|
14
|
+
* the terrain system uses for geometry construction.
|
|
15
|
+
*
|
|
16
|
+
* Local frame layout:
|
|
17
|
+
* - The orientation vector defines the local "up" axis (unit).
|
|
18
|
+
* - An orthonormal basis (u, v, n=orientation) is built from it.
|
|
19
|
+
* - Footprint extends along the basis-u axis over [-size.x/2, +size.x/2]
|
|
20
|
+
* and along the basis-v axis over [-size.z/2, +size.z/2].
|
|
21
|
+
* - The surface height (along orientation) at heightmap-UV (u01, v01) is
|
|
22
|
+
* `sampler.sampleChannelCatmullRomUV(u01, v01, 0)`.
|
|
23
|
+
* - The solid volume occupies `h ∈ [0, sampledHeight(u01, v01)]` along the
|
|
24
|
+
* orientation axis, with `0` being the floor at body-local origin.
|
|
25
|
+
* - `size.y` is the maximum height value of the heightfield (used for the
|
|
26
|
+
* bounding box). Sampler values are NOT clamped to it.
|
|
27
|
+
*
|
|
28
|
+
* NON-CONVEX. {@link support} throws — GJK/EPA cannot be run against a
|
|
29
|
+
* heightmap directly. The physics narrowphase must dispatch a dedicated
|
|
30
|
+
* grid-traversal path when one of the colliders is a heightmap.
|
|
31
|
+
*
|
|
32
|
+
* @author Alex Goldring
|
|
33
|
+
* @copyright Company Named Limited (c) 2026
|
|
34
|
+
*/
|
|
35
|
+
export class HeightMapShape3D extends AbstractShape3D {
|
|
36
|
+
constructor() {
|
|
37
|
+
super();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Unit vector defining the local "up" axis (the direction the
|
|
41
|
+
* heightmap's surface faces). Default is +Y.
|
|
42
|
+
* @readonly
|
|
43
|
+
* @type {Vector3}
|
|
44
|
+
*/
|
|
45
|
+
this.orientation = new Vector3(0, 1, 0);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Bounding-box extents in the heightmap-local (u, height, v) frame.
|
|
49
|
+
* size.x — footprint extent along basis-u (perpendicular to orientation)
|
|
50
|
+
* size.y — maximum heightmap height (extent along orientation)
|
|
51
|
+
* size.z — footprint extent along basis-v (perpendicular to orientation)
|
|
52
|
+
* @readonly
|
|
53
|
+
* @type {Vector3}
|
|
54
|
+
*/
|
|
55
|
+
this.size = new Vector3(1, 1, 1);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sampler holding height values. Float32 backing recommended so the
|
|
59
|
+
* Terrain system's height texture plugs in directly. Single-channel
|
|
60
|
+
* sampler is the common case; only channel 0 is read.
|
|
61
|
+
* @type {Sampler2D | null}
|
|
62
|
+
*/
|
|
63
|
+
this.sampler = null;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Cached orthonormal basis [u_x,u_y,u_z, v_x,v_y,v_z, n_x,n_y,n_z]
|
|
67
|
+
* built from {@link orientation}. Updated lazily by {@link _ensure_basis}.
|
|
68
|
+
* @private
|
|
69
|
+
* @type {Float64Array}
|
|
70
|
+
*/
|
|
71
|
+
this._basis = new Float64Array(9);
|
|
72
|
+
|
|
73
|
+
// last-seen orientation components, used to detect dirty basis
|
|
74
|
+
this._basis_orientation_x = NaN;
|
|
75
|
+
this._basis_orientation_y = NaN;
|
|
76
|
+
this._basis_orientation_z = NaN;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Convenience constructor.
|
|
81
|
+
* @param {Sampler2D} sampler
|
|
82
|
+
* @param {number} size_x footprint extent along basis-u
|
|
83
|
+
* @param {number} size_y maximum heightmap height (along orientation)
|
|
84
|
+
* @param {number} size_z footprint extent along basis-v
|
|
85
|
+
* @param {Vector3} [orientation] defaults to +Y
|
|
86
|
+
* @returns {HeightMapShape3D}
|
|
87
|
+
*/
|
|
88
|
+
static from(sampler, size_x, size_y, size_z, orientation) {
|
|
89
|
+
assert.isNumber(size_x, "size_x");
|
|
90
|
+
assert.isNumber(size_y, "size_y");
|
|
91
|
+
assert.isNumber(size_z, "size_z");
|
|
92
|
+
assert.greaterThanOrEqual(size_x, 0, "size_x");
|
|
93
|
+
assert.greaterThanOrEqual(size_y, 0, "size_y");
|
|
94
|
+
assert.greaterThanOrEqual(size_z, 0, "size_z");
|
|
95
|
+
|
|
96
|
+
const r = new HeightMapShape3D();
|
|
97
|
+
r.sampler = sampler;
|
|
98
|
+
r.size.set(size_x, size_y, size_z);
|
|
99
|
+
|
|
100
|
+
if (orientation !== undefined) {
|
|
101
|
+
r.orientation.set(orientation.x, orientation.y, orientation.z);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return r;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Recompute the orthonormal basis (u, v, n) if {@link orientation} changed.
|
|
109
|
+
*
|
|
110
|
+
* Construction chosen so that the default orientation +Y produces the
|
|
111
|
+
* intuitive mapping `u = +X, v = +Z, n = +Y` — i.e. size.x runs along
|
|
112
|
+
* body X, size.z along body Z. We project body +X onto the plane
|
|
113
|
+
* perpendicular to n (the orientation), with a fall-back to projecting
|
|
114
|
+
* body +Z when n is too close to colinear with +X.
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
_ensure_basis() {
|
|
118
|
+
const nx = this.orientation[0];
|
|
119
|
+
const ny = this.orientation[1];
|
|
120
|
+
const nz = this.orientation[2];
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
nx === this._basis_orientation_x
|
|
124
|
+
&& ny === this._basis_orientation_y
|
|
125
|
+
&& nz === this._basis_orientation_z
|
|
126
|
+
) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// tangent u = normalize(project body +X onto plane perpendicular to n)
|
|
131
|
+
// fall back to body +Z if n is too colinear with +X
|
|
132
|
+
let u_x, u_y, u_z;
|
|
133
|
+
|
|
134
|
+
if (Math.abs(nx) < 0.9) {
|
|
135
|
+
// u = (+X) - (n . +X) * n = (1 - nx*nx, -nx*ny, -nx*nz)
|
|
136
|
+
u_x = 1 - nx * nx;
|
|
137
|
+
u_y = -nx * ny;
|
|
138
|
+
u_z = -nx * nz;
|
|
139
|
+
} else {
|
|
140
|
+
// u = (+Z) - (n . +Z) * n = (-nz*nx, -nz*ny, 1 - nz*nz)
|
|
141
|
+
u_x = -nz * nx;
|
|
142
|
+
u_y = -nz * ny;
|
|
143
|
+
u_z = 1 - nz * nz;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const u_inv = 1 / v3_length(u_x, u_y, u_z);
|
|
147
|
+
u_x *= u_inv;
|
|
148
|
+
u_y *= u_inv;
|
|
149
|
+
u_z *= u_inv;
|
|
150
|
+
|
|
151
|
+
// v = u × n (for n=+Y, u=+X this gives v=+Z, the intuitive choice)
|
|
152
|
+
const v_x = u_y * nz - u_z * ny;
|
|
153
|
+
const v_y = u_z * nx - u_x * nz;
|
|
154
|
+
const v_z = u_x * ny - u_y * nx;
|
|
155
|
+
|
|
156
|
+
const b = this._basis;
|
|
157
|
+
b[0] = u_x; b[1] = u_y; b[2] = u_z;
|
|
158
|
+
b[3] = v_x; b[4] = v_y; b[5] = v_z;
|
|
159
|
+
b[6] = nx; b[7] = ny; b[8] = nz;
|
|
160
|
+
|
|
161
|
+
this._basis_orientation_x = nx;
|
|
162
|
+
this._basis_orientation_y = ny;
|
|
163
|
+
this._basis_orientation_z = nz;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Sample the surface height at heightmap UV coordinates.
|
|
168
|
+
* Uses Catmull-Rom filtering to match the terrain system's geometry construction.
|
|
169
|
+
* @param {number} u01 horizontal UV, in [0, 1]
|
|
170
|
+
* @param {number} v01 vertical UV, in [0, 1]
|
|
171
|
+
* @returns {number} height along orientation axis
|
|
172
|
+
*/
|
|
173
|
+
sample_height_at_uv(u01, v01) {
|
|
174
|
+
return this.sampler.sampleChannelCatmullRomUV(u01, v01, 0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Project a body-local position to the surface and sample the height there.
|
|
179
|
+
* The returned height is in the orientation-axis direction.
|
|
180
|
+
* Positions outside the footprint sample the clamped UV (sampler clamps).
|
|
181
|
+
* @param {number} px body-local x
|
|
182
|
+
* @param {number} py body-local y
|
|
183
|
+
* @param {number} pz body-local z
|
|
184
|
+
* @returns {number}
|
|
185
|
+
*/
|
|
186
|
+
sample_height_at_position(px, py, pz) {
|
|
187
|
+
this._ensure_basis();
|
|
188
|
+
|
|
189
|
+
const b = this._basis;
|
|
190
|
+
|
|
191
|
+
// project onto basis u and basis v
|
|
192
|
+
const u_coord = b[0] * px + b[1] * py + b[2] * pz;
|
|
193
|
+
const v_coord = b[3] * px + b[4] * py + b[5] * pz;
|
|
194
|
+
|
|
195
|
+
const u01 = u_coord / this.size[0] + 0.5;
|
|
196
|
+
const v01 = v_coord / this.size[2] + 0.5;
|
|
197
|
+
|
|
198
|
+
return this.sample_height_at_uv(u01, v01);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
compute_bounding_box(result) {
|
|
202
|
+
this._ensure_basis();
|
|
203
|
+
|
|
204
|
+
const b = this._basis;
|
|
205
|
+
|
|
206
|
+
const sx = this.size[0];
|
|
207
|
+
const sy = this.size[1];
|
|
208
|
+
const sz = this.size[2];
|
|
209
|
+
|
|
210
|
+
const half_u = sx * 0.5;
|
|
211
|
+
const half_v = sz * 0.5;
|
|
212
|
+
|
|
213
|
+
// For each body axis k (0=x, 1=y, 2=z):
|
|
214
|
+
// body[k] = b[0+k]*u + b[3+k]*v + b[6+k]*h
|
|
215
|
+
//
|
|
216
|
+
// The (u,v) footprint contribution is symmetric (u ∈ [-half_u, +half_u], v ∈ [-half_v, +half_v])
|
|
217
|
+
// The height contribution is asymmetric (h ∈ [0, sy])
|
|
218
|
+
for (let k = 0; k < 3; k++) {
|
|
219
|
+
const cu = b[k];
|
|
220
|
+
const cv = b[3 + k];
|
|
221
|
+
const ch = b[6 + k];
|
|
222
|
+
|
|
223
|
+
const uv_extent = Math.abs(cu) * half_u + Math.abs(cv) * half_v;
|
|
224
|
+
|
|
225
|
+
const h_lo = ch < 0 ? ch * sy : 0;
|
|
226
|
+
const h_hi = ch > 0 ? ch * sy : 0;
|
|
227
|
+
|
|
228
|
+
result[k] = -uv_extent + h_lo;
|
|
229
|
+
result[k + 3] = uv_extent + h_hi;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
contains_point(point) {
|
|
234
|
+
const px = point[0];
|
|
235
|
+
const py = point[1];
|
|
236
|
+
const pz = point[2];
|
|
237
|
+
|
|
238
|
+
this._ensure_basis();
|
|
239
|
+
|
|
240
|
+
const b = this._basis;
|
|
241
|
+
|
|
242
|
+
// project into heightmap-local frame (u, v, h)
|
|
243
|
+
const u_coord = b[0] * px + b[1] * py + b[2] * pz;
|
|
244
|
+
const v_coord = b[3] * px + b[4] * py + b[5] * pz;
|
|
245
|
+
const h_coord = b[6] * px + b[7] * py + b[8] * pz;
|
|
246
|
+
|
|
247
|
+
const half_u = this.size[0] * 0.5;
|
|
248
|
+
const half_v = this.size[2] * 0.5;
|
|
249
|
+
|
|
250
|
+
if (u_coord <= -half_u || u_coord >= half_u) return false;
|
|
251
|
+
if (v_coord <= -half_v || v_coord >= half_v) return false;
|
|
252
|
+
if (h_coord <= 0 || h_coord >= this.size[1]) return false;
|
|
253
|
+
|
|
254
|
+
const u01 = u_coord / this.size[0] + 0.5;
|
|
255
|
+
const v01 = v_coord / this.size[2] + 0.5;
|
|
256
|
+
|
|
257
|
+
const surface_h = this.sample_height_at_uv(u01, v01);
|
|
258
|
+
|
|
259
|
+
return h_coord < surface_h;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Approximate signed distance: difference between the point's height
|
|
264
|
+
* (along orientation) and the surface height sampled at the point's
|
|
265
|
+
* footprint UV. POSITIVE = above surface, NEGATIVE = below.
|
|
266
|
+
*
|
|
267
|
+
* Locally correct when the surface is flat; biased when the surface
|
|
268
|
+
* has significant slope. Sufficient for cling/raycast queries that
|
|
269
|
+
* walk down to the surface.
|
|
270
|
+
*/
|
|
271
|
+
signed_distance_at_point(point) {
|
|
272
|
+
const px = point[0];
|
|
273
|
+
const py = point[1];
|
|
274
|
+
const pz = point[2];
|
|
275
|
+
|
|
276
|
+
this._ensure_basis();
|
|
277
|
+
|
|
278
|
+
const b = this._basis;
|
|
279
|
+
|
|
280
|
+
const u_coord = b[0] * px + b[1] * py + b[2] * pz;
|
|
281
|
+
const v_coord = b[3] * px + b[4] * py + b[5] * pz;
|
|
282
|
+
const h_coord = b[6] * px + b[7] * py + b[8] * pz;
|
|
283
|
+
|
|
284
|
+
const u01 = u_coord / this.size[0] + 0.5;
|
|
285
|
+
const v01 = v_coord / this.size[2] + 0.5;
|
|
286
|
+
|
|
287
|
+
const surface_h = this.sample_height_at_uv(u01, v01);
|
|
288
|
+
|
|
289
|
+
return h_coord - surface_h;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Project a reference point onto the surface along the orientation axis.
|
|
294
|
+
* The footprint UV is clamped, so points outside the footprint produce
|
|
295
|
+
* the nearest edge-of-footprint surface sample (approximate).
|
|
296
|
+
*/
|
|
297
|
+
nearest_point_on_surface(result, reference) {
|
|
298
|
+
const rx = reference[0];
|
|
299
|
+
const ry = reference[1];
|
|
300
|
+
const rz = reference[2];
|
|
301
|
+
|
|
302
|
+
this._ensure_basis();
|
|
303
|
+
|
|
304
|
+
const b = this._basis;
|
|
305
|
+
|
|
306
|
+
const u_coord = b[0] * rx + b[1] * ry + b[2] * rz;
|
|
307
|
+
const v_coord = b[3] * rx + b[4] * ry + b[5] * rz;
|
|
308
|
+
|
|
309
|
+
const half_u = this.size[0] * 0.5;
|
|
310
|
+
const half_v = this.size[2] * 0.5;
|
|
311
|
+
|
|
312
|
+
const u_clamped = clamp(u_coord, -half_u, half_u);
|
|
313
|
+
const v_clamped = clamp(v_coord, -half_v, half_v);
|
|
314
|
+
|
|
315
|
+
const u01 = u_clamped / this.size[0] + 0.5;
|
|
316
|
+
const v01 = v_clamped / this.size[2] + 0.5;
|
|
317
|
+
|
|
318
|
+
const surface_h = this.sample_height_at_uv(u01, v01);
|
|
319
|
+
|
|
320
|
+
// compose body-local point: u_axis*u_clamped + v_axis*v_clamped + n_axis*surface_h
|
|
321
|
+
result[0] = b[0] * u_clamped + b[3] * v_clamped + b[6] * surface_h;
|
|
322
|
+
result[1] = b[1] * u_clamped + b[4] * v_clamped + b[7] * surface_h;
|
|
323
|
+
result[2] = b[2] * u_clamped + b[5] * v_clamped + b[8] * surface_h;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Heightmaps are non-convex; GJK/EPA cannot work against them directly.
|
|
328
|
+
* The physics narrowphase must dispatch a grid-traversal path that
|
|
329
|
+
* decomposes the heightmap into per-cell triangle pairs and tests each
|
|
330
|
+
* against the other shape (analogous to Bullet's btHeightfieldTerrainShape
|
|
331
|
+
* × btConcaveShape interface).
|
|
332
|
+
*
|
|
333
|
+
* This throws rather than returning a degenerate result so the call
|
|
334
|
+
* site is forced to handle heightmaps explicitly.
|
|
335
|
+
*/
|
|
336
|
+
support(result, result_offset, direction_x, direction_y, direction_z) {
|
|
337
|
+
throw new Error("HeightMapShape3D.support: heightmaps are non-convex; the narrowphase must dispatch grid-traversal instead.");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
sample_random_point_in_volume(result, result_offset, random) {
|
|
341
|
+
const u01 = random();
|
|
342
|
+
const v01 = random();
|
|
343
|
+
|
|
344
|
+
const u_coord = (u01 - 0.5) * this.size[0];
|
|
345
|
+
const v_coord = (v01 - 0.5) * this.size[2];
|
|
346
|
+
|
|
347
|
+
const surface_h = this.sample_height_at_uv(u01, v01);
|
|
348
|
+
const h_coord = random() * surface_h;
|
|
349
|
+
|
|
350
|
+
this._ensure_basis();
|
|
351
|
+
|
|
352
|
+
const b = this._basis;
|
|
353
|
+
|
|
354
|
+
result[result_offset] = b[0] * u_coord + b[3] * v_coord + b[6] * h_coord;
|
|
355
|
+
result[result_offset + 1] = b[1] * u_coord + b[4] * v_coord + b[7] * h_coord;
|
|
356
|
+
result[result_offset + 2] = b[2] * u_coord + b[5] * v_coord + b[8] * h_coord;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Sum of sampler heights × per-cell footprint area. This is the
|
|
361
|
+
* piecewise-constant approximation of the integral ∫h(u,v) dA over
|
|
362
|
+
* the footprint — exact when h is constant per cell, biased when
|
|
363
|
+
* h is smooth.
|
|
364
|
+
*/
|
|
365
|
+
get volume() {
|
|
366
|
+
const sampler = this.sampler;
|
|
367
|
+
|
|
368
|
+
if (sampler === null) {
|
|
369
|
+
return 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const w = sampler.width;
|
|
373
|
+
const h = sampler.height;
|
|
374
|
+
|
|
375
|
+
if (w === 0 || h === 0) {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const cell_area = (this.size[0] / w) * (this.size[2] / h);
|
|
380
|
+
|
|
381
|
+
let total = 0;
|
|
382
|
+
|
|
383
|
+
for (let y = 0; y < h; y++) {
|
|
384
|
+
for (let x = 0; x < w; x++) {
|
|
385
|
+
total += sampler.readChannel(x, y, 0);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return total * cell_area;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Returns just the footprint area. A true heightmap surface area
|
|
394
|
+
* requires integrating sqrt(1 + (∂h/∂u)² + (∂h/∂v)²) over the grid;
|
|
395
|
+
* the footprint area is a lower bound and is sufficient for the
|
|
396
|
+
* physics inertia-tensor seam (heightmaps are static anyway).
|
|
397
|
+
*/
|
|
398
|
+
get surface_area() {
|
|
399
|
+
return this.size[0] * this.size[2];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* @param {HeightMapShape3D} other
|
|
404
|
+
* @returns {boolean}
|
|
405
|
+
*/
|
|
406
|
+
equals(other) {
|
|
407
|
+
if (!super.equals(other)) return false;
|
|
408
|
+
|
|
409
|
+
if (!this.orientation.equals(other.orientation)) return false;
|
|
410
|
+
if (!this.size.equals(other.size)) return false;
|
|
411
|
+
|
|
412
|
+
// strict identity is enough for sampler equality in the common case;
|
|
413
|
+
// fall through to value equality so two shapes built from independent
|
|
414
|
+
// but identical sampler instances still compare equal
|
|
415
|
+
if (this.sampler === other.sampler) return true;
|
|
416
|
+
|
|
417
|
+
if (this.sampler === null || other.sampler === null) return false;
|
|
418
|
+
|
|
419
|
+
return this.sampler.equals(other.sampler);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
hash() {
|
|
423
|
+
const a = this.orientation.hash();
|
|
424
|
+
const b = this.size.hash();
|
|
425
|
+
const c = this.sampler !== null ? this.sampler.hash() : 0;
|
|
426
|
+
|
|
427
|
+
let h = (a * 31 + b) | 0;
|
|
428
|
+
h = (h * 31 + c) | 0;
|
|
429
|
+
|
|
430
|
+
return h;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Fast type-check marker, matching the pattern on every other concrete
|
|
436
|
+
* AbstractShape3D subclass. The physics narrowphase reads this to dispatch
|
|
437
|
+
* the heightmap-vs-X grid-traversal path.
|
|
438
|
+
* @readonly
|
|
439
|
+
* @type {boolean}
|
|
440
|
+
*/
|
|
441
|
+
HeightMapShape3D.prototype.isHeightMapShape3D = true;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Heightmaps are non-convex: the solid volume bounded by an arbitrary
|
|
445
|
+
* height-field has valleys and overhangs that break GJK's convex-Minkowski
|
|
446
|
+
* precondition. The narrowphase must use grid traversal + per-triangle
|
|
447
|
+
* GJK instead of feeding this shape's {@link support} into pair tests.
|
|
448
|
+
* @readonly
|
|
449
|
+
* @type {boolean}
|
|
450
|
+
*/
|
|
451
|
+
HeightMapShape3D.prototype.is_convex = false;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arbitrary triangle-mesh collider.
|
|
3
|
+
*
|
|
4
|
+
* The shape carries two synchronized representations:
|
|
5
|
+
* - flat (`positions`, `indices`) surface — used by GJK's support
|
|
6
|
+
* function and the per-triangle distance queries;
|
|
7
|
+
* - a {@link TetrahedralMesh} of the *interior* (built from the surface
|
|
8
|
+
* by `compute_tetrahedral_mesh_from_surface`) plus a {@link BVH}
|
|
9
|
+
* keyed by tet AABB — used by `contains_point` and any future
|
|
10
|
+
* volume-aware query.
|
|
11
|
+
*
|
|
12
|
+
* The tet decomposition gives us convex primitives. Two consequences:
|
|
13
|
+
* 1. `contains_point` is a point-in-AABB BVH query followed by a
|
|
14
|
+
* per-tet `orient3d` test. Critically this works for **disconnected
|
|
15
|
+
* meshes** (two cubes welded into one buffer; a torus and a sphere
|
|
16
|
+
* handed to the same shape). A walking query started from one
|
|
17
|
+
* component can't reach the others — that was the previous
|
|
18
|
+
* implementation's silent bug.
|
|
19
|
+
* 2. Per-point queries are O(log N_tets) on average — the BVH's
|
|
20
|
+
* traversal short-circuits cleanly when the query point is far
|
|
21
|
+
* from the mesh.
|
|
22
|
+
*
|
|
23
|
+
* Construct via {@link shape_mesh_from_geometry} rather than `new
|
|
24
|
+
* MeshShape3D()` directly — the factory compacts the tet mesh, builds
|
|
25
|
+
* the BVH, and caches the bbox / volume / surface area.
|
|
26
|
+
*
|
|
27
|
+
* Narrowphase routing:
|
|
28
|
+
* - GJK + EPA for the general case, using {@link support}.
|
|
29
|
+
* - The support function currently returns the deepest tet-mesh
|
|
30
|
+
* vertex, which gives GJK the *convex hull* of the mesh — not the
|
|
31
|
+
* true non-convex surface. For convex authored geometry that's
|
|
32
|
+
* fine; for highly concave shapes (a torus's hole, an L-bracket,
|
|
33
|
+
* two disconnected components) the convex-hull approximation
|
|
34
|
+
* overstates the collision volume. The accurate fix is a per-tet
|
|
35
|
+
* GJK loop in the narrowphase, which would consume `tet_mesh` and
|
|
36
|
+
* `tet_positions` directly — see PLAN.md's mesh-vs-convex
|
|
37
|
+
* closed-form item.
|
|
38
|
+
*
|
|
39
|
+
* @author Alex Goldring
|
|
40
|
+
* @copyright Company Named Limited (c) 2026
|
|
41
|
+
*/
|
|
42
|
+
export class MeshShape3D extends AbstractShape3D {
|
|
43
|
+
/**
|
|
44
|
+
* Surface vertex positions, flat `(x, y, z)` per vertex. Authored
|
|
45
|
+
* by the factory; mutate at your own risk (the cached bbox /
|
|
46
|
+
* volume / surface area / BVH will go stale).
|
|
47
|
+
* @type {Float32Array}
|
|
48
|
+
*/
|
|
49
|
+
positions: Float32Array;
|
|
50
|
+
/**
|
|
51
|
+
* Surface triangle indices, three uint32 per face referring to
|
|
52
|
+
* {@link positions}. Same mutate-at-your-own-risk caveat.
|
|
53
|
+
* @type {Uint32Array}
|
|
54
|
+
*/
|
|
55
|
+
indices: Uint32Array;
|
|
56
|
+
/**
|
|
57
|
+
* Tetrahedral decomposition of the interior. The factory
|
|
58
|
+
* compacts the mesh before storing (low-index slots that the
|
|
59
|
+
* carve pass freed get refilled), so iteration via
|
|
60
|
+
* `tet_id in [0, tet_mesh.count)` is safe — no need for
|
|
61
|
+
* `forEach` or `exists` filtering on the hot path.
|
|
62
|
+
* @type {TetrahedralMesh}
|
|
63
|
+
*/
|
|
64
|
+
tet_mesh: TetrahedralMesh;
|
|
65
|
+
/**
|
|
66
|
+
* Flat positions array for {@link tet_mesh}. Includes the
|
|
67
|
+
* surface vertices and any hole-closing centroids added during
|
|
68
|
+
* tetrahedralisation. For closed airtight meshes this equals
|
|
69
|
+
* {@link positions} element-wise; for meshes with boundary holes
|
|
70
|
+
* (rare in authored content, common in mis-exported assets) the
|
|
71
|
+
* trailing slots carry the closure centroids.
|
|
72
|
+
* @type {Float32Array}
|
|
73
|
+
*/
|
|
74
|
+
tet_positions: Float32Array;
|
|
75
|
+
/**
|
|
76
|
+
* BVH over the tet mesh, keyed by tet index. Built by the
|
|
77
|
+
* factory after compacting the tet mesh, so leaf user_data
|
|
78
|
+
* values are valid tet indices that can be passed straight to
|
|
79
|
+
* `tet_mesh.getVertexIndex`.
|
|
80
|
+
* @type {BVH}
|
|
81
|
+
*/
|
|
82
|
+
tet_bvh: BVH;
|
|
83
|
+
/**
|
|
84
|
+
* Cached axis-aligned bounding box, layout `[minX, minY, minZ,
|
|
85
|
+
* maxX, maxY, maxZ]`.
|
|
86
|
+
* @private
|
|
87
|
+
* @type {Float64Array}
|
|
88
|
+
*/
|
|
89
|
+
private __bbox;
|
|
90
|
+
/**
|
|
91
|
+
* Cached total volume in the shape's local frame, computed from
|
|
92
|
+
* the tetrahedral decomposition at construction.
|
|
93
|
+
* @private
|
|
94
|
+
* @type {number}
|
|
95
|
+
*/
|
|
96
|
+
private __volume;
|
|
97
|
+
/**
|
|
98
|
+
* Cached total surface area, sum of triangle areas.
|
|
99
|
+
* @private
|
|
100
|
+
* @type {number}
|
|
101
|
+
*/
|
|
102
|
+
private __surface_area;
|
|
103
|
+
compute_bounding_box(result: any): void;
|
|
104
|
+
/**
|
|
105
|
+
* Refresh the cached bounding box, volume, and surface area from the
|
|
106
|
+
* current `positions` / `indices` / `tet_mesh`. Called by
|
|
107
|
+
* {@link shape_mesh_from_geometry} at construction; call again after
|
|
108
|
+
* mutating the underlying data.
|
|
109
|
+
*/
|
|
110
|
+
recompute_cached(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Support function: returns the deepest tet-mesh vertex along the
|
|
113
|
+
* supplied direction. This gives GJK the **convex hull** of the
|
|
114
|
+
* mesh — fine for convex authored geometry, an overestimate for
|
|
115
|
+
* concave shapes (a torus's hole or two disconnected components
|
|
116
|
+
* read as one filled blob through this function).
|
|
117
|
+
*
|
|
118
|
+
* Iterates `tet_positions` rather than the original surface
|
|
119
|
+
* `positions` so any hole-closing centroid that the
|
|
120
|
+
* tetrahedralisation added shows up. For airtight closed meshes the
|
|
121
|
+
* two arrays match element-wise.
|
|
122
|
+
*
|
|
123
|
+
* Future work: an accurate non-convex `support` would have to be
|
|
124
|
+
* per-tet (each tet is convex; the shape is the union). The
|
|
125
|
+
* narrowphase would then iterate tets via the BVH. See PLAN.md.
|
|
126
|
+
*
|
|
127
|
+
* @param {number[]|Float32Array} result
|
|
128
|
+
* @param {number} result_offset
|
|
129
|
+
* @param {number} direction_x
|
|
130
|
+
* @param {number} direction_y
|
|
131
|
+
* @param {number} direction_z
|
|
132
|
+
*/
|
|
133
|
+
support(result: number[] | Float32Array, result_offset: number, direction_x: number, direction_y: number, direction_z: number): void;
|
|
134
|
+
/**
|
|
135
|
+
* Inside test via the tet mesh. Queries the tet BVH for the leaves
|
|
136
|
+
* whose AABB contains the point, then runs an `orient3d`-based
|
|
137
|
+
* point-in-tet test on each candidate. Returns `true` on the first
|
|
138
|
+
* hit.
|
|
139
|
+
*
|
|
140
|
+
* Crucially this works for **disconnected meshes** — a single
|
|
141
|
+
* `MeshShape3D` holding two cubes welded into one buffer, or a
|
|
142
|
+
* torus with a separate sphere, will correctly classify points
|
|
143
|
+
* inside either component. The previous implementation walked tet
|
|
144
|
+
* neighbours starting from a single seed, which trapped the query
|
|
145
|
+
* in the seed's connected component and produced silent false
|
|
146
|
+
* negatives in the other components.
|
|
147
|
+
*
|
|
148
|
+
* @param {number[]|Float32Array} point
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
151
|
+
contains_point(point: number[] | Float32Array): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Closest world-space point on the surface to `reference`. Linear
|
|
154
|
+
* scan over triangles using the barycentric closest-point helper.
|
|
155
|
+
* For mesh sizes where this becomes a bottleneck, a triangle BVH
|
|
156
|
+
* would be the next acceleration (sibling to {@link tet_bvh} but
|
|
157
|
+
* keyed on surface triangles).
|
|
158
|
+
*
|
|
159
|
+
* @param {number[]|Float32Array} result
|
|
160
|
+
* @param {number[]|Float32Array} reference
|
|
161
|
+
*/
|
|
162
|
+
nearest_point_on_surface(result: number[] | Float32Array, reference: number[] | Float32Array): void;
|
|
163
|
+
/**
|
|
164
|
+
* Signed distance to the surface: positive outside the mesh,
|
|
165
|
+
* negative inside. Linear scan over triangles for nearest unsigned
|
|
166
|
+
* distance, then the BVH-backed {@link contains_point} flips the
|
|
167
|
+
* sign for interior points.
|
|
168
|
+
*
|
|
169
|
+
* @param {number[]|Float32Array} point
|
|
170
|
+
* @returns {number}
|
|
171
|
+
*/
|
|
172
|
+
signed_distance_at_point(point: number[] | Float32Array): number;
|
|
173
|
+
/**
|
|
174
|
+
* Reject-sample inside the bounding box until a point lands in the
|
|
175
|
+
* mesh volume. For convex or near-convex shapes this terminates in
|
|
176
|
+
* 1–3 attempts; for thin or pinched shapes the worst-case cost can
|
|
177
|
+
* be high. Falls back to the bbox centre after 256 misses.
|
|
178
|
+
*
|
|
179
|
+
* @param {number[]|Float32Array} result
|
|
180
|
+
* @param {number} result_offset
|
|
181
|
+
* @param {function():number} random
|
|
182
|
+
*/
|
|
183
|
+
sample_random_point_in_volume(result: number[] | Float32Array, result_offset: number, random: () => number): void;
|
|
184
|
+
/**
|
|
185
|
+
* Numerical-gradient SDF gradient.
|
|
186
|
+
*
|
|
187
|
+
* @param {number[]|Float32Array} result
|
|
188
|
+
* @param {number[]|Float32Array} point
|
|
189
|
+
* @returns {number}
|
|
190
|
+
*/
|
|
191
|
+
signed_distance_gradient_at_point(result: number[] | Float32Array, point: number[] | Float32Array): number;
|
|
192
|
+
/**
|
|
193
|
+
* Topology equality: same vertex count, same triangle count, and
|
|
194
|
+
* positions / indices match element-by-element.
|
|
195
|
+
*
|
|
196
|
+
* @param {MeshShape3D} other
|
|
197
|
+
* @returns {boolean}
|
|
198
|
+
*/
|
|
199
|
+
equals(other: MeshShape3D): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Fast type-check marker.
|
|
202
|
+
* @readonly
|
|
203
|
+
* @type {boolean}
|
|
204
|
+
*/
|
|
205
|
+
readonly isMeshShape3D: boolean;
|
|
206
|
+
}
|
|
207
|
+
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
208
|
+
import { TetrahedralMesh } from "../tetrahedra/TetrahedralMesh.js";
|
|
209
|
+
import { BVH } from "../../../bvh2/bvh3/BVH.js";
|
|
210
|
+
//# sourceMappingURL=MeshShape3D.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MeshShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/MeshShape3D.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH;IAKQ;;;;;OAKG;IACH,WAFU,YAAY,CAEc;IAEpC;;;;OAIG;IACH,SAFU,WAAW,CAEY;IAEjC;;;;;;;OAOG;IACH,UAFU,eAAe,CAEY;IAErC;;;;;;;;OAQG;IACH,eAFU,YAAY,CAEkB;IAExC;;;;;;OAMG;IACH,SAFU,GAAG,CAEW;IAExB;;;;;OAKG;IACH,eAAkD;IAElD;;;;;OAKG;IACH,iBAAiB;IAEjB;;;;OAIG;IACH,uBAAuB;IAW3B,wCAIC;IAED;;;;;OAKG;IACH,yBAmDC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBANW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,eACN,MAAM,eACN,MAAM,eACN,MAAM,QA0BhB;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,sBAHW,MAAM,EAAE,GAAC,YAAY,GACnB,OAAO,CAkCnB;IAED;;;;;;;;;OASG;IACH,iCAHW,MAAM,EAAE,GAAC,YAAY,aACrB,MAAM,EAAE,GAAC,YAAY,QA4C/B;IAED;;;;;;;;OAQG;IACH,gCAHW,MAAM,EAAE,GAAC,YAAY,GACnB,MAAM,CAsClB;IAED;;;;;;;;;OASG;IACH,sCAJW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,gBACK,MAAM,QAuB3B;IAED;;;;;;OAMG;IACH,0CAJW,MAAM,EAAE,GAAC,YAAY,SACrB,MAAM,EAAE,GAAC,YAAY,GACnB,MAAM,CAiBlB;IAED;;;;;;OAMG;IACH,cAHW,WAAW,GACT,OAAO,CAanB;IAkBL;;;;OAIG;IACH,wBAFU,OAAO,CAEkB;CAPlC;gCAje+B,sBAAsB;gCADtB,kCAAkC;oBAJ9C,2BAA2B"}
|