@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.
Files changed (107) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +112 -0
  3. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -0
  4. package/src/core/geom/3d/shape/ConvexHullShape3D.js +325 -0
  5. package/src/core/geom/vec3/v3_array_copy.d.ts +3 -3
  6. package/src/core/geom/vec3/v3_array_copy.d.ts.map +1 -1
  7. package/src/core/geom/vec3/v3_array_copy.js +2 -2
  8. package/src/core/geom/vec3/v3_cross.d.ts +17 -0
  9. package/src/core/geom/vec3/v3_cross.d.ts.map +1 -0
  10. package/src/core/geom/vec3/v3_cross.js +20 -0
  11. package/src/core/geom/vec3/v3_subtract.d.ts +16 -0
  12. package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -0
  13. package/src/core/geom/vec3/v3_subtract.js +19 -0
  14. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts.map +1 -1
  15. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +8 -0
  16. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +4 -0
  17. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
  18. package/src/engine/graphics/ecs/trail2d/Trail2D.js +21 -0
  19. package/src/engine/physics/PLAN.md +4 -4
  20. package/src/engine/physics/body/BodyStorage.d.ts +3 -1
  21. package/src/engine/physics/body/BodyStorage.d.ts.map +1 -1
  22. package/src/engine/physics/body/BodyStorage.js +452 -450
  23. package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
  24. package/src/engine/physics/body/SolverBodyState.js +6 -5
  25. package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
  26. package/src/engine/physics/broadphase/generate_pairs.js +9 -1
  27. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  28. package/src/engine/physics/ccd/linear_sweep.js +237 -238
  29. package/src/engine/physics/computeInterceptPoint.d.ts.map +1 -1
  30. package/src/engine/physics/computeInterceptPoint.js +8 -3
  31. package/src/engine/physics/contact/ManifoldStore.d.ts +0 -16
  32. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  33. package/src/engine/physics/contact/ManifoldStore.js +1 -38
  34. package/src/engine/physics/ecs/BodyKind.d.ts +3 -2
  35. package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
  36. package/src/engine/physics/ecs/BodyKind.js +25 -24
  37. package/src/engine/physics/ecs/PhysicsEvents.d.ts +4 -5
  38. package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
  39. package/src/engine/physics/ecs/PhysicsEvents.js +15 -16
  40. package/src/engine/physics/ecs/PhysicsSystem.d.ts +5 -30
  41. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  42. package/src/engine/physics/ecs/PhysicsSystem.js +13 -45
  43. package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts.map +1 -1
  44. package/src/engine/physics/ecs/RigidBodySerializationAdapter.js +85 -81
  45. package/src/engine/physics/ecs/is_sensor.d.ts +18 -0
  46. package/src/engine/physics/ecs/is_sensor.d.ts.map +1 -0
  47. package/src/engine/physics/ecs/is_sensor.js +27 -0
  48. package/src/engine/physics/events/ContactEventBuffer.d.ts +2 -1
  49. package/src/engine/physics/events/ContactEventBuffer.d.ts.map +1 -1
  50. package/src/engine/physics/events/ContactEventBuffer.js +84 -83
  51. package/src/engine/physics/gjk/gjk.d.ts +0 -26
  52. package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
  53. package/src/engine/physics/gjk/gjk.js +3 -52
  54. package/src/engine/physics/gjk/gjk_epa_penetration.d.ts +20 -0
  55. package/src/engine/physics/gjk/gjk_epa_penetration.d.ts.map +1 -0
  56. package/src/engine/physics/gjk/gjk_epa_penetration.js +548 -0
  57. package/src/engine/physics/gjk/minkowski_support.d.ts +4 -9
  58. package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -1
  59. package/src/engine/physics/gjk/minkowski_support.js +70 -75
  60. package/src/engine/physics/gjk/mpr.d.ts +1 -1
  61. package/src/engine/physics/gjk/mpr.d.ts.map +1 -1
  62. package/src/engine/physics/gjk/mpr.js +362 -344
  63. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
  64. package/src/engine/physics/island/IslandBuilder.js +431 -428
  65. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  66. package/src/engine/physics/narrowphase/box_box_manifold.js +4 -81
  67. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
  68. package/src/engine/physics/narrowphase/box_triangle_contact.js +4 -39
  69. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  70. package/src/engine/physics/narrowphase/capsule_contacts.js +459 -462
  71. package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -1
  72. package/src/engine/physics/narrowphase/clip_against_axis_uv.js +4 -1
  73. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +83 -0
  74. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -0
  75. package/src/engine/physics/narrowphase/convex_convex_manifold.js +425 -0
  76. package/src/engine/physics/narrowphase/convex_decomposition.d.ts +32 -0
  77. package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -0
  78. package/src/engine/physics/narrowphase/convex_decomposition.js +293 -0
  79. package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts +41 -0
  80. package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts.map +1 -0
  81. package/src/engine/physics/narrowphase/mesh_convex_hull.js +106 -0
  82. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +8 -0
  83. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -0
  84. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -0
  85. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  86. package/src/engine/physics/narrowphase/narrowphase_step.js +105 -102
  87. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +29 -0
  88. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -0
  89. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +69 -0
  90. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  91. package/src/engine/physics/narrowphase/refine_ray_concave.js +152 -145
  92. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  93. package/src/engine/physics/narrowphase/sphere_box_contact.js +132 -123
  94. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
  95. package/src/engine/physics/queries/overlap_shape.js +16 -17
  96. package/src/engine/physics/queries/raycast.d.ts +5 -0
  97. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  98. package/src/engine/physics/queries/raycast.js +16 -8
  99. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
  100. package/src/engine/physics/queries/shape_cast.js +13 -7
  101. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  102. package/src/engine/physics/solver/solve_contacts.js +8 -11
  103. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  104. package/src/engine/physics/vehicle/RaycastVehicle.js +339 -333
  105. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +0 -13
  106. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +0 -1
  107. package/src/engine/physics/gjk/expanding_polytope_algorithm.js +0 -399
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Pure JavaScript game engine. Fully featured and production ready.",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "2.153.0",
9
+ "version": "2.155.0",
10
10
  "main": "build/meep.module.js",
11
11
  "module": "build/meep.module.js",
12
12
  "exports": {
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Convex polyhedron collider.
3
+ *
4
+ * Holds a convex triangle surface (`vertices` + outward-CCW `indices`) and the
5
+ * derived face half-spaces. Because it is convex (`is_convex === true`, the
6
+ * default), it routes straight through the narrowphase's GJK + EPA path via
7
+ * {@link support} — no tetrahedralisation, no per-triangle decomposition, no
8
+ * BVH. This is the right shape for authored convex geometry (props, crates,
9
+ * gems, low-poly rocks, an icosphere) and is far cheaper to construct than
10
+ * {@link MeshShape3D}, which tetrahedralises its interior.
11
+ *
12
+ * Contract:
13
+ * - `support` is exact for any convex point set (deepest vertex along the
14
+ * direction) — the precondition GJK relies on.
15
+ * - `contains_point` is the convex half-space test (behind every face plane).
16
+ * - `signed_distance` / `nearest_point_on_surface` scan the surface triangles
17
+ * (O(F)); these are query-path helpers, not on the collision hot path.
18
+ *
19
+ * The input is assumed convex with outward-facing CCW winding — build it via
20
+ * {@link ConvexHullShape3D.from}. A genuine non-convex mesh belongs in
21
+ * {@link MeshShape3D} (and, once decomposition lands, will be represented as a
22
+ * set of these pieces).
23
+ *
24
+ * @author Alex Goldring
25
+ * @copyright Company Named Limited (c) 2026
26
+ */
27
+ export class ConvexHullShape3D extends AbstractShape3D {
28
+ /**
29
+ * Build from a convex triangle surface. Vertices must lie on the hull and
30
+ * faces must wind CCW when viewed from outside.
31
+ *
32
+ * @param {Float32Array} vertices flat `(x, y, z)` per vertex
33
+ * @param {Uint32Array} indices three uint32 per triangle, outward CCW
34
+ * @returns {ConvexHullShape3D}
35
+ */
36
+ static from(vertices: Float32Array, indices: Uint32Array): ConvexHullShape3D;
37
+ /**
38
+ * Hull vertex positions, flat `(x, y, z)` per vertex, in the body's
39
+ * local frame. Fed to {@link support}.
40
+ * @type {Float32Array}
41
+ */
42
+ vertices: Float32Array;
43
+ /**
44
+ * Surface triangle indices, three uint32 per face (outward CCW). Used by
45
+ * the surface-scan query helpers (signed distance / nearest point).
46
+ * @type {Uint32Array}
47
+ */
48
+ indices: Uint32Array;
49
+ /**
50
+ * Polygon-face representation for the contact clipper (CSR): face `f`'s
51
+ * vertex loop is `face_loops[face_offsets[f] .. face_offsets[f+1]]`,
52
+ * ordered CCW (outward). Currently one triangle per face; a coplanar-merge
53
+ * pass will collapse coplanar triangles into larger polygon faces.
54
+ * @type {Uint32Array}
55
+ */
56
+ face_offsets: Uint32Array;
57
+ /** @type {Uint32Array} */
58
+ face_loops: Uint32Array;
59
+ /**
60
+ * Face half-spaces, four doubles per face `(nx, ny, nz, d)` with unit
61
+ * outward normal and `d = n · v` — point is inside iff `n · p − d ≤ 0`
62
+ * for every face. Derived in {@link recompute}.
63
+ * @private
64
+ * @type {Float64Array}
65
+ */
66
+ private __planes;
67
+ /** @private @type {Float64Array} */
68
+ private __bbox;
69
+ /** @private @type {number} */
70
+ private __volume;
71
+ /** @private @type {number} */
72
+ private __surface_area;
73
+ /**
74
+ * Refresh the cached bbox / face planes / volume / surface area from the
75
+ * current `vertices` / `indices`.
76
+ */
77
+ recompute(): void;
78
+ compute_bounding_box(result: any): void;
79
+ /**
80
+ * GJK support: the hull vertex furthest along the direction. Exact for a
81
+ * convex point set. Linear scan over the vertices — fine for the modest
82
+ * vertex counts of authored hulls; a hill-climb over vertex adjacency is
83
+ * the acceleration for very large hulls.
84
+ */
85
+ support(result: any, result_offset: any, direction_x: any, direction_y: any, direction_z: any): void;
86
+ /**
87
+ * Convex half-space test: inside iff the point is behind (or on) every face
88
+ * plane. Exact for a convex polytope.
89
+ */
90
+ contains_point(point: any): boolean;
91
+ /**
92
+ * Unsigned nearest-surface distance via a per-triangle scan, signed by
93
+ * {@link contains_point}. Same approach as {@link MeshShape3D}; O(F).
94
+ */
95
+ signed_distance_at_point(point: any): number;
96
+ signed_distance_gradient_at_point(result: any, point: any): number;
97
+ nearest_point_on_surface(result: any, reference: any): void;
98
+ sample_random_point_in_volume(result: any, result_offset: any, random: any): void;
99
+ /**
100
+ * @param {ConvexHullShape3D} other
101
+ * @returns {boolean}
102
+ */
103
+ equals(other: ConvexHullShape3D): boolean;
104
+ /**
105
+ * Fast type-check marker.
106
+ * @readonly
107
+ * @type {boolean}
108
+ */
109
+ readonly isConvexHullShape3D: boolean;
110
+ }
111
+ import { AbstractShape3D } from "./AbstractShape3D.js";
112
+ //# sourceMappingURL=ConvexHullShape3D.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConvexHullShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/ConvexHullShape3D.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH;IA+CI;;;;;;;OAOG;IACH,sBAJW,YAAY,WACZ,WAAW,GACT,iBAAiB,CAQ7B;IAxDG;;;;OAIG;IACH,UAFU,YAAY,CAEa;IAEnC;;;;OAIG;IACH,SAFU,WAAW,CAEY;IAEjC;;;;;;OAMG;IACH,cAFU,WAAW,CAEiB;IACtC,0BAA0B;IAC1B,YADW,WAAW,CACc;IAEpC;;;;;;OAMG;IACH,iBAAmC;IAEnC,oCAAoC;IACpC,eAAiC;IACjC,8BAA8B;IAC9B,iBAAiB;IACjB,8BAA8B;IAC9B,uBAAuB;IAmB3B;;;OAGG;IACH,kBAmDC;IAKD,wCAIC;IAED;;;;;OAKG;IACH,qGAgBC;IAED;;;OAGG;IACH,oCAgBC;IAED;;;OAGG;IACH,6CAwBC;IAED,mEAEC;IAED,4DAwBC;IAED,kFAmBC;IAED;;;OAGG;IACH,cAHW,iBAAiB,GACf,OAAO,CAanB;IAcL;;;;OAIG;IACH,8BAFU,OAAO,CAE8B;CAP9C;gCAxT+B,sBAAsB"}
@@ -0,0 +1,325 @@
1
+ import { computeHashFloat } from "../../../primitives/numbers/computeHashFloat.js";
2
+ import { computeTriangleClosestPointToPointBarycentric } from "../triangle/computeTriangleClosestPointToPointBarycentric.js";
3
+ import { AbstractShape3D } from "./AbstractShape3D.js";
4
+ import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
5
+
6
+ /**
7
+ * Convex polyhedron collider.
8
+ *
9
+ * Holds a convex triangle surface (`vertices` + outward-CCW `indices`) and the
10
+ * derived face half-spaces. Because it is convex (`is_convex === true`, the
11
+ * default), it routes straight through the narrowphase's GJK + EPA path via
12
+ * {@link support} — no tetrahedralisation, no per-triangle decomposition, no
13
+ * BVH. This is the right shape for authored convex geometry (props, crates,
14
+ * gems, low-poly rocks, an icosphere) and is far cheaper to construct than
15
+ * {@link MeshShape3D}, which tetrahedralises its interior.
16
+ *
17
+ * Contract:
18
+ * - `support` is exact for any convex point set (deepest vertex along the
19
+ * direction) — the precondition GJK relies on.
20
+ * - `contains_point` is the convex half-space test (behind every face plane).
21
+ * - `signed_distance` / `nearest_point_on_surface` scan the surface triangles
22
+ * (O(F)); these are query-path helpers, not on the collision hot path.
23
+ *
24
+ * The input is assumed convex with outward-facing CCW winding — build it via
25
+ * {@link ConvexHullShape3D.from}. A genuine non-convex mesh belongs in
26
+ * {@link MeshShape3D} (and, once decomposition lands, will be represented as a
27
+ * set of these pieces).
28
+ *
29
+ * @author Alex Goldring
30
+ * @copyright Company Named Limited (c) 2026
31
+ */
32
+ export class ConvexHullShape3D extends AbstractShape3D {
33
+
34
+ constructor() {
35
+ super();
36
+
37
+ /**
38
+ * Hull vertex positions, flat `(x, y, z)` per vertex, in the body's
39
+ * local frame. Fed to {@link support}.
40
+ * @type {Float32Array}
41
+ */
42
+ this.vertices = new Float32Array(0);
43
+
44
+ /**
45
+ * Surface triangle indices, three uint32 per face (outward CCW). Used by
46
+ * the surface-scan query helpers (signed distance / nearest point).
47
+ * @type {Uint32Array}
48
+ */
49
+ this.indices = new Uint32Array(0);
50
+
51
+ /**
52
+ * Polygon-face representation for the contact clipper (CSR): face `f`'s
53
+ * vertex loop is `face_loops[face_offsets[f] .. face_offsets[f+1]]`,
54
+ * ordered CCW (outward). Currently one triangle per face; a coplanar-merge
55
+ * pass will collapse coplanar triangles into larger polygon faces.
56
+ * @type {Uint32Array}
57
+ */
58
+ this.face_offsets = new Uint32Array(0);
59
+ /** @type {Uint32Array} */
60
+ this.face_loops = new Uint32Array(0);
61
+
62
+ /**
63
+ * Face half-spaces, four doubles per face `(nx, ny, nz, d)` with unit
64
+ * outward normal and `d = n · v` — point is inside iff `n · p − d ≤ 0`
65
+ * for every face. Derived in {@link recompute}.
66
+ * @private
67
+ * @type {Float64Array}
68
+ */
69
+ this.__planes = new Float64Array(0);
70
+
71
+ /** @private @type {Float64Array} */
72
+ this.__bbox = new Float64Array(6);
73
+ /** @private @type {number} */
74
+ this.__volume = 0;
75
+ /** @private @type {number} */
76
+ this.__surface_area = 0;
77
+ }
78
+
79
+ /**
80
+ * Build from a convex triangle surface. Vertices must lie on the hull and
81
+ * faces must wind CCW when viewed from outside.
82
+ *
83
+ * @param {Float32Array} vertices flat `(x, y, z)` per vertex
84
+ * @param {Uint32Array} indices three uint32 per triangle, outward CCW
85
+ * @returns {ConvexHullShape3D}
86
+ */
87
+ static from(vertices, indices) {
88
+ const s = new ConvexHullShape3D();
89
+ s.vertices = vertices;
90
+ s.indices = indices;
91
+ s.recompute();
92
+ return s;
93
+ }
94
+
95
+ /**
96
+ * Refresh the cached bbox / face planes / volume / surface area from the
97
+ * current `vertices` / `indices`.
98
+ */
99
+ recompute() {
100
+ const v = this.vertices;
101
+ const idx = this.indices;
102
+
103
+ let mnx = Infinity, mny = Infinity, mnz = Infinity;
104
+ let mxx = -Infinity, mxy = -Infinity, mxz = -Infinity;
105
+ const vc = v.length / 3;
106
+ for (let i = 0; i < vc; i++) {
107
+ const x = v[i * 3], y = v[i * 3 + 1], z = v[i * 3 + 2];
108
+ if (x < mnx) mnx = x; if (x > mxx) mxx = x;
109
+ if (y < mny) mny = y; if (y > mxy) mxy = y;
110
+ if (z < mnz) mnz = z; if (z > mxz) mxz = z;
111
+ }
112
+ if (vc === 0) { mnx = mny = mnz = mxx = mxy = mxz = 0; }
113
+ const b = this.__bbox;
114
+ b[0] = mnx; b[1] = mny; b[2] = mnz; b[3] = mxx; b[4] = mxy; b[5] = mxz;
115
+
116
+ const tri = idx.length / 3;
117
+ const planes = new Float64Array(tri * 4);
118
+ let area = 0;
119
+ let vol6 = 0; // 6× the signed volume (Σ a·(b×c) over outward faces)
120
+ for (let f = 0; f < tri; f++) {
121
+ const ia = idx[f * 3] * 3, ib = idx[f * 3 + 1] * 3, ic = idx[f * 3 + 2] * 3;
122
+ const ax = v[ia], ay = v[ia + 1], az = v[ia + 2];
123
+ const bx = v[ib], by = v[ib + 1], bz = v[ib + 2];
124
+ const cx = v[ic], cy = v[ic + 1], cz = v[ic + 2];
125
+
126
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
127
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
128
+ let nx = e1y * e2z - e1z * e2y;
129
+ let ny = e1z * e2x - e1x * e2z;
130
+ let nz = e1x * e2y - e1y * e2x;
131
+ const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
132
+ area += 0.5 * len;
133
+ vol6 += ax * (by * cz - bz * cy) + ay * (bz * cx - bx * cz) + az * (bx * cy - by * cx);
134
+
135
+ nx /= len; ny /= len; nz /= len;
136
+ const o = f * 4;
137
+ planes[o] = nx; planes[o + 1] = ny; planes[o + 2] = nz;
138
+ planes[o + 3] = nx * ax + ny * ay + nz * az;
139
+ }
140
+ this.__planes = planes;
141
+ this.__surface_area = area;
142
+ this.__volume = Math.abs(vol6) / 6;
143
+
144
+ // Polygon faces for the clipper. v1: one triangle per face (the contact
145
+ // clipper consumes polygon loops; a tetrahedral piece is exactly this).
146
+ const fo = new Uint32Array(tri + 1);
147
+ for (let f = 0; f <= tri; f++) fo[f] = f * 3;
148
+ this.face_offsets = fo;
149
+ this.face_loops = idx;
150
+ }
151
+
152
+ get volume() { return this.__volume; }
153
+ get surface_area() { return this.__surface_area; }
154
+
155
+ compute_bounding_box(result) {
156
+ const b = this.__bbox;
157
+ result[0] = b[0]; result[1] = b[1]; result[2] = b[2];
158
+ result[3] = b[3]; result[4] = b[4]; result[5] = b[5];
159
+ }
160
+
161
+ /**
162
+ * GJK support: the hull vertex furthest along the direction. Exact for a
163
+ * convex point set. Linear scan over the vertices — fine for the modest
164
+ * vertex counts of authored hulls; a hill-climb over vertex adjacency is
165
+ * the acceleration for very large hulls.
166
+ */
167
+ support(result, result_offset, direction_x, direction_y, direction_z) {
168
+ const v = this.vertices;
169
+ const vc = v.length / 3;
170
+ if (vc === 0) {
171
+ result[result_offset] = 0; result[result_offset + 1] = 0; result[result_offset + 2] = 0;
172
+ return;
173
+ }
174
+ let best = -Infinity;
175
+ let bi = 0;
176
+ for (let i = 0; i < vc; i++) {
177
+ const d = direction_x * v[i * 3] + direction_y * v[i * 3 + 1] + direction_z * v[i * 3 + 2];
178
+ if (d > best) { best = d; bi = i; }
179
+ }
180
+ result[result_offset] = v[bi * 3];
181
+ result[result_offset + 1] = v[bi * 3 + 1];
182
+ result[result_offset + 2] = v[bi * 3 + 2];
183
+ }
184
+
185
+ /**
186
+ * Convex half-space test: inside iff the point is behind (or on) every face
187
+ * plane. Exact for a convex polytope.
188
+ */
189
+ contains_point(point) {
190
+ const px = point[0], py = point[1], pz = point[2];
191
+ const b = this.__bbox;
192
+ if (px < b[0] || py < b[1] || pz < b[2] || px > b[3] || py > b[4] || pz > b[5]) {
193
+ return false;
194
+ }
195
+ const planes = this.__planes;
196
+ const f = planes.length / 4;
197
+ const EPS = 1e-6;
198
+ for (let i = 0; i < f; i++) {
199
+ const o = i * 4;
200
+ if (planes[o] * px + planes[o + 1] * py + planes[o + 2] * pz - planes[o + 3] > EPS) {
201
+ return false;
202
+ }
203
+ }
204
+ return true;
205
+ }
206
+
207
+ /**
208
+ * Unsigned nearest-surface distance via a per-triangle scan, signed by
209
+ * {@link contains_point}. Same approach as {@link MeshShape3D}; O(F).
210
+ */
211
+ signed_distance_at_point(point) {
212
+ const v = this.vertices;
213
+ const idx = this.indices;
214
+ const tri = idx.length / 3;
215
+ const px = point[0], py = point[1], pz = point[2];
216
+
217
+ let best_d2 = Infinity;
218
+ const baryc = scratch_barycentric;
219
+ for (let i = 0; i < tri; i++) {
220
+ const ia = idx[i * 3] * 3, ib = idx[i * 3 + 1] * 3, ic = idx[i * 3 + 2] * 3;
221
+ const ax = v[ia], ay = v[ia + 1], az = v[ia + 2];
222
+ const bx = v[ib], by = v[ib + 1], bz = v[ib + 2];
223
+ const cx = v[ic], cy = v[ic + 1], cz = v[ic + 2];
224
+ computeTriangleClosestPointToPointBarycentric(baryc, 0, px, py, pz, ax, ay, az, bx, by, bz, cx, cy, cz);
225
+ const u = baryc[0], w = baryc[1], t = 1 - u - w;
226
+ const qx = u * ax + w * bx + t * cx;
227
+ const qy = u * ay + w * by + t * cy;
228
+ const qz = u * az + w * bz + t * cz;
229
+ const dx = qx - px, dy = qy - py, dz = qz - pz;
230
+ const d2 = dx * dx + dy * dy + dz * dz;
231
+ if (d2 < best_d2) best_d2 = d2;
232
+ }
233
+ const dist = Math.sqrt(best_d2);
234
+ return this.contains_point(point) ? -dist : dist;
235
+ }
236
+
237
+ signed_distance_gradient_at_point(result, point) {
238
+ return compute_signed_distance_gradient_by_sampling(result, this, point);
239
+ }
240
+
241
+ nearest_point_on_surface(result, reference) {
242
+ const v = this.vertices;
243
+ const idx = this.indices;
244
+ const tri = idx.length / 3;
245
+ const px = reference[0], py = reference[1], pz = reference[2];
246
+
247
+ let best_d2 = Infinity;
248
+ let bx_ = 0, by_ = 0, bz_ = 0;
249
+ const baryc = scratch_barycentric;
250
+ for (let i = 0; i < tri; i++) {
251
+ const ia = idx[i * 3] * 3, ib = idx[i * 3 + 1] * 3, ic = idx[i * 3 + 2] * 3;
252
+ const ax = v[ia], ay = v[ia + 1], az = v[ia + 2];
253
+ const bx = v[ib], by = v[ib + 1], bz = v[ib + 2];
254
+ const cx = v[ic], cy = v[ic + 1], cz = v[ic + 2];
255
+ computeTriangleClosestPointToPointBarycentric(baryc, 0, px, py, pz, ax, ay, az, bx, by, bz, cx, cy, cz);
256
+ const u = baryc[0], w = baryc[1], t = 1 - u - w;
257
+ const qx = u * ax + w * bx + t * cx;
258
+ const qy = u * ay + w * by + t * cy;
259
+ const qz = u * az + w * bz + t * cz;
260
+ const dx = qx - px, dy = qy - py, dz = qz - pz;
261
+ const d2 = dx * dx + dy * dy + dz * dz;
262
+ if (d2 < best_d2) { best_d2 = d2; bx_ = qx; by_ = qy; bz_ = qz; }
263
+ }
264
+ result[0] = bx_; result[1] = by_; result[2] = bz_;
265
+ }
266
+
267
+ sample_random_point_in_volume(result, result_offset, random) {
268
+ const b = this.__bbox;
269
+ const lx = b[0], ly = b[1], lz = b[2];
270
+ const sx = b[3] - b[0], sy = b[4] - b[1], sz = b[5] - b[2];
271
+ const probe = scratch_point;
272
+ for (let attempt = 0; attempt < 256; attempt++) {
273
+ probe[0] = lx + random() * sx;
274
+ probe[1] = ly + random() * sy;
275
+ probe[2] = lz + random() * sz;
276
+ if (this.contains_point(probe)) {
277
+ result[result_offset] = probe[0];
278
+ result[result_offset + 1] = probe[1];
279
+ result[result_offset + 2] = probe[2];
280
+ return;
281
+ }
282
+ }
283
+ result[result_offset] = lx + sx * 0.5;
284
+ result[result_offset + 1] = ly + sy * 0.5;
285
+ result[result_offset + 2] = lz + sz * 0.5;
286
+ }
287
+
288
+ /**
289
+ * @param {ConvexHullShape3D} other
290
+ * @returns {boolean}
291
+ */
292
+ equals(other) {
293
+ if (!super.equals(other)) return false;
294
+ if (this.vertices.length !== other.vertices.length) return false;
295
+ if (this.indices.length !== other.indices.length) return false;
296
+ for (let i = 0; i < this.vertices.length; i++) {
297
+ if (this.vertices[i] !== other.vertices[i]) return false;
298
+ }
299
+ for (let i = 0; i < this.indices.length; i++) {
300
+ if (this.indices[i] !== other.indices[i]) return false;
301
+ }
302
+ return true;
303
+ }
304
+
305
+ hash() {
306
+ const v_count = this.vertices.length / 3 | 0;
307
+ const t_count = this.indices.length / 3 | 0;
308
+ let h = (v_count * 31 + t_count) | 0;
309
+ if (this.vertices.length >= 3) {
310
+ h = (h * 31 + (computeHashFloat(this.vertices[0]))) | 0;
311
+ h = (h * 31 + (computeHashFloat(this.vertices[this.vertices.length - 1]))) | 0;
312
+ }
313
+ return h | 0;
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Fast type-check marker.
319
+ * @readonly
320
+ * @type {boolean}
321
+ */
322
+ ConvexHullShape3D.prototype.isConvexHullShape3D = true;
323
+
324
+ const scratch_barycentric = new Float64Array(2);
325
+ const scratch_point = new Float64Array(3);
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  *
3
- * @param {number[]|Float32Array|Uint32Array|Int32Array} target
3
+ * @param {number[]|Float32Array|Float64Array|Uint32Array|Int32Array} target
4
4
  * @param {number} target_offset
5
- * @param {number[]|Float32Array|Uint32Array|Int32Array} source
5
+ * @param {number[]|Float32Array|Float64Array|Uint32Array|Int32Array} source
6
6
  * @param {number} source_offset
7
7
  */
8
- export function v3_array_copy(target: number[] | Float32Array | Uint32Array | Int32Array, target_offset: number, source: number[] | Float32Array | Uint32Array | Int32Array, source_offset: number): void;
8
+ export function v3_array_copy(target: number[] | Float32Array | Float64Array | Uint32Array | Int32Array, target_offset: number, source: number[] | Float32Array | Float64Array | Uint32Array | Int32Array, source_offset: number): void;
9
9
  //# sourceMappingURL=v3_array_copy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"v3_array_copy.d.ts","sourceRoot":"","sources":["../../../../../src/core/geom/vec3/v3_array_copy.js"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,sCALW,MAAM,EAAE,GAAC,YAAY,GAAC,WAAW,GAAC,UAAU,iBAC5C,MAAM,UACN,MAAM,EAAE,GAAC,YAAY,GAAC,WAAW,GAAC,UAAU,iBAC5C,MAAM,QAWhB"}
1
+ {"version":3,"file":"v3_array_copy.d.ts","sourceRoot":"","sources":["../../../../../src/core/geom/vec3/v3_array_copy.js"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,sCALW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAAC,WAAW,GAAC,UAAU,iBACzD,MAAM,UACN,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAAC,WAAW,GAAC,UAAU,iBACzD,MAAM,QAWhB"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  *
3
- * @param {number[]|Float32Array|Uint32Array|Int32Array} target
3
+ * @param {number[]|Float32Array|Float64Array|Uint32Array|Int32Array} target
4
4
  * @param {number} target_offset
5
- * @param {number[]|Float32Array|Uint32Array|Int32Array} source
5
+ * @param {number[]|Float32Array|Float64Array|Uint32Array|Int32Array} source
6
6
  * @param {number} source_offset
7
7
  */
8
8
  export function v3_array_copy(target, target_offset, source, source_offset) {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Cross product `a × b`, written to `result[result_offset .. result_offset+2]`.
3
+ *
4
+ * Scalar-argument companion to {@link v3_cross_array} (which takes both operands
5
+ * at array offsets): use this when the operands are already in registers/locals,
6
+ * to avoid staging them into a buffer just to cross them. Allocation-free.
7
+ *
8
+ * @param {number[]|Float32Array|Float64Array} result destination
9
+ * @param {number} result_offset offset into `result`
10
+ * @param {number} ax @param {number} ay @param {number} az first vector
11
+ * @param {number} bx @param {number} by @param {number} bz second vector
12
+ *
13
+ * @author Alex Goldring
14
+ * @copyright Company Named Limited (c) 2026
15
+ */
16
+ export function v3_cross(result: number[] | Float32Array | Float64Array, result_offset: number, ax: number, ay: number, az: number, bx: number, by: number, bz: number): void;
17
+ //# sourceMappingURL=v3_cross.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3_cross.d.ts","sourceRoot":"","sources":["../../../../../src/core/geom/vec3/v3_cross.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,iCARW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,iBAClC,MAAM,MACN,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,MAAa,MAAM,MAAa,MAAM,QAStD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cross product `a × b`, written to `result[result_offset .. result_offset+2]`.
3
+ *
4
+ * Scalar-argument companion to {@link v3_cross_array} (which takes both operands
5
+ * at array offsets): use this when the operands are already in registers/locals,
6
+ * to avoid staging them into a buffer just to cross them. Allocation-free.
7
+ *
8
+ * @param {number[]|Float32Array|Float64Array} result destination
9
+ * @param {number} result_offset offset into `result`
10
+ * @param {number} ax @param {number} ay @param {number} az first vector
11
+ * @param {number} bx @param {number} by @param {number} bz second vector
12
+ *
13
+ * @author Alex Goldring
14
+ * @copyright Company Named Limited (c) 2026
15
+ */
16
+ export function v3_cross(result, result_offset, ax, ay, az, bx, by, bz) {
17
+ result[result_offset] = ay * bz - az * by;
18
+ result[result_offset + 1] = az * bx - ax * bz;
19
+ result[result_offset + 2] = ax * by - ay * bx;
20
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Vector difference `a − b`, written to `result[result_offset .. result_offset+2]`.
3
+ *
4
+ * Scalar-argument, out-parameter form: lets callers compute an edge/offset vector
5
+ * straight into a scratch buffer without allocating a temporary. Allocation-free.
6
+ *
7
+ * @param {number[]|Float32Array|Float64Array} result destination
8
+ * @param {number} result_offset offset into `result`
9
+ * @param {number} ax @param {number} ay @param {number} az minuend
10
+ * @param {number} bx @param {number} by @param {number} bz subtrahend
11
+ *
12
+ * @author Alex Goldring
13
+ * @copyright Company Named Limited (c) 2026
14
+ */
15
+ export function v3_subtract(result: number[] | Float32Array | Float64Array, result_offset: number, ax: number, ay: number, az: number, bx: number, by: number, bz: number): void;
16
+ //# sourceMappingURL=v3_subtract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3_subtract.d.ts","sourceRoot":"","sources":["../../../../../src/core/geom/vec3/v3_subtract.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,oCARW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,iBAClC,MAAM,MACN,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,MAAa,MAAM,MAAa,MAAM,QAStD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Vector difference `a − b`, written to `result[result_offset .. result_offset+2]`.
3
+ *
4
+ * Scalar-argument, out-parameter form: lets callers compute an edge/offset vector
5
+ * straight into a scratch buffer without allocating a temporary. Allocation-free.
6
+ *
7
+ * @param {number[]|Float32Array|Float64Array} result destination
8
+ * @param {number} result_offset offset into `result`
9
+ * @param {number} ax @param {number} ay @param {number} az minuend
10
+ * @param {number} bx @param {number} by @param {number} bz subtrahend
11
+ *
12
+ * @author Alex Goldring
13
+ * @copyright Company Named Limited (c) 2026
14
+ */
15
+ export function v3_subtract(result, result_offset, ax, ay, az, bx, by, bz) {
16
+ result[result_offset] = ax - bx;
17
+ result[result_offset + 1] = ay - by;
18
+ result[result_offset + 2] = az - bz;
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"FPDecalSystem.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/decal/v2/FPDecalSystem.js"],"names":[],"mappings":"AAoOA;;GAEG;AACH;IAGI;;;OAGG;IACH,4BA0CC;IAhDD,SAAgB;IASZ,kDAAsC;IAEtC;;;;OAIG;IACH,wBAUE;IAEF;;;;OAIG;IACH,iBAAmC;IAEnC;;;;OAIG;IACH,iBAAsB;IAEtB;;;;OAIG;IACH,oBAAiC;IAGrC,gCAEC;IAED,iCAEC;IAED;;;;;;OAMG;IACH,4BALW,MAAM,EAAE,iBACR,MAAM,UACN,MAAM,EAAE,GACN,MAAM,CA2DlB;IAED;;;;;;;;;;;OAWG;IACH,kBAVW,MAAM,YACN,MAAM,YACN,MAAM,eACN,MAAM,eACN,MAAM,eACN,MAAM,uEAGJ;QAAC,MAAM,EAAC,MAAM,CAAC;QAAC,SAAS,EAAC,KAAK,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAC,EAAE,CAkGtE;CACJ;sCA1bqC,iDAAiD;oBArBnE,sCAAsC;0BAuBhC,wCAAwC;sBAM5C,YAAY;2BArBP,4CAA4C;8BASzC,8CAA8C"}
1
+ {"version":3,"file":"FPDecalSystem.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/decal/v2/FPDecalSystem.js"],"names":[],"mappings":"AAqOA;;GAEG;AACH;IAGI;;;OAGG;IACH,4BA0CC;IAhDD,SAAgB;IASZ,kDAAsC;IAEtC;;;;OAIG;IACH,wBAUE;IAEF;;;;OAIG;IACH,iBAAmC;IAEnC;;;;OAIG;IACH,iBAAsB;IAEtB;;;;OAIG;IACH,oBAAiC;IAGrC,gCASC;IAED,iCAEC;IAED;;;;;;OAMG;IACH,4BALW,MAAM,EAAE,iBACR,MAAM,UACN,MAAM,EAAE,GACN,MAAM,CA2DlB;IAED;;;;;;;;;;;OAWG;IACH,kBAVW,MAAM,YACN,MAAM,YACN,MAAM,eACN,MAAM,eACN,MAAM,eACN,MAAM,uEAGJ;QAAC,MAAM,EAAC,MAAM,CAAC;QAAC,SAAS,EAAC,KAAK,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAC,EAAE,CAkGtE;CACJ;sCAjcqC,iDAAiD;oBAtBnE,sCAAsC;0BAwBhC,wCAAwC;sBAM5C,YAAY;2BAtBP,4CAA4C;8BASzC,8CAA8C"}
@@ -21,6 +21,7 @@ import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js";
21
21
  import { computeStringHash } from "../../../../../core/primitives/strings/computeStringHash.js";
22
22
  import { AssetManager } from "../../../../asset/AssetManager.js";
23
23
  import { GameAssetType } from "../../../../asset/GameAssetType.js";
24
+ import { ImageRGBADataLoader } from "../../../../asset/loaders/image/ImageRGBADataLoader.js";
24
25
  import { AbstractContextSystem } from "../../../../ecs/system/AbstractContextSystem.js";
25
26
  import { SystemEntityContext } from "../../../../ecs/system/SystemEntityContext.js";
26
27
  import { Transform } from "../../../../ecs/transform/Transform.js";
@@ -282,6 +283,13 @@ export class FPDecalSystem extends AbstractContextSystem {
282
283
 
283
284
  async startup(em) {
284
285
  this.__fp_plugin = await this.__engine.plugins.acquire(ForwardPlusRenderingPlugin);
286
+
287
+ const am = this.__engine.assetManager;
288
+ if (!am.hasLoaderForType(GameAssetType.Image)) {
289
+ await am.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
290
+ }
291
+
292
+
285
293
  }
286
294
 
287
295
  async shutdown(em) {
@@ -90,6 +90,10 @@ declare class Trail2D {
90
90
  */
91
91
  build(segment_count: number): void;
92
92
  mesh: import("three").Mesh<import("three").BufferGeometry, import("three").Material | import("three").Material[]>;
93
+ /**
94
+ * Clear the trail's geometry and reset its state.
95
+ */
96
+ clear(): void;
93
97
  fromJSON({ maxAge, width, textureURL, offset, color }: {
94
98
  maxAge?: number;
95
99
  width?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"Trail2D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2D.js"],"names":[],"mappings":";AAmBA;IAkJI,oCAMC;IAtJD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;;OAGG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,QAFU,OAAO,GAAC,IAAI,CAER;IAEd;;;OAGG;IACH,UAFU,mBAAmB,CAEQ;IAErC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAMD;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;OAGG;IACH,qBAFW,MAAM,QAehB;IADG,kHAAgB;IAWpB;;;;;;;;;;;aAcC;IAGD;;;;;;;;;;;;;;;MAQC;IAED;;;OAGG;IACH,cAFW,OAAO,WASjB;IAED;;;;;;;OAOG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CA2CnB;CAEJ;;;;;;sBAvQqB,iCAAiC;oBACnC,kCAAkC;wBAI9B,0BAA0B;oCACd,sCAAsC;0BAPhD,yCAAyC;6BAStC,mBAAmB;kCADd,wBAAwB"}
1
+ {"version":3,"file":"Trail2D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2D.js"],"names":[],"mappings":";AAoBA;IAsKI,oCAMC;IA1KD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;;OAGG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,QAFU,OAAO,GAAC,IAAI,CAER;IAEd;;;OAGG;IACH,UAFU,mBAAmB,CAEQ;IAErC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAMD;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;OAGG;IACH,qBAFW,MAAM,QAehB;IADG,kHAAgB;IAGpB;;OAEG;IACH,cAeC;IAUD;;;;;;;;;;;aAcC;IAGD;;;;;;;;;;;;;;;MAQC;IAED;;;OAGG;IACH,cAFW,OAAO,WASjB;IAED;;;;;;;OAOG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CA2CnB;CAEJ;;;;;;sBA3RqB,iCAAiC;oBACnC,kCAAkC;wBAI9B,0BAA0B;oCACd,sCAAsC;0BAPhD,yCAAyC;6BAStC,mBAAmB;kCADd,wBAAwB"}
@@ -1,3 +1,4 @@
1
+ import { FLOAT32_MAX } from "../../../../core/binary/FLOAT32_MAX.js";
1
2
  import { BvhClient } from "../../../../core/bvh2/bvh3/BvhClient.js";
2
3
  import { Color } from "../../../../core/color/Color.js";
3
4
  import Vector3 from "../../../../core/geom/Vector3.js";
@@ -163,6 +164,26 @@ class Trail2D {
163
164
  this.mesh = mesh;
164
165
  }
165
166
 
167
+ /**
168
+ * Clear the trail's geometry and reset its state.
169
+ */
170
+ clear() {
171
+ const ribbon = this.ribbon;
172
+
173
+ if (ribbon === null) {
174
+ // nothing to clear
175
+ return;
176
+ }
177
+
178
+ const n = ribbon.getCount();
179
+
180
+ for (let i = 0; i < n; i++) {
181
+ ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_AGE, FLOAT32_MAX);
182
+ ribbon.setPointAlpha(i, 0);
183
+ }
184
+
185
+ }
186
+
166
187
  static fromJSON(json) {
167
188
  const r = new Trail2D();
168
189
 
@@ -117,10 +117,10 @@ Architectural references for design choices:
117
117
  call. A 100-block stack hit at the base wakes top-down in one frame
118
118
  rather than over 100 frames of broadphase propagation.
119
119
  - `DisableSleep` on any island member exempts the whole island.
120
- - ContactBegin / Stay / End buffer + dispatch through both
121
- `PhysicsSystem.onContactBegin/Stay/End` Signals and the per-entity
122
- `entity.sendEvent(PhysicsEvents.ContactBegin, ...)` channel (when a
123
- dataset is attached).
120
+ - ContactBegin / Stay / End buffer + dispatch through the per-entity
121
+ `dataset.sendEvent(entity, PhysicsEvents.ContactBegin, ...)` channel the
122
+ sole contact-event path, delivered to each entity of the pair when a
123
+ dataset is attached.
124
124
 
125
125
  ### Islands
126
126
  - **Union-find** with path halving + union by min-index over the awake-body