@woosh/meep-engine 3.25.0 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +59 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.js +285 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +57 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +102 -0
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.js +64 -0
- package/src/engine/physics/cloth/MEASUREMENTS.md +582 -0
- package/src/engine/physics/cloth/PLAN.md +330 -49
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +84 -17
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
- package/src/engine/physics/cloth/collider/ClothColliderIndex.js +451 -42
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +65 -7
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +72 -8
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +25 -4
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +75 -8
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +41 -4
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +207 -63
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +10 -0
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +17 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +27 -3
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +28 -3
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +75 -10
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +131 -12
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +19 -0
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +249 -201
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -0
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +62 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.js +18 -10
- package/src/engine/physics/cloth/playground/collide_main.js +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +29 -8
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.js +37 -9
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +12 -2
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_solver_constants.js +12 -2
- package/src/engine/physics/cloth/solver/cloth_step_instance.js +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +9 -2
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +9 -2
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +23 -9
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/AbstractClothWind.js +23 -9
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +7 -5
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/ClothAmbientWind.js +12 -9
- package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/ClothFluidWind.js +5 -2
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +7 -2
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/cloth_sample_air.js +12 -5
package/package.json
CHANGED
|
@@ -158,6 +158,65 @@ export class HeightMapShape3D extends AbstractShape3D {
|
|
|
158
158
|
* site is forced to handle heightmaps explicitly.
|
|
159
159
|
*/
|
|
160
160
|
support(result: any, result_offset: any, direction_x: any, direction_y: any, direction_z: any): void;
|
|
161
|
+
/**
|
|
162
|
+
* Nearest ray hit against the height-field SURFACE, in the shape's body-local
|
|
163
|
+
* frame. Writes `[px, py, pz, nx, ny, nz]` into `hit` and returns true; a miss
|
|
164
|
+
* returns false and leaves `hit` untouched.
|
|
165
|
+
*
|
|
166
|
+
* ## What is hit
|
|
167
|
+
*
|
|
168
|
+
* The surface, not the solid. The class models a closed volume (floor at h=0,
|
|
169
|
+
* height field above), but a ray meets only the height field — there is no
|
|
170
|
+
* floor quad and no side skirt, so a ray that passes under the terrain or
|
|
171
|
+
* enters through the footprint edge below the surface reports a miss. That is
|
|
172
|
+
* the same body {@link heightmap_enumerate_triangles} hands the contact
|
|
173
|
+
* narrowphase, and the same body the terrain renderer draws; a raycast that
|
|
174
|
+
* answered for the skirt would disagree with both. Mirrors
|
|
175
|
+
* {@link MeshShape3D#raycast}, which is likewise surface-only.
|
|
176
|
+
*
|
|
177
|
+
* Precisely the same triangles, too: the traversal reads its corner heights at
|
|
178
|
+
* the identical `i / seg_u` / `j / seg_v` UVs through the identical Catmull-Rom
|
|
179
|
+
* filter, and splits each sub-cell on the same lower-left diagonal
|
|
180
|
+
* (`(A,D,B)`, `(D,C,B)`), so a cast and a contact resolve against a surface
|
|
181
|
+
* that agrees bit for bit — {@link tessellation} included.
|
|
182
|
+
*
|
|
183
|
+
* NORMAL: the triangle's geometric normal, flipped to face the incoming ray
|
|
184
|
+
* (the shared {@link tri3_raycast} convention). A cast from below the terrain
|
|
185
|
+
* therefore reports a downward normal, not the surface's true upward one.
|
|
186
|
+
*
|
|
187
|
+
* A hit landing exactly ON a sub-cell boundary gets the normal of one of the
|
|
188
|
+
* facets meeting there, and which one follows from traversal order: this walks
|
|
189
|
+
* the grid along the ray, the narrowphase enumerates it row-major, so on a grid
|
|
190
|
+
* line — and especially a grid corner, where four facets meet — the two can
|
|
191
|
+
* name different facets. The hit POINT still agrees; a faceted surface simply
|
|
192
|
+
* has no single normal at a seam. Measure-zero, and called out because it is
|
|
193
|
+
* the only place the two answers part company.
|
|
194
|
+
*
|
|
195
|
+
* `size.y` does NOT bound the query. Sampler values are not clamped to it (see
|
|
196
|
+
* the class docstring), so clipping the ray to the height range that
|
|
197
|
+
* {@link compute_bounding_box} reports could reject real surface standing above
|
|
198
|
+
* it; only the footprint is clipped against.
|
|
199
|
+
*
|
|
200
|
+
* ## Cost
|
|
201
|
+
*
|
|
202
|
+
* A 2-D DDA over the `(W−1)·N × (H−1)·N` sub-cell grid, two triangles per cell,
|
|
203
|
+
* stopping at the first cell that hits — cells are visited in increasing ray
|
|
204
|
+
* order and a sub-cell's triangles project exactly onto its own footprint
|
|
205
|
+
* rectangle, so nothing behind can be nearer. O(cells the ray crosses), not
|
|
206
|
+
* O(cells in the ray's bounding box): a long diagonal cast costs the grid's
|
|
207
|
+
* diagonal rather than its area.
|
|
208
|
+
*
|
|
209
|
+
* Four Catmull-Rom evaluations per cell, uncached — deliberately unlike
|
|
210
|
+
* {@link heightmap_enumerate_triangles}, whose corner-row cache exists because
|
|
211
|
+
* the contact narrowphase re-samples the same rect every substep. A ray walks
|
|
212
|
+
* a diagonal, visits each cell once, and stops early; there is nothing to
|
|
213
|
+
* amortise against.
|
|
214
|
+
*
|
|
215
|
+
* @param {Float32Array|number[]} hit out: [px, py, pz, nx, ny, nz]
|
|
216
|
+
* @param {Ray3} ray body-local ray (unit direction; `ray[6]` = tMax)
|
|
217
|
+
* @returns {boolean} whether anything was hit
|
|
218
|
+
*/
|
|
219
|
+
raycast(hit: Float32Array | number[], ray: Ray3): boolean;
|
|
161
220
|
sample_random_point_in_volume(result: any, result_offset: any, random: any): void;
|
|
162
221
|
/**
|
|
163
222
|
* @param {HeightMapShape3D} other
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeightMapShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/HeightMapShape3D.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HeightMapShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/HeightMapShape3D.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH;IA4DI;;;;;;;;;;;OAWG;IACH,wCATW,MAAM,UACN,MAAM,UACN,MAAM,gBACN,OAAO,iBACP,MAAM,GAGJ,gBAAgB,CAqB5B;IA1FD;;;;;OAKG;IACH,sBAFU,OAAO,CAEkB;IAEnC;;;;;;;OAOG;IACH,eAFU,OAAO,CAEW;IAE5B;;;;;OAKG;IACH,SAFU,YAAY,IAAI,CAEX;IAEf;;;;;;;;;;;;;;;;OAgBG;IACH,cAFU,MAAM,CAEC;IAEjB;;;;;OAKG;IACH,eAA6B;IAG7B,6BAA2B;IAE3B,6BAA2B;IAC3B,6BAA2B;IAmC3B;;;;;;;;;OASG;IACH,sBA+CC;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,OACN,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;OAQG;IACH,8BALW,MAAM,MACN,MAAM,MACN,MAAM,GACJ,MAAM,CAelB;IAED,wCA8BC;IAED,oCA2BC;IAED;;;;;;;;OAQG;IACH,6CAmBC;IAED;;;;OAIG;IACH,4DA2BC;IAED;;;;;;;;;OASG;IACH,qGAEC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyDG;IACH,aAJW,YAAY,GAAC,MAAM,EAAE,cAEnB,OAAO,CAyNnB;IAED,kFAiBC;IA6CD;;;OAGG;IACH,cAHW,gBAAgB,GACd,OAAO,CAUnB;IAWL;;;;;;OAMG;IACH,6BAFU,OAAO,CAE4B;CAT5C;gCA5tB+B,sBAAsB;wBAD9B,kBAAkB"}
|
|
@@ -4,10 +4,20 @@ import { assert } from "../../../assert.js";
|
|
|
4
4
|
import { clamp } from "../../../math/clamp.js";
|
|
5
5
|
import { optional_equality } from "../../../model/object/optional_equality.js";
|
|
6
6
|
import { optional_hash } from "../../../model/object/optional_hash.js";
|
|
7
|
+
import { tri3_raycast } from "../triangle/tri3_raycast.js";
|
|
7
8
|
import { v3_length } from "../../vec3/v3_length.js";
|
|
8
9
|
import { Vector3 } from "../../Vector3.js";
|
|
9
10
|
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Below this, a ray's footprint-plane velocity component counts as zero in
|
|
14
|
+
* {@link HeightMapShape3D#raycast}'s slab clip — the reciprocal a smaller value
|
|
15
|
+
* would produce is not a usable slab distance. Matches the concave narrowphase's
|
|
16
|
+
* `refine_ray_concave`, so the two agree on rays that graze the footprint edge.
|
|
17
|
+
* @type {number}
|
|
18
|
+
*/
|
|
19
|
+
const RAY_SLAB_EPSILON = 1e-12;
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* Heightmap shape, intended primarily for terrain.
|
|
13
23
|
*
|
|
@@ -371,6 +381,281 @@ export class HeightMapShape3D extends AbstractShape3D {
|
|
|
371
381
|
throw new Error("HeightMapShape3D.support: heightmaps are non-convex; the narrowphase must dispatch grid-traversal instead.");
|
|
372
382
|
}
|
|
373
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Nearest ray hit against the height-field SURFACE, in the shape's body-local
|
|
386
|
+
* frame. Writes `[px, py, pz, nx, ny, nz]` into `hit` and returns true; a miss
|
|
387
|
+
* returns false and leaves `hit` untouched.
|
|
388
|
+
*
|
|
389
|
+
* ## What is hit
|
|
390
|
+
*
|
|
391
|
+
* The surface, not the solid. The class models a closed volume (floor at h=0,
|
|
392
|
+
* height field above), but a ray meets only the height field — there is no
|
|
393
|
+
* floor quad and no side skirt, so a ray that passes under the terrain or
|
|
394
|
+
* enters through the footprint edge below the surface reports a miss. That is
|
|
395
|
+
* the same body {@link heightmap_enumerate_triangles} hands the contact
|
|
396
|
+
* narrowphase, and the same body the terrain renderer draws; a raycast that
|
|
397
|
+
* answered for the skirt would disagree with both. Mirrors
|
|
398
|
+
* {@link MeshShape3D#raycast}, which is likewise surface-only.
|
|
399
|
+
*
|
|
400
|
+
* Precisely the same triangles, too: the traversal reads its corner heights at
|
|
401
|
+
* the identical `i / seg_u` / `j / seg_v` UVs through the identical Catmull-Rom
|
|
402
|
+
* filter, and splits each sub-cell on the same lower-left diagonal
|
|
403
|
+
* (`(A,D,B)`, `(D,C,B)`), so a cast and a contact resolve against a surface
|
|
404
|
+
* that agrees bit for bit — {@link tessellation} included.
|
|
405
|
+
*
|
|
406
|
+
* NORMAL: the triangle's geometric normal, flipped to face the incoming ray
|
|
407
|
+
* (the shared {@link tri3_raycast} convention). A cast from below the terrain
|
|
408
|
+
* therefore reports a downward normal, not the surface's true upward one.
|
|
409
|
+
*
|
|
410
|
+
* A hit landing exactly ON a sub-cell boundary gets the normal of one of the
|
|
411
|
+
* facets meeting there, and which one follows from traversal order: this walks
|
|
412
|
+
* the grid along the ray, the narrowphase enumerates it row-major, so on a grid
|
|
413
|
+
* line — and especially a grid corner, where four facets meet — the two can
|
|
414
|
+
* name different facets. The hit POINT still agrees; a faceted surface simply
|
|
415
|
+
* has no single normal at a seam. Measure-zero, and called out because it is
|
|
416
|
+
* the only place the two answers part company.
|
|
417
|
+
*
|
|
418
|
+
* `size.y` does NOT bound the query. Sampler values are not clamped to it (see
|
|
419
|
+
* the class docstring), so clipping the ray to the height range that
|
|
420
|
+
* {@link compute_bounding_box} reports could reject real surface standing above
|
|
421
|
+
* it; only the footprint is clipped against.
|
|
422
|
+
*
|
|
423
|
+
* ## Cost
|
|
424
|
+
*
|
|
425
|
+
* A 2-D DDA over the `(W−1)·N × (H−1)·N` sub-cell grid, two triangles per cell,
|
|
426
|
+
* stopping at the first cell that hits — cells are visited in increasing ray
|
|
427
|
+
* order and a sub-cell's triangles project exactly onto its own footprint
|
|
428
|
+
* rectangle, so nothing behind can be nearer. O(cells the ray crosses), not
|
|
429
|
+
* O(cells in the ray's bounding box): a long diagonal cast costs the grid's
|
|
430
|
+
* diagonal rather than its area.
|
|
431
|
+
*
|
|
432
|
+
* Four Catmull-Rom evaluations per cell, uncached — deliberately unlike
|
|
433
|
+
* {@link heightmap_enumerate_triangles}, whose corner-row cache exists because
|
|
434
|
+
* the contact narrowphase re-samples the same rect every substep. A ray walks
|
|
435
|
+
* a diagonal, visits each cell once, and stops early; there is nothing to
|
|
436
|
+
* amortise against.
|
|
437
|
+
*
|
|
438
|
+
* @param {Float32Array|number[]} hit out: [px, py, pz, nx, ny, nz]
|
|
439
|
+
* @param {Ray3} ray body-local ray (unit direction; `ray[6]` = tMax)
|
|
440
|
+
* @returns {boolean} whether anything was hit
|
|
441
|
+
*/
|
|
442
|
+
raycast(hit, ray) {
|
|
443
|
+
const sampler = this.sampler;
|
|
444
|
+
|
|
445
|
+
if (sampler === null) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const size_x = this.size[0];
|
|
450
|
+
const size_z = this.size[2];
|
|
451
|
+
|
|
452
|
+
if (size_x <= 0 || size_z <= 0) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// The collision grid heightmap_enumerate_triangles builds. A sampler too
|
|
457
|
+
// thin to hold one cell on an axis (width or height < 2), or tessellation
|
|
458
|
+
// 0, collapses it — no triangles exist, so nothing can be hit.
|
|
459
|
+
const seg_u = (sampler.width - 1) * this.tessellation;
|
|
460
|
+
const seg_v = (sampler.height - 1) * this.tessellation;
|
|
461
|
+
|
|
462
|
+
if (seg_u < 1 || seg_v < 1) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
this._ensure_basis();
|
|
467
|
+
|
|
468
|
+
const b = this._basis;
|
|
469
|
+
|
|
470
|
+
const o_x = ray[0], o_y = ray[1], o_z = ray[2];
|
|
471
|
+
const d_x = ray[3], d_y = ray[4], d_z = ray[5];
|
|
472
|
+
|
|
473
|
+
// Body → heightmap-local footprint coordinates. The basis is orthonormal,
|
|
474
|
+
// so the direction stays unit and `t` is the same distance in both frames;
|
|
475
|
+
// the height component is never needed, the triangles carry it.
|
|
476
|
+
const o_u = b[0] * o_x + b[1] * o_y + b[2] * o_z;
|
|
477
|
+
const o_v = b[3] * o_x + b[4] * o_y + b[5] * o_z;
|
|
478
|
+
const d_u = b[0] * d_x + b[1] * d_y + b[2] * d_z;
|
|
479
|
+
const d_v = b[3] * d_x + b[4] * d_y + b[5] * d_z;
|
|
480
|
+
|
|
481
|
+
const half_u = size_x * 0.5;
|
|
482
|
+
const half_v = size_z * 0.5;
|
|
483
|
+
|
|
484
|
+
const t_max = ray[6];
|
|
485
|
+
|
|
486
|
+
// ── Clip to the footprint slabs → the segment the DDA walks ──────────
|
|
487
|
+
let t_enter = 0;
|
|
488
|
+
let t_exit = t_max;
|
|
489
|
+
|
|
490
|
+
if (d_u > RAY_SLAB_EPSILON || d_u < -RAY_SLAB_EPSILON) {
|
|
491
|
+
const inv = 1 / d_u;
|
|
492
|
+
let t0 = (-half_u - o_u) * inv;
|
|
493
|
+
let t1 = (half_u - o_u) * inv;
|
|
494
|
+
if (t0 > t1) { const s = t0; t0 = t1; t1 = s; }
|
|
495
|
+
if (t0 > t_enter) t_enter = t0;
|
|
496
|
+
if (t1 < t_exit) t_exit = t1;
|
|
497
|
+
} else if (o_u < -half_u || o_u > half_u) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (d_v > RAY_SLAB_EPSILON || d_v < -RAY_SLAB_EPSILON) {
|
|
502
|
+
const inv = 1 / d_v;
|
|
503
|
+
let t0 = (-half_v - o_v) * inv;
|
|
504
|
+
let t1 = (half_v - o_v) * inv;
|
|
505
|
+
if (t0 > t1) { const s = t0; t0 = t1; t1 = s; }
|
|
506
|
+
if (t0 > t_enter) t_enter = t0;
|
|
507
|
+
if (t1 < t_exit) t_exit = t1;
|
|
508
|
+
} else if (o_v < -half_v || o_v > half_v) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (t_exit < t_enter) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ── Amanatides–Woo setup on the sub-cell grid ────────────────────────
|
|
517
|
+
//
|
|
518
|
+
// Grid coordinates run [0, seg_u] × [0, seg_v] across the footprint. A
|
|
519
|
+
// ray with no footprint-plane velocity (cast straight down the
|
|
520
|
+
// orientation axis) keeps both rates at 0, both t_next at Infinity, and
|
|
521
|
+
// the loop tests its single cell and stops.
|
|
522
|
+
const per_cell_u = seg_u / size_x;
|
|
523
|
+
const per_cell_v = seg_v / size_z;
|
|
524
|
+
|
|
525
|
+
const rate_u = d_u * per_cell_u;
|
|
526
|
+
const rate_v = d_v * per_cell_v;
|
|
527
|
+
|
|
528
|
+
const g_u = (o_u + d_u * t_enter + half_u) * per_cell_u;
|
|
529
|
+
const g_v = (o_v + d_v * t_enter + half_v) * per_cell_v;
|
|
530
|
+
|
|
531
|
+
// Clamped because the slab clip lands the entry point ON the boundary,
|
|
532
|
+
// where rounding can put it a ULP outside the grid.
|
|
533
|
+
let i = Math.floor(g_u);
|
|
534
|
+
let j = Math.floor(g_v);
|
|
535
|
+
|
|
536
|
+
if (i < 0) i = 0; else if (i > seg_u - 1) i = seg_u - 1;
|
|
537
|
+
if (j < 0) j = 0; else if (j > seg_v - 1) j = seg_v - 1;
|
|
538
|
+
|
|
539
|
+
let step_i = 0, t_delta_u = Infinity, t_next_u = Infinity;
|
|
540
|
+
if (rate_u > 0) {
|
|
541
|
+
step_i = 1;
|
|
542
|
+
t_delta_u = 1 / rate_u;
|
|
543
|
+
t_next_u = t_enter + (i + 1 - g_u) * t_delta_u;
|
|
544
|
+
} else if (rate_u < 0) {
|
|
545
|
+
step_i = -1;
|
|
546
|
+
t_delta_u = -1 / rate_u;
|
|
547
|
+
t_next_u = t_enter + (i - g_u) / rate_u;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
let step_j = 0, t_delta_v = Infinity, t_next_v = Infinity;
|
|
551
|
+
if (rate_v > 0) {
|
|
552
|
+
step_j = 1;
|
|
553
|
+
t_delta_v = 1 / rate_v;
|
|
554
|
+
t_next_v = t_enter + (j + 1 - g_v) * t_delta_v;
|
|
555
|
+
} else if (rate_v < 0) {
|
|
556
|
+
step_j = -1;
|
|
557
|
+
t_delta_v = -1 / rate_v;
|
|
558
|
+
t_next_v = t_enter + (j - g_v) / rate_v;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const inv_seg_u = 1 / seg_u;
|
|
562
|
+
const inv_seg_v = 1 / seg_v;
|
|
563
|
+
|
|
564
|
+
const ux = b[0], uy = b[1], uz = b[2];
|
|
565
|
+
const vx = b[3], vy = b[4], vz = b[5];
|
|
566
|
+
const nx = b[6], ny = b[7], nz = b[8];
|
|
567
|
+
|
|
568
|
+
// Every iteration but the last ends by crossing one grid line, and a
|
|
569
|
+
// straight line crosses fewer than seg_u + seg_v of them (a corner costs
|
|
570
|
+
// two iterations to move diagonally — one per axis — but no extra lines).
|
|
571
|
+
// The walk normally ends on t_exit; this is the bound that holds when a
|
|
572
|
+
// near-denormal rate makes t_exit unreachable.
|
|
573
|
+
const max_steps = seg_u + seg_v + 2;
|
|
574
|
+
|
|
575
|
+
for (let step = 0; step < max_steps; step++) {
|
|
576
|
+
// ── The sub-cell's two triangles, built exactly as the enumerator
|
|
577
|
+
// builds them: corners (A,B,C,D) = (i,j), (i+1,j), (i+1,j+1),
|
|
578
|
+
// (i,j+1), split (A,D,B) and (D,C,B).
|
|
579
|
+
const u01_lo = i * inv_seg_u;
|
|
580
|
+
const u01_hi = (i + 1) * inv_seg_u;
|
|
581
|
+
const v01_lo = j * inv_seg_v;
|
|
582
|
+
const v01_hi = (j + 1) * inv_seg_v;
|
|
583
|
+
|
|
584
|
+
const u_lo = -half_u + size_x * u01_lo;
|
|
585
|
+
const u_hi = -half_u + size_x * u01_hi;
|
|
586
|
+
const v_lo = -half_v + size_z * v01_lo;
|
|
587
|
+
const v_hi = -half_v + size_z * v01_hi;
|
|
588
|
+
|
|
589
|
+
const hA = sampler.sampleChannelCatmullRomUV(u01_lo, v01_lo, 0);
|
|
590
|
+
const hB = sampler.sampleChannelCatmullRomUV(u01_hi, v01_lo, 0);
|
|
591
|
+
const hC = sampler.sampleChannelCatmullRomUV(u01_hi, v01_hi, 0);
|
|
592
|
+
const hD = sampler.sampleChannelCatmullRomUV(u01_lo, v01_hi, 0);
|
|
593
|
+
|
|
594
|
+
const Ax = ux * u_lo + vx * v_lo + nx * hA;
|
|
595
|
+
const Ay = uy * u_lo + vy * v_lo + ny * hA;
|
|
596
|
+
const Az = uz * u_lo + vz * v_lo + nz * hA;
|
|
597
|
+
|
|
598
|
+
const Bx = ux * u_hi + vx * v_lo + nx * hB;
|
|
599
|
+
const By = uy * u_hi + vy * v_lo + ny * hB;
|
|
600
|
+
const Bz = uz * u_hi + vz * v_lo + nz * hB;
|
|
601
|
+
|
|
602
|
+
const Cx = ux * u_hi + vx * v_hi + nx * hC;
|
|
603
|
+
const Cy = uy * u_hi + vy * v_hi + ny * hC;
|
|
604
|
+
const Cz = uz * u_hi + vz * v_hi + nz * hC;
|
|
605
|
+
|
|
606
|
+
const Dx = ux * u_lo + vx * v_hi + nx * hD;
|
|
607
|
+
const Dy = uy * u_lo + vy * v_hi + ny * hD;
|
|
608
|
+
const Dz = uz * u_lo + vz * v_hi + nz * hD;
|
|
609
|
+
|
|
610
|
+
// tri3_raycast writes hit[3..5] only for a hit inside the budget it
|
|
611
|
+
// was given, so handing the second triangle the first's distance
|
|
612
|
+
// leaves the nearer one's normal standing. Both can claim a point on
|
|
613
|
+
// the shared D–B diagonal, and this keeps the second where the
|
|
614
|
+
// narrowphase's copy of the same test keeps the first — the seam case
|
|
615
|
+
// the docstring calls out.
|
|
616
|
+
let best = tri3_raycast(
|
|
617
|
+
hit, 3, o_x, o_y, o_z, d_x, d_y, d_z, t_max,
|
|
618
|
+
Ax, Ay, Az, Dx, Dy, Dz, Bx, By, Bz
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
const t_second = tri3_raycast(
|
|
622
|
+
hit, 3, o_x, o_y, o_z, d_x, d_y, d_z, best < t_max ? best : t_max,
|
|
623
|
+
Dx, Dy, Dz, Cx, Cy, Cz, Bx, By, Bz
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
if (t_second < best) {
|
|
627
|
+
best = t_second;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (best !== Infinity) {
|
|
631
|
+
hit[0] = o_x + d_x * best;
|
|
632
|
+
hit[1] = o_y + d_y * best;
|
|
633
|
+
hit[2] = o_z + d_z * best;
|
|
634
|
+
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ── Advance to the next cell along the ray ───────────────────────
|
|
639
|
+
if (t_next_u < t_next_v) {
|
|
640
|
+
if (t_next_u > t_exit) break;
|
|
641
|
+
i += step_i;
|
|
642
|
+
if (i < 0 || i >= seg_u) break;
|
|
643
|
+
t_next_u += t_delta_u;
|
|
644
|
+
} else if (t_next_v !== Infinity) {
|
|
645
|
+
if (t_next_v > t_exit) break;
|
|
646
|
+
j += step_j;
|
|
647
|
+
if (j < 0 || j >= seg_v) break;
|
|
648
|
+
t_next_v += t_delta_v;
|
|
649
|
+
} else {
|
|
650
|
+
// Both rates are zero: the ray runs along the orientation axis and
|
|
651
|
+
// never leaves the cell it started in.
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
|
|
374
659
|
sample_random_point_in_volume(result, result_offset, random) {
|
|
375
660
|
const u01 = random();
|
|
376
661
|
const v01 = random();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PosedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/PosedShape3D.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PosedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/PosedShape3D.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAEI,mCAAmC;IACnC,OADW,eAAe,GAAC,IAAI,CAClB;IAEb,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IAEP;;;;;;;OAOG;IACH,aAJW,eAAe,YACf,UAAQ;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YACpC,aAAW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAe1D;IAED;;;;;;;;;;;;;;;OAeG;IACH,wBATW,eAAe,MACf,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAMhB;IA+HD,wCASC;IA4BD;;;;;;;OAOG;IACH,gCAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,MAAM,CAKlB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,OAAO,CAKnB;IAGL;;;;OAIG;IACH,yBAFU,OAAO,CAEoB;;CAPpC;gCA5Q+B,sBAAsB"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { assert } from "../../../assert.js";
|
|
2
|
+
import { v3_quaternion_apply } from "../../vec3/v3_quaternion_apply.js";
|
|
3
|
+
import { v3_quaternion_apply_inverse } from "../../vec3/v3_quaternion_apply_inverse.js";
|
|
2
4
|
import { aabb3_transform_oriented } from "../aabb/aabb3_transform_oriented.js";
|
|
5
|
+
import { Ray3 } from "../ray/Ray3.js";
|
|
3
6
|
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
4
7
|
|
|
5
8
|
const scratch_local = new Float64Array(3);
|
|
@@ -154,6 +157,60 @@ export class PosedShape3D extends AbstractShape3D {
|
|
|
154
157
|
result[result_offset + 2] = tsz * qw - tsw * qz - tsx * qy + tsy * qx + this.pz;
|
|
155
158
|
}
|
|
156
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Ray ↔ posed shape, with the ray given in the WRAPPER's frame. The inverse pose carries it into
|
|
162
|
+
* the subject's frame (translate by −position, rotate by the conjugate quaternion), the subject
|
|
163
|
+
* answers there, and the answer is rotated back — the point re-translated, the normal not.
|
|
164
|
+
*
|
|
165
|
+
* A rigid pose is an isometry, so the crossing changes nothing about the ray's parametrisation:
|
|
166
|
+
* the rotated direction is still unit, `tMax` goes through untouched, and the distance the subject
|
|
167
|
+
* measures IS the distance here. The normal's ORIENTATION survives for the same reason — a rotation
|
|
168
|
+
* preserves the angle between normal and direction, so a subject that turned its normal to meet the
|
|
169
|
+
* ray has turned this one to meet it too, and there is nothing to re-check.
|
|
170
|
+
*
|
|
171
|
+
* `hit` is handed straight to the subject rather than buffered and copied back: on a hit its
|
|
172
|
+
* contents are mapped in place, and on a miss the subject leaves it untouched — which is the
|
|
173
|
+
* convention this class owes its own caller, so it is passed on intact.
|
|
174
|
+
*
|
|
175
|
+
* The physics ray query does not come through here; it uses `refine_ray_posed`, which performs the
|
|
176
|
+
* same mapping on loose scalars. That path reports a distance rather than a point, so it never
|
|
177
|
+
* rotates anything but the normal.
|
|
178
|
+
*
|
|
179
|
+
* @param {Float32Array|number[]} hit out: [px, py, pz, nx, ny, nz]
|
|
180
|
+
* @param {Ray3} ray wrapper-frame ray (origin in [0..2], unit direction in [3..5], tMax in [6])
|
|
181
|
+
* @returns {boolean} whether anything was hit
|
|
182
|
+
*/
|
|
183
|
+
raycast(hit, ray) {
|
|
184
|
+
const shape = this.shape;
|
|
185
|
+
|
|
186
|
+
if (shape === null) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const qx = this.qx, qy = this.qy, qz = this.qz, qw = this.qw;
|
|
191
|
+
|
|
192
|
+
// Allocated per call rather than kept as a module scratch: a posed shape may wrap another
|
|
193
|
+
// wrapper, and every level of that recursion needs its own sub-ray alive across the call below.
|
|
194
|
+
const local_ray = new Ray3();
|
|
195
|
+
|
|
196
|
+
v3_quaternion_apply_inverse(local_ray, 0, ray[0] - this.px, ray[1] - this.py, ray[2] - this.pz, qx, qy, qz, qw);
|
|
197
|
+
v3_quaternion_apply_inverse(local_ray, 3, ray[3], ray[4], ray[5], qx, qy, qz, qw);
|
|
198
|
+
local_ray[6] = ray[6];
|
|
199
|
+
|
|
200
|
+
if (!shape.raycast(hit, local_ray)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
v3_quaternion_apply(hit, 0, hit[0], hit[1], hit[2], qx, qy, qz, qw);
|
|
205
|
+
v3_quaternion_apply(hit, 3, hit[3], hit[4], hit[5], qx, qy, qz, qw);
|
|
206
|
+
|
|
207
|
+
hit[0] += this.px;
|
|
208
|
+
hit[1] += this.py;
|
|
209
|
+
hit[2] += this.pz;
|
|
210
|
+
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
157
214
|
compute_bounding_box(result) {
|
|
158
215
|
this.shape.compute_bounding_box(scratch_local_aabb);
|
|
159
216
|
aabb3_transform_oriented(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransformedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/TransformedShape3D.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TransformedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/TransformedShape3D.js"],"names":[],"mappings":"AAyBA;IAwCI;;;;;OAKG;IACH,wBAHW,eAAe,MADf,MAAM,EAAE,GAAC,YAAY,OAAK,GAExB,kBAAkB,CAS9B;IAED;;;;;OAKG;IACH,uCAJW,eAAe,eACf,MAAM,EAAE,SACR,MAAM,EAAE,sBAiBlB;IAED;;;;OAIG;IACH,iCAHW,eAAe,eACf,MAAM,EAAE,sBAIlB;IAED;;;;;OAKG;IACH,2BAHW,eAAe,SACf,MAAM,EAAE,GAAC,YAAY,OAAK,GAFxB,kBAAkB,CAM9B;IA9FD;;;;OAIG;IACH,iBAAgC;IAEhC;;;;OAIG;IACH,yBAAwC;IAExC;;;;OAIG;IACH,kBAAiB;IAEjB;;;OAGG;IACH,+BAEC;IAqED;;;OAGG;IACH,0CAIC;IAED,yCAEC;IAED;;;;;;;OAOG;IACH,6BAEC;IAUD,wCAKC;IAED,mEAyDC;IAED,6CAEC;IAED,oCAKC;IAED,kFAIC;IAED,qGAwCC;IAuGD;;;;OAIG;IACH,cAHW,kBAAkB,GAChB,OAAO,CAOnB;IAQL;;;;;OAKG;IACH,+BAFU,OAAO,CAEgC;CARhD;gCAnY+B,sBAAsB"}
|
|
@@ -8,6 +8,7 @@ import { aabb3_matrix4_project } from "../aabb/aabb3_matrix4_project.js";
|
|
|
8
8
|
import { m4_extract_scale } from "../mat4/m4_extract_scale.js";
|
|
9
9
|
import { m4_invert } from "../mat4/m4_invert.js";
|
|
10
10
|
import { m4_make_scale } from "../mat4/m4_make_scale.js";
|
|
11
|
+
import { Ray3 } from "../ray/Ray3.js";
|
|
11
12
|
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -278,6 +279,107 @@ export class TransformedShape3D extends AbstractShape3D {
|
|
|
278
279
|
v3_matrix4_multiply(result, result_offset, scratch_v3_0, 0, this.__matrix);
|
|
279
280
|
}
|
|
280
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Ray ↔ transformed shape, with the ray given in the WRAPPER's frame. The cached inverse matrix
|
|
284
|
+
* carries the ray into the subject's frame, the subject answers there, and the answer comes back
|
|
285
|
+
* by two different maps: the point through the forward matrix, the normal through the inverse
|
|
286
|
+
* TRANSPOSE.
|
|
287
|
+
*
|
|
288
|
+
* Three things an affine transform does to a ray that a rigid pose ({@link PosedShape3D#raycast})
|
|
289
|
+
* does not:
|
|
290
|
+
*
|
|
291
|
+
* - DISTANCE. The mapped direction is no longer unit — a scaled shape stretches travel along the
|
|
292
|
+
* ray — so it is renormalised and the stretch factor is carried on `tMax` INTO the subject.
|
|
293
|
+
* Nothing carries it back out, because the hit point is mapped by the matrix rather than
|
|
294
|
+
* rebuilt from a distance, and an affine map takes the subject's point on its ray to this
|
|
295
|
+
* ray's point at the matching parameter. So a caller recovering `t` as `(p − origin)·direction`
|
|
296
|
+
* still gets the distance in THIS frame, which is what the family contract promises.
|
|
297
|
+
* - NORMAL. It maps by (M⁻¹)ᵀ rather than by M — that is what keeps a normal perpendicular to
|
|
298
|
+
* its surface under non-uniform scale — and is renormalised afterwards, because that map does
|
|
299
|
+
* not preserve length. Done inline against the cached {@link inverse_transform}, not through
|
|
300
|
+
* `v3_matrix4_rotate_normal`, which would invert the matrix again on every ray.
|
|
301
|
+
* - ORIENTATION. Nothing to fix, as it happens: `n_here · d_here = scale · (n_subject ·
|
|
302
|
+
* d_subject)` with `scale > 0`, so a normal the subject turned to meet its ray meets this one
|
|
303
|
+
* too. True even of a mirroring matrix — its determinant is negative, but that scale, a vector
|
|
304
|
+
* length, is not.
|
|
305
|
+
*
|
|
306
|
+
* A transform that was never successfully inverted — a singular matrix, or a shape whose
|
|
307
|
+
* `transform` was never set — leaves the cached inverse zeroed, and the mapped direction with it;
|
|
308
|
+
* that is reported as a miss rather than propagated onward as NaN. Note the narrower case this
|
|
309
|
+
* does NOT rescue: {@link m4_invert} leaves a PREVIOUS inverse in place when it refuses, so a
|
|
310
|
+
* shape handed a singular transform after a good one goes on casting against the older one. That
|
|
311
|
+
* hazard belongs to every method here that reads the cached inverse, not to this one.
|
|
312
|
+
*
|
|
313
|
+
* The physics ray query does not come through here; it uses `refine_ray_transformed`, which does
|
|
314
|
+
* the same mapping on loose scalars and, reporting a distance rather than a point, divides that
|
|
315
|
+
* distance by the stretch factor instead of pushing a point through the matrix.
|
|
316
|
+
*
|
|
317
|
+
* @param {Float32Array|number[]} hit out: [px, py, pz, nx, ny, nz]
|
|
318
|
+
* @param {Ray3} ray wrapper-frame ray (origin in [0..2], unit direction in [3..5], tMax in [6])
|
|
319
|
+
* @returns {boolean} whether anything was hit
|
|
320
|
+
*/
|
|
321
|
+
raycast(hit, ray) {
|
|
322
|
+
const subject = this.__subject;
|
|
323
|
+
|
|
324
|
+
if (subject === null) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const inverse = this.__inverse_matrix;
|
|
329
|
+
|
|
330
|
+
// Direction takes the linear part only, so it is written out rather than run through
|
|
331
|
+
// v3_matrix4_multiply — and its length is needed anyway.
|
|
332
|
+
const dx = ray[3], dy = ray[4], dz = ray[5];
|
|
333
|
+
|
|
334
|
+
const sdx = inverse[0] * dx + inverse[4] * dy + inverse[8] * dz;
|
|
335
|
+
const sdy = inverse[1] * dx + inverse[5] * dy + inverse[9] * dz;
|
|
336
|
+
const sdz = inverse[2] * dx + inverse[6] * dy + inverse[10] * dz;
|
|
337
|
+
|
|
338
|
+
// How much longer one unit of travel here is in the subject's frame.
|
|
339
|
+
const scale = Math.sqrt(sdx * sdx + sdy * sdy + sdz * sdz);
|
|
340
|
+
|
|
341
|
+
if (!(scale > 0)) {
|
|
342
|
+
// Singular (or never-inverted) transform: the direction has no image to cast along.
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const inv_scale = 1 / scale;
|
|
347
|
+
|
|
348
|
+
// Allocated per call rather than kept as a module scratch: a transformed shape may wrap another
|
|
349
|
+
// wrapper, and every level of that recursion needs its own sub-ray alive across the call below.
|
|
350
|
+
const subject_ray = new Ray3();
|
|
351
|
+
|
|
352
|
+
v3_matrix4_multiply(subject_ray, 0, ray, 0, inverse);
|
|
353
|
+
|
|
354
|
+
subject_ray[3] = sdx * inv_scale;
|
|
355
|
+
subject_ray[4] = sdy * inv_scale;
|
|
356
|
+
subject_ray[5] = sdz * inv_scale;
|
|
357
|
+
subject_ray[6] = ray[6] * scale;
|
|
358
|
+
|
|
359
|
+
if (!subject.raycast(hit, subject_ray)) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Point: the full forward transform, in place. v3_matrix4_multiply reads all three components
|
|
364
|
+
// before writing any, so `hit` may be both its source and its destination.
|
|
365
|
+
v3_matrix4_multiply(hit, 0, hit, 0, this.__matrix);
|
|
366
|
+
|
|
367
|
+
// Normal: (M⁻¹)ᵀ · n, then renormalised.
|
|
368
|
+
const nx = hit[3], ny = hit[4], nz = hit[5];
|
|
369
|
+
|
|
370
|
+
const wx = inverse[0] * nx + inverse[1] * ny + inverse[2] * nz;
|
|
371
|
+
const wy = inverse[4] * nx + inverse[5] * ny + inverse[6] * nz;
|
|
372
|
+
const wz = inverse[8] * nx + inverse[9] * ny + inverse[10] * nz;
|
|
373
|
+
|
|
374
|
+
const inv_length = 1 / Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
375
|
+
|
|
376
|
+
hit[3] = wx * inv_length;
|
|
377
|
+
hit[4] = wy * inv_length;
|
|
378
|
+
hit[5] = wz * inv_length;
|
|
379
|
+
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
|
|
281
383
|
/**
|
|
282
384
|
*
|
|
283
385
|
* @param {TransformedShape3D} other
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnionShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/UnionShape3D.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UnionShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/UnionShape3D.js"],"names":[],"mappings":"AA4BA;IAkII;;;;OAIG;IACH,sBAHW,eAAe,EAAE,GACf,YAAY,CAYxB;IAhJD;;;OAGG;IACH,UAFU,eAAe,EAAE,CAEb;IAEd;;;;;OAKG;IACH,kCAA+B;IAE/B,qBAAkB;IAClB,qBAAmB;IACnB,uBAAqB;IAErB,wCAgCC;IAED;;;OAGG;IACH,mCAkBC;IAED;;;OAGG;IACH,2CA+CC;IA2DD,mEAEC;IAED,6CAaC;IAED,oCAYC;IAED,kFAoDC;IAED,qGAyBC;IAiED;;;;OAIG;IACH,cAHW,YAAY,GACV,OAAO,CAKnB;IA6BL;;;;OAIG;IACH,yBAFU,OAAO,CAEoB;CApBpC;gCApZ+B,sBAAsB"}
|