@woosh/meep-engine 2.141.0 → 2.143.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 (59) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +1 -1
  3. package/src/core/geom/3d/shape/CapsuleShape3D.js +1 -1
  4. package/src/core/geom/3d/shape/SphereShape3D.d.ts +47 -0
  5. package/src/core/geom/3d/shape/SphereShape3D.d.ts.map +1 -0
  6. package/src/core/geom/3d/shape/SphereShape3D.js +127 -0
  7. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts +30 -18
  8. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
  9. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -92
  10. package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
  11. package/src/core/geom/3d/shape/json/shape_to_type.js +4 -2
  12. package/src/core/geom/3d/shape/json/type_adapters.d.ts +12 -3
  13. package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
  14. package/src/core/geom/3d/shape/json/type_adapters.js +16 -4
  15. package/src/core/geom/3d/shape/util/shape_to_visual_entity.js +2 -2
  16. package/src/engine/control/first-person/DESIGN_COLLISION.md +255 -0
  17. package/src/engine/control/first-person/prototype_first_person_controller.js +5 -0
  18. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  19. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +70 -43
  20. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts +12 -22
  21. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts.map +1 -1
  22. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +345 -186
  23. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts +44 -0
  24. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts.map +1 -0
  25. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js +151 -0
  26. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts +14 -0
  27. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts.map +1 -0
  28. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.js +78 -0
  29. package/src/engine/physics/PLAN.md +705 -578
  30. package/src/engine/physics/REVIEW_003.md +166 -0
  31. package/src/engine/physics/constraint/solve_constraints.d.ts +24 -2
  32. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  33. package/src/engine/physics/constraint/solve_constraints.js +402 -165
  34. package/src/engine/physics/ecs/Joint.d.ts +115 -0
  35. package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
  36. package/src/engine/physics/ecs/Joint.js +168 -0
  37. package/src/engine/physics/ecs/JointSerializationAdapter.d.ts +29 -0
  38. package/src/engine/physics/ecs/JointSerializationAdapter.d.ts.map +1 -0
  39. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -0
  40. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  41. package/src/engine/physics/narrowphase/narrowphase_step.js +20 -13
  42. package/src/engine/physics/narrowphase/ray_shapes.d.ts +66 -0
  43. package/src/engine/physics/narrowphase/ray_shapes.d.ts.map +1 -0
  44. package/src/engine/physics/narrowphase/ray_shapes.js +187 -0
  45. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +16 -0
  46. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -0
  47. package/src/engine/physics/narrowphase/refine_ray_concave.js +145 -0
  48. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +39 -0
  49. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -0
  50. package/src/engine/physics/narrowphase/refine_ray_hit.js +78 -0
  51. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +8 -7
  52. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -1
  53. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +8 -7
  54. package/src/engine/physics/queries/raycast.d.ts +11 -9
  55. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  56. package/src/engine/physics/queries/raycast.js +108 -159
  57. package/src/engine/physics/vehicle/RaycastVehicle.d.ts +114 -0
  58. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -0
  59. package/src/engine/physics/vehicle/RaycastVehicle.js +333 -0
@@ -8,42 +8,67 @@ import {
8
8
  } from "../../../core/bvh2/bvh3/BVH.js";
9
9
  import { returnTrue } from "../../../core/function/returnTrue.js";
10
10
  import { aabb3_near_distance_to_intersection_ray_segment } from "../../../core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js";
11
+ import { refine_ray_hit, RAY_REFINE_UNSUPPORTED } from "../narrowphase/refine_ray_hit.js";
11
12
 
12
13
  const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
13
14
 
14
15
  /**
15
- * Walk a single BVH along a ray and update the running nearest hit.
16
+ * Reusable nearest-hit accumulator + scratch. Module-scoped so {@link raycast}
17
+ * doesn't allocate per call; the physics step is single-threaded so contention
18
+ * isn't a concern.
19
+ * @type {{best_t:number, best_body:number}}
20
+ */
21
+ const acc = { best_t: Infinity, best_body: 0 };
22
+ const best_normal = new Float64Array(3); // winning hit's world normal
23
+ const cand_normal = new Float64Array(3); // per-leaf candidate normal
24
+
25
+ /**
26
+ * Outward AABB-face normal at a hit point — the fallback normal for a leaf
27
+ * whose shape has no exact ray test (composite convex). The hit point is
28
+ * projected into the AABB's normalised local space (centre = 0, faces = ±1);
29
+ * the dominant component's axis is the entry face and its sign the outward
30
+ * normal.
31
+ */
32
+ function aabb_face_normal(out, min_x, min_y, min_z, max_x, max_y, max_z, hx, hy, hz) {
33
+ const cx = (min_x + max_x) * 0.5;
34
+ const cy = (min_y + max_y) * 0.5;
35
+ const cz = (min_z + max_z) * 0.5;
36
+ const px = (hx - cx) * 2 / (max_x - min_x);
37
+ const py = (hy - cy) * 2 / (max_y - min_y);
38
+ const pz = (hz - cz) * 2 / (max_z - min_z);
39
+ const apx = px < 0 ? -px : px;
40
+ const apy = py < 0 ? -py : py;
41
+ const apz = pz < 0 ? -pz : pz;
42
+ if (apx >= apy && apx >= apz) { out[0] = px >= 0 ? 1 : -1; out[1] = 0; out[2] = 0; }
43
+ else if (apy >= apz) { out[0] = 0; out[1] = py >= 0 ? 1 : -1; out[2] = 0; }
44
+ else { out[0] = 0; out[1] = 0; out[2] = pz >= 0 ? 1 : -1; }
45
+ }
46
+
47
+ /**
48
+ * Walk a single BVH along a ray, refining each crossing leaf against its true
49
+ * shape geometry and keeping the nearest confirmed hit.
16
50
  *
17
- * Hit refinement is broadphase-only: the returned `t` is the distance to
18
- * the inflated leaf AABB. Narrowphase shape refinement is a follow-up.
51
+ * Pruning stays on the *inflated leaf AABB* entry distance `t_near`: a shape
52
+ * hit is always at or beyond its tight AABB entry, which is at or beyond the
53
+ * inflated-AABB entry, so a subtree whose AABB entry is past the current best
54
+ * cannot contain a closer hit. A leaf whose ray crosses the fat AABB but misses
55
+ * the true shape contributes nothing — the correctness gain over broadphase.
19
56
  *
20
- * @param {BVH} bvh
21
- * @param {number} root
22
- * @param {number} ox
23
- * @param {number} oy
24
- * @param {number} oz
25
- * @param {number} dx
26
- * @param {number} dy
27
- * @param {number} dz
28
- * @param {number} inv_dx
29
- * @param {number} inv_dy
30
- * @param {number} inv_dz
57
+ * @param {BVH} bvh @param {number} root
58
+ * @param {PhysicsSystem} system
59
+ * @param {number} ox @param {number} oy @param {number} oz ray origin
60
+ * @param {number} dx @param {number} dy @param {number} dz ray dir (unit)
61
+ * @param {number} inv_dx @param {number} inv_dy @param {number} inv_dz
31
62
  * @param {number} max_distance
32
- * @param {{best_t:number, best_body:number, best_node:number, best_bvh:BVH|null}} acc
33
- * @param {(body_id:number)=>boolean} test per-leaf filter
63
+ * @param {(entity:number, collider:Collider)=>boolean} filter
34
64
  */
35
65
  function bvh_raycast_nearest(
36
- bvh, root,
37
- ox, oy, oz,
38
- dx, dy, dz,
66
+ bvh, root, system,
67
+ ox, oy, oz, dx, dy, dz,
39
68
  inv_dx, inv_dy, inv_dz,
40
- max_distance,
41
- acc,
42
- test
69
+ max_distance, filter
43
70
  ) {
44
- if (root === NULL_NODE) {
45
- return;
46
- }
71
+ if (root === NULL_NODE) return;
47
72
 
48
73
  const float32 = bvh.__data_float32;
49
74
  const uint32 = bvh.__data_uint32;
@@ -66,181 +91,105 @@ function bvh_raycast_nearest(
66
91
  );
67
92
 
68
93
  // No intersection or this subtree can't beat the best known hit — prune.
69
- if (t_near >= acc.best_t) {
70
- continue;
71
- }
94
+ if (t_near >= acc.best_t) continue;
72
95
 
73
96
  const child_1 = uint32[address + COLUMN_CHILD_1];
74
97
  if (child_1 !== NULL_NODE) {
75
98
  stack[pointer++] = uint32[address + COLUMN_CHILD_2];
76
99
  stack[pointer++] = child_1;
77
- } else {
78
- const body_id = uint32[address + COLUMN_USER_DATA];
79
- if (test(body_id)) {
100
+ continue;
101
+ }
102
+
103
+ // Leaf — refine against the true shape.
104
+ const body_id = uint32[address + COLUMN_USER_DATA];
105
+ const entity = system.entityOf(body_id);
106
+ if (entity < 0) continue; // unlinked concurrently
107
+ const idx = system.__index_of(body_id);
108
+ const collider = system.__primary_collider(idx);
109
+ if (collider === null) continue;
110
+ if (!filter(entity, collider)) continue;
111
+
112
+ const tr = system.__transforms[idx];
113
+ const refined = refine_ray_hit(
114
+ collider.shape, tr.position, tr.rotation,
115
+ ox, oy, oz, dx, dy, dz, acc.best_t, cand_normal
116
+ );
117
+
118
+ if (refined === RAY_REFINE_UNSUPPORTED) {
119
+ // No exact ray test for this shape — keep the broadphase AABB hit.
120
+ if (t_near < acc.best_t) {
80
121
  acc.best_t = t_near;
81
122
  acc.best_body = body_id;
82
- acc.best_node = node;
83
- acc.best_bvh = bvh;
123
+ aabb_face_normal(best_normal,
124
+ float32[address], float32[address + 1], float32[address + 2],
125
+ float32[address + 3], float32[address + 4], float32[address + 5],
126
+ ox + dx * t_near, oy + dy * t_near, oz + dz * t_near);
84
127
  }
128
+ } else if (refined < acc.best_t) { // a refined miss is Infinity → never wins
129
+ acc.best_t = refined;
130
+ acc.best_body = body_id;
131
+ best_normal[0] = cand_normal[0];
132
+ best_normal[1] = cand_normal[1];
133
+ best_normal[2] = cand_normal[2];
85
134
  }
86
135
  }
87
136
 
88
137
  stack.pointer = stack_top;
89
138
  }
90
139
 
91
- /**
92
- * Reusable nearest-hit accumulator. Module-scoped so {@link raycast} doesn't
93
- * allocate per call. The physics step is single-threaded so contention isn't
94
- * a concern.
95
- * @type {{best_t:number, best_body:number, best_node:number, best_bvh:BVH|null}}
96
- */
97
- const acc = { best_t: Infinity, best_body: 0, best_node: 0, best_bvh: null };
98
-
99
- /**
100
- * Reusable closure bound to the current system + user filter so we don't
101
- * allocate a new function object per call. Re-wired at the top of each
102
- * {@link raycast} invocation. The BVH traversal sees a stable identity
103
- * which helps V8 keep the call site monomorphic.
104
- *
105
- * Carries state via the module-scoped `bound_*` slots below.
106
- * @type {(body_id:number)=>boolean}
107
- */
108
- const bound_test = (body_id) => {
109
- const system = bound_system;
110
- const entity = system.entityOf(body_id);
111
- if (entity < 0) return false;
112
- // v1 limitation: when a multi-collider body's BVH leaf is hit, we pass
113
- // the body's primary (first-attached) collider rather than the specific
114
- // collider the ray actually crossed. Sufficient for entity-level
115
- // filtering; per-collider filtering needs the BVH user_data scheme to
116
- // encode collider index too — future work.
117
- const collider = system.__primary_collider(system.__index_of(body_id));
118
- return bound_filter(entity, collider);
119
- };
120
-
121
- let bound_system = null;
122
- let bound_filter = returnTrue;
123
-
124
140
  /**
125
141
  * Raycast against both broadphase trees (static + dynamic) of a
126
- * {@link PhysicsSystem}, filling `result` with the nearest hit and returning
127
- * `true` on hit, `false` on miss.
142
+ * {@link PhysicsSystem}, refined against each candidate's true shape geometry.
143
+ * Fills `result` with the nearest hit and returns `true` on hit, `false` on
144
+ * miss. `result.t` is the exact surface distance and `result.normal` the true
145
+ * surface normal for sphere / box / capsule / mesh / heightmap colliders;
146
+ * composite convex shapes (no exact ray test yet) fall back to the broadphase
147
+ * AABB hit + AABB-face normal.
128
148
  *
129
- * Hit normal is the AABB face normalexact for AABB-shaped colliders,
130
- * a stable approximation for general convex shapes (an upcoming narrowphase
131
- * refinement pass will replace this with the true shape normal at the same
132
- * call site, no API change).
149
+ * Multi-collider bodies resolve their primary (first-attached) colliderthe
150
+ * BVH leaf encodes only `body_id`; per-collider rays need the leaf user-data to
151
+ * carry the collider index (future work).
133
152
  *
134
153
  * @param {PhysicsSystem} system
135
154
  * @param {Ray3} ray origin + unit direction + `tMax`
136
155
  * @param {PhysicsSurfacePoint} result populated on hit; untouched on miss
137
- * @param {(entity:number, collider:Collider)=>boolean} [filter] mandatory in
138
- * contract; defaults to {@link returnTrue} (accept everything). Called once
139
- * per BVH leaf that crosses the ray.
156
+ * @param {(entity:number, collider:Collider)=>boolean} [filter] called once per
157
+ * crossing leaf; defaults to {@link returnTrue}.
140
158
  * @returns {boolean} true on hit, false on miss
141
159
  */
142
160
  export function raycast(system, ray, result, filter = returnTrue) {
143
- const ox = ray.origin_x;
144
- const oy = ray.origin_y;
145
- const oz = ray.origin_z;
146
- const dx = ray.direction_x;
147
- const dy = ray.direction_y;
148
- const dz = ray.direction_z;
161
+ const ox = ray.origin_x, oy = ray.origin_y, oz = ray.origin_z;
162
+ const dx = ray.direction_x, dy = ray.direction_y, dz = ray.direction_z;
149
163
  const max_distance = ray.tMax;
150
164
 
151
165
  acc.best_t = max_distance;
152
166
  acc.best_body = 0;
153
- acc.best_node = 0;
154
- acc.best_bvh = null;
167
+ best_normal[0] = 0; best_normal[1] = 0; best_normal[2] = 0;
155
168
 
156
- bound_system = system;
157
- bound_filter = filter;
158
-
159
- const inv_dx = 1 / dx;
160
- const inv_dy = 1 / dy;
161
- const inv_dz = 1 / dz;
169
+ const inv_dx = 1 / dx, inv_dy = 1 / dy, inv_dz = 1 / dz;
162
170
 
163
171
  bvh_raycast_nearest(
164
- system.staticBvh, system.staticBvh.root,
165
- ox, oy, oz,
166
- dx, dy, dz,
167
- inv_dx, inv_dy, inv_dz,
168
- max_distance,
169
- acc, bound_test
172
+ system.staticBvh, system.staticBvh.root, system,
173
+ ox, oy, oz, dx, dy, dz, inv_dx, inv_dy, inv_dz, max_distance, filter
170
174
  );
171
-
172
175
  bvh_raycast_nearest(
173
- system.dynamicBvh, system.dynamicBvh.root,
174
- ox, oy, oz,
175
- dx, dy, dz,
176
- inv_dx, inv_dy, inv_dz,
177
- max_distance,
178
- acc, bound_test
176
+ system.dynamicBvh, system.dynamicBvh.root, system,
177
+ ox, oy, oz, dx, dy, dz, inv_dx, inv_dy, inv_dz, max_distance, filter
179
178
  );
180
179
 
181
- if (acc.best_bvh === null || acc.best_t >= max_distance) {
182
- return false;
183
- }
180
+ // Any hit updates best_t to strictly below max_distance (both the refined
181
+ // and AABB-fallback paths require t < current best); a miss leaves it at
182
+ // max_distance. This is body_id-agnostic — the first body packs to id 0.
183
+ if (acc.best_t >= max_distance) return false;
184
184
 
185
185
  const entity = system.entityOf(acc.best_body);
186
- if (entity < 0) {
187
- // Body was unlinked concurrently; treat as a miss.
188
- return false;
189
- }
186
+ if (entity < 0) return false; // body unlinked concurrently → treat as miss
190
187
 
191
- // Hit position in world space.
192
188
  const t = acc.best_t;
193
- const hx = ox + dx * t;
194
- const hy = oy + dy * t;
195
- const hz = oz + dz * t;
196
-
197
- // AABB face normal at the hit point. Read the best leaf's AABB back from
198
- // its BVH; project the hit point into the AABB's normalised local space
199
- // (centre = 0, faces = ±1); the dominant component is the entry-face
200
- // axis, its sign gives the outward normal.
201
- const bvh = acc.best_bvh;
202
- const node_address = acc.best_node * ELEMENT_WORD_COUNT;
203
- const f32 = bvh.__data_float32;
204
- const aabb_min_x = f32[node_address];
205
- const aabb_min_y = f32[node_address + 1];
206
- const aabb_min_z = f32[node_address + 2];
207
- const aabb_max_x = f32[node_address + 3];
208
- const aabb_max_y = f32[node_address + 4];
209
- const aabb_max_z = f32[node_address + 5];
210
-
211
- const cx = (aabb_min_x + aabb_max_x) * 0.5;
212
- const cy = (aabb_min_y + aabb_max_y) * 0.5;
213
- const cz = (aabb_min_z + aabb_max_z) * 0.5;
214
- // Multiplicative inverse of the half-extent saves one divide per axis.
215
- // A degenerate AABB (zero extent on an axis) is impossible for live BVH
216
- // leaves — physics shapes always have non-zero bounding extent.
217
- const inv_half_x = 2 / (aabb_max_x - aabb_min_x);
218
- const inv_half_y = 2 / (aabb_max_y - aabb_min_y);
219
- const inv_half_z = 2 / (aabb_max_z - aabb_min_z);
220
-
221
- const px = (hx - cx) * inv_half_x;
222
- const py = (hy - cy) * inv_half_y;
223
- const pz = (hz - cz) * inv_half_z;
224
-
225
- const apx = px < 0 ? -px : px;
226
- const apy = py < 0 ? -py : py;
227
- const apz = pz < 0 ? -pz : pz;
228
-
229
- let nx, ny, nz;
230
- if (apx >= apy && apx >= apz) {
231
- nx = px >= 0 ? 1 : -1; ny = 0; nz = 0;
232
- } else if (apy >= apz) {
233
- nx = 0; ny = py >= 0 ? 1 : -1; nz = 0;
234
- } else {
235
- nx = 0; ny = 0; nz = pz >= 0 ? 1 : -1;
236
- }
237
-
238
- // Fill result. Direct typed-array writes — Vector3 extends Float32Array,
239
- // and there are no observers subscribed to a query-result vector.
240
189
  const rp = result.position;
241
190
  const rn = result.normal;
242
- rp[0] = hx; rp[1] = hy; rp[2] = hz;
243
- rn[0] = nx; rn[1] = ny; rn[2] = nz;
191
+ rp[0] = ox + dx * t; rp[1] = oy + dy * t; rp[2] = oz + dz * t;
192
+ rn[0] = best_normal[0]; rn[1] = best_normal[1]; rn[2] = best_normal[2];
244
193
  result.t = t;
245
194
  result.entity = entity;
246
195
  result.body_id = acc.best_body;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * One wheel's config + per-frame runtime state. The runtime fields
3
+ * (`inContact`, `compression`, contact point/normal, `rotation`, …) are read by
4
+ * rendering / gameplay; do not write them.
5
+ */
6
+ export class Wheel {
7
+ /** Mount point on the chassis, local. @type {Float64Array} */
8
+ localPosition: Float64Array;
9
+ /** Suspension direction (local, unit, points "down"). @type {Float64Array} */
10
+ localSuspensionDir: Float64Array;
11
+ /** Forward / rolling direction (local, unit). @type {Float64Array} */
12
+ localForward: Float64Array;
13
+ suspensionRestLength: number;
14
+ suspensionStiffness: number;
15
+ suspensionDamping: number;
16
+ suspensionMaxForce: number;
17
+ radius: number;
18
+ friction: number;
19
+ steered: boolean;
20
+ driven: boolean;
21
+ inContact: boolean;
22
+ suspensionLength: number;
23
+ compression: number;
24
+ /** World contact point. @type {Float64Array} */
25
+ contactPoint: Float64Array;
26
+ /** World contact normal. @type {Float64Array} */
27
+ contactNormal: Float64Array;
28
+ /** Body id of the surface under the wheel, or −1. */
29
+ contactBodyId: number;
30
+ /** Current steering angle (rad), set via {@link RaycastVehicle#setSteering}. */
31
+ steering: number;
32
+ /** Accumulated spin angle (rad) for rendering. */
33
+ rotation: number;
34
+ /** Forward ground speed at the contact (m/s). */
35
+ forwardSpeed: number;
36
+ /** Suspension force magnitude applied this frame (N). */
37
+ suspensionForce: number;
38
+ }
39
+ export class RaycastVehicle {
40
+ /**
41
+ * @param {PhysicsSystem} system
42
+ * @param {RigidBody} chassisBody the chassis rigid body (Dynamic)
43
+ * @param {Transform} chassisTransform its world transform
44
+ */
45
+ constructor(system: PhysicsSystem, chassisBody: RigidBody, chassisTransform: Transform);
46
+ system: PhysicsSystem;
47
+ chassisBody: RigidBody;
48
+ chassisTransform: Transform;
49
+ /** @type {Wheel[]} */
50
+ wheels: Wheel[];
51
+ __driveForce: number;
52
+ __brakeForce: number;
53
+ __drivenCount: number;
54
+ __ray: Ray3;
55
+ __hit: PhysicsSurfacePoint;
56
+ __filter: (entity: any, collider: any) => boolean;
57
+ __force: {
58
+ x: number;
59
+ y: number;
60
+ z: number;
61
+ };
62
+ __point: {
63
+ x: number;
64
+ y: number;
65
+ z: number;
66
+ };
67
+ /**
68
+ * Add a wheel. Returns the created {@link Wheel} (also pushed to
69
+ * {@link wheels}); set any further config fields on it before stepping.
70
+ *
71
+ * @param {object} opts
72
+ * @param {number[]} opts.localPosition mount point on the chassis, local.
73
+ * @param {number[]} [opts.localSuspensionDir] unit "down", default (0,−1,0).
74
+ * @param {number[]} [opts.localForward] unit rolling dir, default (0,0,1).
75
+ * @param {number} [opts.suspensionRestLength]
76
+ * @param {number} [opts.suspensionStiffness] N/m.
77
+ * @param {number} [opts.suspensionDamping] N·s/m.
78
+ * @param {number} [opts.suspensionMaxForce] N (clamp).
79
+ * @param {number} [opts.radius]
80
+ * @param {number} [opts.friction] tyre friction coefficient μ.
81
+ * @param {boolean} [opts.steered]
82
+ * @param {boolean} [opts.driven]
83
+ * @returns {Wheel}
84
+ */
85
+ addWheel(opts: {
86
+ localPosition: number[];
87
+ localSuspensionDir?: number[];
88
+ localForward?: number[];
89
+ suspensionRestLength?: number;
90
+ suspensionStiffness?: number;
91
+ suspensionDamping?: number;
92
+ suspensionMaxForce?: number;
93
+ radius?: number;
94
+ friction?: number;
95
+ steered?: boolean;
96
+ driven?: boolean;
97
+ }): Wheel;
98
+ /** Set the steering angle (rad) on every steered wheel. */
99
+ setSteering(angle: any): void;
100
+ /** Total engine drive force (N), split evenly across the driven wheels. */
101
+ setDriveForce(force: any): void;
102
+ /** Brake force (N) per wheel, opposing each wheel's forward motion. */
103
+ setBrake(force: any): void;
104
+ /**
105
+ * Step the vehicle: cast suspension rays, apply suspension + tyre forces.
106
+ * Call once per frame **before** `PhysicsSystem.fixedUpdate(dt)` with the
107
+ * same `dt`.
108
+ * @param {number} dt
109
+ */
110
+ update(dt: number): void;
111
+ }
112
+ import { Ray3 } from "../../../core/geom/3d/ray/Ray3.js";
113
+ import { PhysicsSurfacePoint } from "../queries/PhysicsSurfacePoint.js";
114
+ //# sourceMappingURL=RaycastVehicle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaycastVehicle.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/vehicle/RaycastVehicle.js"],"names":[],"mappings":"AAyEA;;;;GAIG;AACH;IAGQ,8DAA8D;IAC9D,eAD8C,YAAY,CAClB;IACxC,8EAA8E;IAC9E,oBAD8D,YAAY,CACnB;IACvD,sEAAsE;IACtE,cADsD,YAAY,CAClB;IAChD,6BAA+B;IAC/B,4BAA8B;IAC9B,0BAA2B;IAC3B,2BAAkC;IAClC,eAAiB;IACjB,iBAAmB;IACnB,iBAAoB;IACpB,gBAAmB;IAGnB,mBAAsB;IACtB,yBAAiD;IACjD,oBAAoB;IACpB,gDAAgD;IAChD,cADgC,YAAY,CACL;IACvC,iDAAiD;IACjD,eADiC,YAAY,CACI;IACjD,qDAAqD;IACrD,sBAAuB;IACvB,gFAAgF;IAChF,iBAAiB;IACjB,kDAAkD;IAClD,iBAAiB;IACjB,iDAAiD;IACjD,qBAAqB;IACrB,yDAAyD;IACzD,wBAAwB;CAE/B;AAED;IACI;;;;OAIG;IACH,wFAoBC;IAnBG,sBAAoB;IACpB,uBAA8B;IAC9B,4BAAwC;IAExC,sBAAsB;IACtB,QADW,KAAK,EAAE,CACF;IAEhB,qBAAqB;IACrB,qBAAqB;IACrB,sBAAsB;IAEtB,YAAuB;IACvB,2BAAsC;IAEtC,kDAAmF;IAGnF;;;;MAAmC;IACnC;;;;MAAmC;IAGvC;;;;;;;;;;;;;;;;;OAiBG;IACH;QAb0B,aAAa,EAA5B,MAAM,EAAE;QACQ,kBAAkB,GAAlC,MAAM,EAAE;QACQ,YAAY,GAA5B,MAAM,EAAE;QACM,oBAAoB,GAAlC,MAAM;QACQ,mBAAmB,GAAjC,MAAM;QACQ,iBAAiB,GAA/B,MAAM;QACQ,kBAAkB,GAAhC,MAAM;QACQ,MAAM,GAApB,MAAM;QACQ,QAAQ,GAAtB,MAAM;QACS,OAAO,GAAtB,OAAO;QACQ,MAAM,GAArB,OAAO;QACL,KAAK,CA0BjB;IAED,2DAA2D;IAC3D,8BAIC;IAED,2EAA2E;IAC3E,gCAAmD;IAEnD,uEAAuE;IACvE,2BAA8C;IAE9C;;;;;OAKG;IACH,WAFW,MAAM,QA6HhB;CACJ;qBA5UoB,mCAAmC;oCACpB,mCAAmC"}