From 0c4be9f08c1bd9d76858c4094bcb32100cc1098b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Fri, 11 Jul 2025 21:02:05 +0200 Subject: [PATCH 1/5] feat: un-deprecate collider properties accessors --- src.ts/geometry/collider.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src.ts/geometry/collider.ts b/src.ts/geometry/collider.ts index 5704633b..974a4573 100644 --- a/src.ts/geometry/collider.ts +++ b/src.ts/geometry/collider.ts @@ -110,7 +110,7 @@ export type ColliderHandle = number; export class Collider { private colliderSet: ColliderSet; // The Collider won't need to free this. readonly handle: ColliderHandle; - private _shape: Shape; + private _shape: Shape; // TODO: deprecate/remove this since it isn’t a reliable way of getting the latest shape properties. private _parent: RigidBody | null; constructor( @@ -591,7 +591,6 @@ export class Collider { /** * The type of the shape of this collider. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public shapeType(): ShapeType { return this.colliderSet.raw.coShapeType( @@ -601,7 +600,6 @@ export class Collider { /** * The half-extents of this collider if it is a cuboid shape. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public halfExtents(): Vector { return VectorOps.fromRaw( @@ -621,7 +619,6 @@ export class Collider { /** * The radius of this collider if it is a ball, cylinder, capsule, or cone shape. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public radius(): number { return this.colliderSet.raw.coRadius(this.handle); @@ -638,7 +635,6 @@ export class Collider { /** * The radius of the round edges of this collider if it is a round cylinder. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public roundRadius(): number { return this.colliderSet.raw.coRoundRadius(this.handle); @@ -655,7 +651,6 @@ export class Collider { /** * The half height of this collider if it is a cylinder, capsule, or cone shape. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public halfHeight(): number { return this.colliderSet.raw.coHalfHeight(this.handle); @@ -800,7 +795,6 @@ export class Collider { /** * If this collider has a triangle mesh, polyline, convex polygon, or convex polyhedron shape, * this returns the vertex buffer of said shape. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public vertices(): Float32Array { return this.colliderSet.raw.coVertices(this.handle); @@ -809,7 +803,6 @@ export class Collider { /** * If this collider has a triangle mesh, polyline, or convex polyhedron shape, * this returns the index buffer of said shape. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public indices(): Uint32Array | undefined { return this.colliderSet.raw.coIndices(this.handle); @@ -819,7 +812,6 @@ export class Collider { * If this collider has a heightfield shape, this returns the heights buffer of * the heightfield. * In 3D, the returned height matrix is provided in column-major order. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public heightfieldHeights(): Float32Array { return this.colliderSet.raw.coHeightfieldHeights(this.handle); @@ -828,7 +820,6 @@ export class Collider { /** * If this collider has a heightfield shape, this returns the scale * applied to it. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public heightfieldScale(): Vector { let scale = this.colliderSet.raw.coHeightfieldScale(this.handle); @@ -839,7 +830,6 @@ export class Collider { /** * If this collider has a heightfield shape, this returns the number of * rows of its height matrix. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public heightfieldNRows(): number { return this.colliderSet.raw.coHeightfieldNRows(this.handle); @@ -848,7 +838,6 @@ export class Collider { /** * If this collider has a heightfield shape, this returns the number of * columns of its height matrix. - * @deprecated this field will be removed in the future, please access this field on `shape` member instead. */ public heightfieldNCols(): number { return this.colliderSet.raw.coHeightfieldNCols(this.handle); From 0bd794c8ece97ba4979fd98bd34f4c767eb4e2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Fri, 11 Jul 2025 21:02:38 +0200 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20add=20functions=20for=20getting=20a?= =?UTF-8?q?=20collider=E2=80=99s=20translation/rotation=20wrt.=20its=20par?= =?UTF-8?q?ent=20rigid-body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src.ts/geometry/collider.ts | 26 ++++++++++++++++++++++++-- src/geometry/collider.rs | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src.ts/geometry/collider.ts b/src.ts/geometry/collider.ts index 974a4573..6f2dd272 100644 --- a/src.ts/geometry/collider.ts +++ b/src.ts/geometry/collider.ts @@ -168,7 +168,7 @@ export class Collider { } /** - * The world-space translation of this rigid-body. + * The world-space translation of this collider. */ public translation(): Vector { return VectorOps.fromRaw( @@ -177,7 +177,18 @@ export class Collider { } /** - * The world-space orientation of this rigid-body. + * The translation of this collider relative to its parent rigid-body. + * + * Returns `null` if the collider doesn’t have a parent rigid-body. + */ + public translationWrtParent(): Vector | null { + return VectorOps.fromRaw( + this.colliderSet.raw.coTranslation(this.handle), + ); + } + + /** + * The world-space orientation of this collider. */ public rotation(): Rotation { return RotationOps.fromRaw( @@ -185,6 +196,17 @@ export class Collider { ); } + /** + * The orientation of this collider relative to its parent rigid-body. + * + * Returns `null` if the collider doesn’t have a parent rigid-body. + */ + public rotationWrtParent(): Rotation | null { + return RotationOps.fromRaw( + this.colliderSet.raw.coRotation(this.handle), + ); + } + /** * Is this collider a sensor? */ diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 3f3813e7..5f5d3490 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -25,6 +25,26 @@ impl RawColliderSet { self.map(handle, |co| co.position().rotation.into()) } + /// The translation of this collider relative to its parent rigid-body. + /// + /// Returns the `None` if it doesn’t have a parent. + pub fn coTranslationWrtParent(&self, handle: FlatHandle) -> Option { + self.map(handle, |co| { + co.position_wrt_parent() + .map(|pose| pose.translation.vector.into()) + }) + } + + /// The orientation of this collider relative to its parent rigid-body. + /// + /// Returns the `None` if it doesn’t have a parent. + pub fn coRotationWrtParent(&self, handle: FlatHandle) -> Option { + self.map(handle, |co| { + co.position_wrt_parent() + .map(|pose| pose.rotation.into()) + }) + } + /// Sets the translation of this collider. /// /// # Parameters From 45892bbea20e8b22cd98e4a3449ee1bfb6fde174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 12 Jul 2025 00:11:43 +0200 Subject: [PATCH 3/5] Update to rapier 0.27.0-beta.0 --- CHANGELOG.md | 13 +- Cargo.lock | 65 ++- .../prepare_builds/templates/Cargo.toml.tera | 2 +- src.ts/control/character_controller.ts | 16 +- src.ts/control/pid_controller.ts | 2 +- src.ts/control/ray_cast_vehicle_controller.ts | 16 +- src.ts/geometry/broad_phase.ts | 498 +++++++++++++++++- src.ts/geometry/collider.ts | 4 +- src.ts/pipeline/query_pipeline.ts | 494 +---------------- src.ts/pipeline/serialization_pipeline.ts | 2 +- src.ts/pipeline/world.ts | 66 ++- src/control/character_controller.rs | 53 +- src/control/ray_cast_vehicle_controller.rs | 17 +- src/geometry/broad_phase.rs | 449 ++++++++++++++++ src/geometry/collider.rs | 3 +- src/geometry/narrow_phase.rs | 38 +- src/pipeline/mod.rs | 2 - src/pipeline/physics_pipeline.rs | 2 - src/pipeline/query_pipeline.rs | 405 -------------- 19 files changed, 1127 insertions(+), 1020 deletions(-) delete mode 100644 src/pipeline/query_pipeline.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c14b7a..580013bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,19 @@ ### Unreleased +#### Modified + +- Update to Rapier 0.22.0-beta.1 which includes a fully reworked narrow-phase tha supports scene queries. + This implies a performance gain on large scenes by avoiding the need to re-build the underlying acceleration + structure at each frame. +- Un-deprecate methods for reading shape properties (for example `collider.radius()`). It turned out that these + methods are more convenient as they are guaranteed to always be in sync with rapier’s state on wasm. +- Add `collider.translationWrtParent()` and `collider.rotationWrtParent()` to get the collider’s transaltion/rotation + relative to its parent rigid-body. + #### Fix -- rapier-compat top level javascript files extensions have been changed from `.cjs.js` and `.es.js` to `.cjs` and `mjs` respectively. This results in better compatibility with NPM. +- rapier-compat top level javascript files extensions have been changed from `.cjs.js` and `.es.js` to `.cjs` and `mjs` + respectively. This results in better compatibility with NPM. ### 0.17.3 (30 May 2025) diff --git a/Cargo.lock b/Cargo.lock index 72692f57..ff375001 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,7 +369,7 @@ dependencies = [ [[package]] name = "dimforge_rapier2d" -version = "0.17.0" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-deterministic" -version = "0.17.0" +version = "0.17.3" dependencies = [ "bincode", "js-sys", @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-simd" -version = "0.17.0" +version = "0.17.3" dependencies = [ "bincode", "js-sys", @@ -411,7 +411,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d" -version = "0.17.0" +version = "0.17.3" dependencies = [ "bincode", "js-sys", @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-deterministic" -version = "0.17.0" +version = "0.17.3" dependencies = [ "bincode", "js-sys", @@ -439,7 +439,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-simd" -version = "0.17.0" +version = "0.17.3" dependencies = [ "bincode", "js-sys", @@ -484,6 +484,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "foldhash" version = "0.1.5" @@ -511,6 +517,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glam" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50a99dbe56b72736564cfa4b85bf9a33079f16ae8b74983ab06af3b1a3696b11" + [[package]] name = "globset" version = "0.4.16" @@ -824,9 +836,9 @@ dependencies = [ [[package]] name = "parry2d" -version = "0.21.1" +version = "0.22.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc75102c1fac2294401262c78e5a42abe7168244c9436df2fec8fe0c27395c7" +checksum = "6826172689f7365694e8c3123b57065709869c13f1b686e8294a2a0f9a8e4bec" dependencies = [ "approx", "arrayvec", @@ -844,15 +856,16 @@ dependencies = [ "serde", "simba", "slab", + "smallvec", "spade", "thiserror", ] [[package]] name = "parry3d" -version = "0.21.1" +version = "0.22.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f8d0a3b2f4c0e250d4599b69e490535521c3497e2b88b0b5d2ada251bc83a8" +checksum = "f1d866b7e9e63a05780111b13f275e1a398a60f34b6749392b53369fe4eb5520" dependencies = [ "approx", "arrayvec", @@ -860,6 +873,7 @@ dependencies = [ "downcast-rs", "either", "ena", + "glam", "hashbrown", "indexmap", "log", @@ -871,7 +885,9 @@ dependencies = [ "serde", "simba", "slab", + "smallvec", "spade", + "static_assertions", "thiserror", ] @@ -941,6 +957,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" +dependencies = [ + "fixedbitset", + "hashbrown", + "indexmap", + "serde", +] + [[package]] name = "phf" version = "0.11.3" @@ -1080,9 +1108,9 @@ dependencies = [ [[package]] name = "rapier2d" -version = "0.26.1" +version = "0.27.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b70793351467e663d06f52ffdf6138a1d7920b305ee4b7751e1adb90d56a797f" +checksum = "e89e01890c583fe4258cd12f03664ad5dbc0cf4cda37aac790681e2b4f627778" dependencies = [ "approx", "arrayvec", @@ -1106,9 +1134,9 @@ dependencies = [ [[package]] name = "rapier3d" -version = "0.26.1" +version = "0.27.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1015500058823ba9c479c908d7069adcf3f0f51a3e49dca7efc5575df7e574" +checksum = "1ab3ba62d1788a880b297e5c56612c68cfa18db3ad0413e80704729820378577" dependencies = [ "approx", "arrayvec", @@ -1122,12 +1150,15 @@ dependencies = [ "num-traits", "ordered-float", "parry3d", + "petgraph", "profiling", "rustc-hash", "serde", "simba", + "smallvec", "thiserror", "vec_map", + "wide", ] [[package]] @@ -1351,6 +1382,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" diff --git a/builds/prepare_builds/templates/Cargo.toml.tera b/builds/prepare_builds/templates/Cargo.toml.tera index 208addd5..6147ccb6 100644 --- a/builds/prepare_builds/templates/Cargo.toml.tera +++ b/builds/prepare_builds/templates/Cargo.toml.tera @@ -27,7 +27,7 @@ rust.unexpected_cfgs = { level = "warn", check-cfg = [ ] } [dependencies] -rapier{{ dimension }}d = { version = "0.26.1", features = [ +rapier{{ dimension }}d = { version = "0.27.0-beta.0", features = [ "serde-serialize", "debug-render", {%- for feature in additional_features %} diff --git a/src.ts/control/character_controller.ts b/src.ts/control/character_controller.ts index 4fa7ac29..e2392ea2 100644 --- a/src.ts/control/character_controller.ts +++ b/src.ts/control/character_controller.ts @@ -1,7 +1,7 @@ import {RawKinematicCharacterController, RawCharacterCollision} from "../raw"; import {Rotation, Vector, VectorOps} from "../math"; -import {Collider, ColliderSet, InteractionGroups, Shape} from "../geometry"; -import {QueryFilterFlags, QueryPipeline, World} from "../pipeline"; +import {BroadPhase, Collider, ColliderSet, InteractionGroups, NarrowPhase, Shape} from "../geometry"; +import {QueryFilterFlags, World} from "../pipeline"; import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics"; /** @@ -35,23 +35,26 @@ export class KinematicCharacterController { private rawCharacterCollision: RawCharacterCollision; private params: IntegrationParameters; + private broadPhase: BroadPhase; + private narrowPhase: NarrowPhase; private bodies: RigidBodySet; private colliders: ColliderSet; - private queries: QueryPipeline; private _applyImpulsesToDynamicBodies: boolean; private _characterMass: number | null; constructor( offset: number, params: IntegrationParameters, + broadPhase: BroadPhase, + narrowPhase: NarrowPhase, bodies: RigidBodySet, colliders: ColliderSet, - queries: QueryPipeline, ) { this.params = params; this.bodies = bodies; this.colliders = colliders; - this.queries = queries; + this.broadPhase = broadPhase; + this.narrowPhase = narrowPhase; this.raw = new RawKinematicCharacterController(offset); this.rawCharacterCollision = new RawCharacterCollision(); this._applyImpulsesToDynamicBodies = false; @@ -305,9 +308,10 @@ export class KinematicCharacterController { let rawTranslationDelta = VectorOps.intoRaw(desiredTranslationDelta); this.raw.computeColliderMovement( this.params.dt, + this.broadPhase.raw, + this.narrowPhase.raw, this.bodies.raw, this.colliders.raw, - this.queries.raw, collider.handle, rawTranslationDelta, this._applyImpulsesToDynamicBodies, diff --git a/src.ts/control/pid_controller.ts b/src.ts/control/pid_controller.ts index 840205d2..52b2f006 100644 --- a/src.ts/control/pid_controller.ts +++ b/src.ts/control/pid_controller.ts @@ -1,7 +1,7 @@ import {RawPidController} from "../raw"; import {Rotation, RotationOps, Vector, VectorOps} from "../math"; import {Collider, ColliderSet, InteractionGroups, Shape} from "../geometry"; -import {QueryFilterFlags, QueryPipeline, World} from "../pipeline"; +import {QueryFilterFlags, World} from "../pipeline"; import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics"; // TODO: unify with the JointAxesMask diff --git a/src.ts/control/ray_cast_vehicle_controller.ts b/src.ts/control/ray_cast_vehicle_controller.ts index 2b88e060..15405523 100644 --- a/src.ts/control/ray_cast_vehicle_controller.ts +++ b/src.ts/control/ray_cast_vehicle_controller.ts @@ -1,7 +1,7 @@ import {RawDynamicRayCastVehicleController} from "../raw"; import {Vector, VectorOps} from "../math"; -import {Collider, ColliderSet, InteractionGroups} from "../geometry"; -import {QueryFilterFlags, QueryPipeline} from "../pipeline"; +import {BroadPhase, Collider, ColliderSet, InteractionGroups, NarrowPhase} from "../geometry"; +import {QueryFilterFlags} from "../pipeline"; import {RigidBody, RigidBodyHandle, RigidBodySet} from "../dynamics"; /** @@ -9,21 +9,24 @@ import {RigidBody, RigidBodyHandle, RigidBodySet} from "../dynamics"; */ export class DynamicRayCastVehicleController { private raw: RawDynamicRayCastVehicleController; + private broadPhase: BroadPhase; + private narrowPhase: NarrowPhase; private bodies: RigidBodySet; private colliders: ColliderSet; - private queries: QueryPipeline; private _chassis: RigidBody; constructor( chassis: RigidBody, + broadPhase: BroadPhase, + narrowPhase: NarrowPhase, bodies: RigidBodySet, colliders: ColliderSet, - queries: QueryPipeline, ) { this.raw = new RawDynamicRayCastVehicleController(chassis.handle); + this.broadPhase = broadPhase; + this.narrowPhase = narrowPhase; this.bodies = bodies; this.colliders = colliders; - this.queries = queries; this._chassis = chassis; } @@ -54,9 +57,10 @@ export class DynamicRayCastVehicleController { ) { this.raw.update_vehicle( dt, + this.broadPhase.raw, + this.narrowPhase.raw, this.bodies.raw, this.colliders.raw, - this.queries.raw, filterFlags, filterGroups, this.colliders.castClosure(filterPredicate), diff --git a/src.ts/geometry/broad_phase.ts b/src.ts/geometry/broad_phase.ts index 4ca603d1..b9aaeec8 100644 --- a/src.ts/geometry/broad_phase.ts +++ b/src.ts/geometry/broad_phase.ts @@ -1,4 +1,15 @@ -import {RawBroadPhase} from "../raw"; +import {RawBroadPhase, RawRayColliderIntersection} from "../raw"; +import {RigidBodyHandle, RigidBodySet} from "../dynamics"; +import {ColliderSet} from "./collider_set"; +import {Ray, RayColliderHit, RayColliderIntersection} from "./ray"; +import {InteractionGroups} from "./interaction_groups"; +import {ColliderHandle} from "./collider"; +import {Rotation, RotationOps, Vector, VectorOps} from "../math"; +import {Shape} from "./shape"; +import {PointColliderProjection} from "./point"; +import {ColliderShapeCastHit} from "./toi"; +import {QueryFilterFlags} from "../pipeline"; +import {NarrowPhase} from "./narrow_phase"; /** * The broad-phase used for coarse collision-detection. @@ -22,4 +33,489 @@ export class BroadPhase { constructor(raw?: RawBroadPhase) { this.raw = raw || new RawBroadPhase(); } + + + /** + * Find the closest intersection between a ray and a set of collider. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param ray - The ray to cast. + * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively + * limits the length of the ray to `ray.dir.norm() * maxToi`. + * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its + * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, + * whereas `false` implies that all shapes are hollow for this ray-cast. + * @param groups - Used to filter the colliders that can or cannot be hit by the ray. + * @param filter - The callback to filter out which collider will be hit. + */ + public castRay( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + ray: Ray, + maxToi: number, + solid: boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): RayColliderHit | null { + let rawOrig = VectorOps.intoRaw(ray.origin); + let rawDir = VectorOps.intoRaw(ray.dir); + let result = RayColliderHit.fromRaw( + colliders, + this.raw.castRay( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawOrig, + rawDir, + maxToi, + solid, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ), + ); + + rawOrig.free(); + rawDir.free(); + + return result; + } + + /** + * Find the closest intersection between a ray and a set of collider. + * + * This also computes the normal at the hit point. + * @param colliders - The set of colliders taking part in this pipeline. + * @param ray - The ray to cast. + * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively + * limits the length of the ray to `ray.dir.norm() * maxToi`. + * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its + * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, + * whereas `false` implies that all shapes are hollow for this ray-cast. + * @param groups - Used to filter the colliders that can or cannot be hit by the ray. + */ + public castRayAndGetNormal( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + ray: Ray, + maxToi: number, + solid: boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): RayColliderIntersection | null { + let rawOrig = VectorOps.intoRaw(ray.origin); + let rawDir = VectorOps.intoRaw(ray.dir); + let result = RayColliderIntersection.fromRaw( + colliders, + this.raw.castRayAndGetNormal( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawOrig, + rawDir, + maxToi, + solid, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ), + ); + + rawOrig.free(); + rawDir.free(); + + return result; + } + + /** + * Cast a ray and collects all the intersections between a ray and the scene. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param ray - The ray to cast. + * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively + * limits the length of the ray to `ray.dir.norm() * maxToi`. + * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its + * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, + * whereas `false` implies that all shapes are hollow for this ray-cast. + * @param groups - Used to filter the colliders that can or cannot be hit by the ray. + * @param callback - The callback called once per hit (in no particular order) between a ray and a collider. + * If this callback returns `false`, then the cast will stop and no further hits will be detected/reported. + */ + public intersectionsWithRay( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + ray: Ray, + maxToi: number, + solid: boolean, + callback: (intersect: RayColliderIntersection) => boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ) { + let rawOrig = VectorOps.intoRaw(ray.origin); + let rawDir = VectorOps.intoRaw(ray.dir); + let rawCallback = (rawInter: RawRayColliderIntersection) => { + return callback( + RayColliderIntersection.fromRaw(colliders, rawInter), + ); + }; + + this.raw.intersectionsWithRay( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawOrig, + rawDir, + maxToi, + solid, + rawCallback, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ); + + rawOrig.free(); + rawDir.free(); + } + + /** + * Gets the handle of up to one collider intersecting the given shape. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param shapePos - The position of the shape used for the intersection test. + * @param shapeRot - The orientation of the shape used for the intersection test. + * @param shape - The shape used for the intersection test. + * @param groups - The bit groups and filter associated to the ray, in order to only + * hit the colliders with collision groups compatible with the ray's group. + */ + public intersectionWithShape( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + shapePos: Vector, + shapeRot: Rotation, + shape: Shape, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): ColliderHandle | null { + let rawPos = VectorOps.intoRaw(shapePos); + let rawRot = RotationOps.intoRaw(shapeRot); + let rawShape = shape.intoRaw(); + let result = this.raw.intersectionWithShape( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPos, + rawRot, + rawShape, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ); + + rawPos.free(); + rawRot.free(); + rawShape.free(); + + return result; + } + + /** + * Find the projection of a point on the closest collider. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param point - The point to project. + * @param solid - If this is set to `true` then the collider shapes are considered to + * be plain (if the point is located inside of a plain shape, its projection is the point + * itself). If it is set to `false` the collider shapes are considered to be hollow + * (if the point is located inside of an hollow shape, it is projected on the shape's + * boundary). + * @param groups - The bit groups and filter associated to the point to project, in order to only + * project on colliders with collision groups compatible with the ray's group. + */ + public projectPoint( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + point: Vector, + solid: boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): PointColliderProjection | null { + let rawPoint = VectorOps.intoRaw(point); + let result = PointColliderProjection.fromRaw( + colliders, + this.raw.projectPoint( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPoint, + solid, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ), + ); + + rawPoint.free(); + + return result; + } + + /** + * Find the projection of a point on the closest collider. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param point - The point to project. + * @param groups - The bit groups and filter associated to the point to project, in order to only + * project on colliders with collision groups compatible with the ray's group. + */ + public projectPointAndGetFeature( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + point: Vector, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): PointColliderProjection | null { + let rawPoint = VectorOps.intoRaw(point); + let result = PointColliderProjection.fromRaw( + colliders, + this.raw.projectPointAndGetFeature( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPoint, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ), + ); + + rawPoint.free(); + + return result; + } + + /** + * Find all the colliders containing the given point. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param point - The point used for the containment test. + * @param groups - The bit groups and filter associated to the point to test, in order to only + * test on colliders with collision groups compatible with the ray's group. + * @param callback - A function called with the handles of each collider with a shape + * containing the `point`. + */ + public intersectionsWithPoint( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + point: Vector, + callback: (handle: ColliderHandle) => boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ) { + let rawPoint = VectorOps.intoRaw(point); + + this.raw.intersectionsWithPoint( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPoint, + callback, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ); + + rawPoint.free(); + } + + /** + * Casts a shape at a constant linear velocity and retrieve the first collider it hits. + * This is similar to ray-casting except that we are casting a whole shape instead of + * just a point (the ray origin). + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param shapePos - The initial position of the shape to cast. + * @param shapeRot - The initial rotation of the shape to cast. + * @param shapeVel - The constant velocity of the shape to cast (i.e. the cast direction). + * @param shape - The shape to cast. + * @param targetDistance − If the shape moves closer to this distance from a collider, a hit + * will be returned. + * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively + * limits the distance traveled by the shape to `shapeVel.norm() * maxToi`. + * @param stopAtPenetration - If set to `false`, the linear shape-cast won’t immediately stop if + * the shape is penetrating another shape at its starting point **and** its trajectory is such + * that it’s on a path to exit that penetration state. + * @param groups - The bit groups and filter associated to the shape to cast, in order to only + * test on colliders with collision groups compatible with this group. + */ + public castShape( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + shapePos: Vector, + shapeRot: Rotation, + shapeVel: Vector, + shape: Shape, + targetDistance: number, + maxToi: number, + stopAtPenetration: boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ): ColliderShapeCastHit | null { + let rawPos = VectorOps.intoRaw(shapePos); + let rawRot = RotationOps.intoRaw(shapeRot); + let rawVel = VectorOps.intoRaw(shapeVel); + let rawShape = shape.intoRaw(); + + let result = ColliderShapeCastHit.fromRaw( + colliders, + this.raw.castShape( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPos, + rawRot, + rawVel, + rawShape, + targetDistance, + maxToi, + stopAtPenetration, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ), + ); + + rawPos.free(); + rawRot.free(); + rawVel.free(); + rawShape.free(); + + return result; + } + + /** + * Retrieve all the colliders intersecting the given shape. + * + * @param colliders - The set of colliders taking part in this pipeline. + * @param shapePos - The position of the shape to test. + * @param shapeRot - The orientation of the shape to test. + * @param shape - The shape to test. + * @param groups - The bit groups and filter associated to the shape to test, in order to only + * test on colliders with collision groups compatible with this group. + * @param callback - A function called with the handles of each collider intersecting the `shape`. + */ + public intersectionsWithShape( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + shapePos: Vector, + shapeRot: Rotation, + shape: Shape, + callback: (handle: ColliderHandle) => boolean, + filterFlags?: QueryFilterFlags, + filterGroups?: InteractionGroups, + filterExcludeCollider?: ColliderHandle, + filterExcludeRigidBody?: RigidBodyHandle, + filterPredicate?: (collider: ColliderHandle) => boolean, + ) { + let rawPos = VectorOps.intoRaw(shapePos); + let rawRot = RotationOps.intoRaw(shapeRot); + let rawShape = shape.intoRaw(); + + this.raw.intersectionsWithShape( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPos, + rawRot, + rawShape, + callback, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ); + + rawPos.free(); + rawRot.free(); + rawShape.free(); + } + + /** + * Finds the handles of all the colliders with an AABB intersecting the given AABB. + * + * @param aabbCenter - The center of the AABB to test. + * @param aabbHalfExtents - The half-extents of the AABB to test. + * @param callback - The callback that will be called with the handles of all the colliders + * currently intersecting the given AABB. + */ + public collidersWithAabbIntersectingAabb( + narrowPhase: NarrowPhase, + bodies: RigidBodySet, + colliders: ColliderSet, + aabbCenter: Vector, + aabbHalfExtents: Vector, + callback: (handle: ColliderHandle) => boolean, + ) { + let rawCenter = VectorOps.intoRaw(aabbCenter); + let rawHalfExtents = VectorOps.intoRaw(aabbHalfExtents); + this.raw.collidersWithAabbIntersectingAabb( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawCenter, + rawHalfExtents, + callback, + ); + rawCenter.free(); + rawHalfExtents.free(); + } } diff --git a/src.ts/geometry/collider.ts b/src.ts/geometry/collider.ts index 6f2dd272..25817362 100644 --- a/src.ts/geometry/collider.ts +++ b/src.ts/geometry/collider.ts @@ -183,7 +183,7 @@ export class Collider { */ public translationWrtParent(): Vector | null { return VectorOps.fromRaw( - this.colliderSet.raw.coTranslation(this.handle), + this.colliderSet.raw.coTranslationWrtParent(this.handle), ); } @@ -203,7 +203,7 @@ export class Collider { */ public rotationWrtParent(): Rotation | null { return RotationOps.fromRaw( - this.colliderSet.raw.coRotation(this.handle), + this.colliderSet.raw.coRotationWrtParent(this.handle), ); } diff --git a/src.ts/pipeline/query_pipeline.ts b/src.ts/pipeline/query_pipeline.ts index 8b68db6b..5610ff31 100644 --- a/src.ts/pipeline/query_pipeline.ts +++ b/src.ts/pipeline/query_pipeline.ts @@ -1,4 +1,4 @@ -import {RawQueryPipeline, RawRayColliderIntersection} from "../raw"; +import {RawRayColliderIntersection} from "../raw"; import { ColliderHandle, ColliderSet, @@ -55,495 +55,3 @@ export enum QueryFilterFlags { ONLY_FIXED = QueryFilterFlags.EXCLUDE_DYNAMIC | QueryFilterFlags.EXCLUDE_KINEMATIC, } - -/** - * A pipeline for performing queries on all the colliders of a scene. - * - * To avoid leaking WASM resources, this MUST be freed manually with `queryPipeline.free()` - * once you are done using it (and all the rigid-bodies it created). - */ -export class QueryPipeline { - raw: RawQueryPipeline; - - /** - * Release the WASM memory occupied by this query pipeline. - */ - free() { - if (!!this.raw) { - this.raw.free(); - } - this.raw = undefined; - } - - constructor(raw?: RawQueryPipeline) { - this.raw = raw || new RawQueryPipeline(); - } - - /** - * Updates the acceleration structure of the query pipeline. - * @param colliders - The set of colliders taking part in this pipeline. - */ - public update(colliders: ColliderSet) { - this.raw.update(colliders.raw); - } - - /** - * Find the closest intersection between a ray and a set of collider. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param ray - The ray to cast. - * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively - * limits the length of the ray to `ray.dir.norm() * maxToi`. - * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its - * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, - * whereas `false` implies that all shapes are hollow for this ray-cast. - * @param groups - Used to filter the colliders that can or cannot be hit by the ray. - * @param filter - The callback to filter out which collider will be hit. - */ - public castRay( - bodies: RigidBodySet, - colliders: ColliderSet, - ray: Ray, - maxToi: number, - solid: boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): RayColliderHit | null { - let rawOrig = VectorOps.intoRaw(ray.origin); - let rawDir = VectorOps.intoRaw(ray.dir); - let result = RayColliderHit.fromRaw( - colliders, - this.raw.castRay( - bodies.raw, - colliders.raw, - rawOrig, - rawDir, - maxToi, - solid, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), - ); - - rawOrig.free(); - rawDir.free(); - - return result; - } - - /** - * Find the closest intersection between a ray and a set of collider. - * - * This also computes the normal at the hit point. - * @param colliders - The set of colliders taking part in this pipeline. - * @param ray - The ray to cast. - * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively - * limits the length of the ray to `ray.dir.norm() * maxToi`. - * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its - * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, - * whereas `false` implies that all shapes are hollow for this ray-cast. - * @param groups - Used to filter the colliders that can or cannot be hit by the ray. - */ - public castRayAndGetNormal( - bodies: RigidBodySet, - colliders: ColliderSet, - ray: Ray, - maxToi: number, - solid: boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): RayColliderIntersection | null { - let rawOrig = VectorOps.intoRaw(ray.origin); - let rawDir = VectorOps.intoRaw(ray.dir); - let result = RayColliderIntersection.fromRaw( - colliders, - this.raw.castRayAndGetNormal( - bodies.raw, - colliders.raw, - rawOrig, - rawDir, - maxToi, - solid, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), - ); - - rawOrig.free(); - rawDir.free(); - - return result; - } - - /** - * Cast a ray and collects all the intersections between a ray and the scene. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param ray - The ray to cast. - * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively - * limits the length of the ray to `ray.dir.norm() * maxToi`. - * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its - * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, - * whereas `false` implies that all shapes are hollow for this ray-cast. - * @param groups - Used to filter the colliders that can or cannot be hit by the ray. - * @param callback - The callback called once per hit (in no particular order) between a ray and a collider. - * If this callback returns `false`, then the cast will stop and no further hits will be detected/reported. - */ - public intersectionsWithRay( - bodies: RigidBodySet, - colliders: ColliderSet, - ray: Ray, - maxToi: number, - solid: boolean, - callback: (intersect: RayColliderIntersection) => boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ) { - let rawOrig = VectorOps.intoRaw(ray.origin); - let rawDir = VectorOps.intoRaw(ray.dir); - let rawCallback = (rawInter: RawRayColliderIntersection) => { - return callback( - RayColliderIntersection.fromRaw(colliders, rawInter), - ); - }; - - this.raw.intersectionsWithRay( - bodies.raw, - colliders.raw, - rawOrig, - rawDir, - maxToi, - solid, - rawCallback, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ); - - rawOrig.free(); - rawDir.free(); - } - - /** - * Gets the handle of up to one collider intersecting the given shape. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param shapePos - The position of the shape used for the intersection test. - * @param shapeRot - The orientation of the shape used for the intersection test. - * @param shape - The shape used for the intersection test. - * @param groups - The bit groups and filter associated to the ray, in order to only - * hit the colliders with collision groups compatible with the ray's group. - */ - public intersectionWithShape( - bodies: RigidBodySet, - colliders: ColliderSet, - shapePos: Vector, - shapeRot: Rotation, - shape: Shape, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): ColliderHandle | null { - let rawPos = VectorOps.intoRaw(shapePos); - let rawRot = RotationOps.intoRaw(shapeRot); - let rawShape = shape.intoRaw(); - let result = this.raw.intersectionWithShape( - bodies.raw, - colliders.raw, - rawPos, - rawRot, - rawShape, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ); - - rawPos.free(); - rawRot.free(); - rawShape.free(); - - return result; - } - - /** - * Find the projection of a point on the closest collider. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param point - The point to project. - * @param solid - If this is set to `true` then the collider shapes are considered to - * be plain (if the point is located inside of a plain shape, its projection is the point - * itself). If it is set to `false` the collider shapes are considered to be hollow - * (if the point is located inside of an hollow shape, it is projected on the shape's - * boundary). - * @param groups - The bit groups and filter associated to the point to project, in order to only - * project on colliders with collision groups compatible with the ray's group. - */ - public projectPoint( - bodies: RigidBodySet, - colliders: ColliderSet, - point: Vector, - solid: boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): PointColliderProjection | null { - let rawPoint = VectorOps.intoRaw(point); - let result = PointColliderProjection.fromRaw( - colliders, - this.raw.projectPoint( - bodies.raw, - colliders.raw, - rawPoint, - solid, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), - ); - - rawPoint.free(); - - return result; - } - - /** - * Find the projection of a point on the closest collider. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param point - The point to project. - * @param groups - The bit groups and filter associated to the point to project, in order to only - * project on colliders with collision groups compatible with the ray's group. - */ - public projectPointAndGetFeature( - bodies: RigidBodySet, - colliders: ColliderSet, - point: Vector, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): PointColliderProjection | null { - let rawPoint = VectorOps.intoRaw(point); - let result = PointColliderProjection.fromRaw( - colliders, - this.raw.projectPointAndGetFeature( - bodies.raw, - colliders.raw, - rawPoint, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), - ); - - rawPoint.free(); - - return result; - } - - /** - * Find all the colliders containing the given point. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param point - The point used for the containment test. - * @param groups - The bit groups and filter associated to the point to test, in order to only - * test on colliders with collision groups compatible with the ray's group. - * @param callback - A function called with the handles of each collider with a shape - * containing the `point`. - */ - public intersectionsWithPoint( - bodies: RigidBodySet, - colliders: ColliderSet, - point: Vector, - callback: (handle: ColliderHandle) => boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ) { - let rawPoint = VectorOps.intoRaw(point); - - this.raw.intersectionsWithPoint( - bodies.raw, - colliders.raw, - rawPoint, - callback, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ); - - rawPoint.free(); - } - - /** - * Casts a shape at a constant linear velocity and retrieve the first collider it hits. - * This is similar to ray-casting except that we are casting a whole shape instead of - * just a point (the ray origin). - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param shapePos - The initial position of the shape to cast. - * @param shapeRot - The initial rotation of the shape to cast. - * @param shapeVel - The constant velocity of the shape to cast (i.e. the cast direction). - * @param shape - The shape to cast. - * @param targetDistance − If the shape moves closer to this distance from a collider, a hit - * will be returned. - * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively - * limits the distance traveled by the shape to `shapeVel.norm() * maxToi`. - * @param stopAtPenetration - If set to `false`, the linear shape-cast won’t immediately stop if - * the shape is penetrating another shape at its starting point **and** its trajectory is such - * that it’s on a path to exit that penetration state. - * @param groups - The bit groups and filter associated to the shape to cast, in order to only - * test on colliders with collision groups compatible with this group. - */ - public castShape( - bodies: RigidBodySet, - colliders: ColliderSet, - shapePos: Vector, - shapeRot: Rotation, - shapeVel: Vector, - shape: Shape, - targetDistance: number, - maxToi: number, - stopAtPenetration: boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ): ColliderShapeCastHit | null { - let rawPos = VectorOps.intoRaw(shapePos); - let rawRot = RotationOps.intoRaw(shapeRot); - let rawVel = VectorOps.intoRaw(shapeVel); - let rawShape = shape.intoRaw(); - - let result = ColliderShapeCastHit.fromRaw( - colliders, - this.raw.castShape( - bodies.raw, - colliders.raw, - rawPos, - rawRot, - rawVel, - rawShape, - targetDistance, - maxToi, - stopAtPenetration, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), - ); - - rawPos.free(); - rawRot.free(); - rawVel.free(); - rawShape.free(); - - return result; - } - - /** - * Retrieve all the colliders intersecting the given shape. - * - * @param colliders - The set of colliders taking part in this pipeline. - * @param shapePos - The position of the shape to test. - * @param shapeRot - The orientation of the shape to test. - * @param shape - The shape to test. - * @param groups - The bit groups and filter associated to the shape to test, in order to only - * test on colliders with collision groups compatible with this group. - * @param callback - A function called with the handles of each collider intersecting the `shape`. - */ - public intersectionsWithShape( - bodies: RigidBodySet, - colliders: ColliderSet, - shapePos: Vector, - shapeRot: Rotation, - shape: Shape, - callback: (handle: ColliderHandle) => boolean, - filterFlags?: QueryFilterFlags, - filterGroups?: InteractionGroups, - filterExcludeCollider?: ColliderHandle, - filterExcludeRigidBody?: RigidBodyHandle, - filterPredicate?: (collider: ColliderHandle) => boolean, - ) { - let rawPos = VectorOps.intoRaw(shapePos); - let rawRot = RotationOps.intoRaw(shapeRot); - let rawShape = shape.intoRaw(); - - this.raw.intersectionsWithShape( - bodies.raw, - colliders.raw, - rawPos, - rawRot, - rawShape, - callback, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ); - - rawPos.free(); - rawRot.free(); - rawShape.free(); - } - - /** - * Finds the handles of all the colliders with an AABB intersecting the given AABB. - * - * @param aabbCenter - The center of the AABB to test. - * @param aabbHalfExtents - The half-extents of the AABB to test. - * @param callback - The callback that will be called with the handles of all the colliders - * currently intersecting the given AABB. - */ - public collidersWithAabbIntersectingAabb( - aabbCenter: Vector, - aabbHalfExtents: Vector, - callback: (handle: ColliderHandle) => boolean, - ) { - let rawCenter = VectorOps.intoRaw(aabbCenter); - let rawHalfExtents = VectorOps.intoRaw(aabbHalfExtents); - this.raw.collidersWithAabbIntersectingAabb( - rawCenter, - rawHalfExtents, - callback, - ); - rawCenter.free(); - rawHalfExtents.free(); - } -} diff --git a/src.ts/pipeline/serialization_pipeline.ts b/src.ts/pipeline/serialization_pipeline.ts index 7e97d640..87fde398 100644 --- a/src.ts/pipeline/serialization_pipeline.ts +++ b/src.ts/pipeline/serialization_pipeline.ts @@ -13,7 +13,7 @@ import {World} from "./world"; /** * A pipeline for serializing the physics scene. * - * To avoid leaking WASM resources, this MUST be freed manually with `queryPipeline.free()` + * To avoid leaking WASM resources, this MUST be freed manually with `serializationPipeline.free()` * once you are done using it (and all the rigid-bodies it created). */ export class SerializationPipeline { diff --git a/src.ts/pipeline/world.ts b/src.ts/pipeline/world.ts index 1a7a4372..fc5c9644 100644 --- a/src.ts/pipeline/world.ts +++ b/src.ts/pipeline/world.ts @@ -9,7 +9,6 @@ import { RawMultibodyJointSet, RawNarrowPhase, RawPhysicsPipeline, - RawQueryPipeline, RawRigidBodySet, RawSerializationPipeline, RawDebugRenderPipeline, @@ -49,7 +48,7 @@ import { } from "../dynamics"; import {Rotation, Vector, VectorOps} from "../math"; import {PhysicsPipeline} from "./physics_pipeline"; -import {QueryFilterFlags, QueryPipeline} from "./query_pipeline"; +import {QueryFilterFlags} from "./query_pipeline"; import {SerializationPipeline} from "./serialization_pipeline"; import {EventQueue} from "./event_queue"; import {PhysicsHooks} from "./physics_hooks"; @@ -83,7 +82,6 @@ export class World { impulseJoints: ImpulseJointSet; multibodyJoints: MultibodyJointSet; ccdSolver: CCDSolver; - queryPipeline: QueryPipeline; physicsPipeline: PhysicsPipeline; serializationPipeline: SerializationPipeline; debugRenderPipeline: DebugRenderPipeline; @@ -111,7 +109,6 @@ export class World { this.impulseJoints.free(); this.multibodyJoints.free(); this.ccdSolver.free(); - this.queryPipeline.free(); this.physicsPipeline.free(); this.serializationPipeline.free(); this.debugRenderPipeline.free(); @@ -131,7 +128,6 @@ export class World { this.ccdSolver = undefined; this.impulseJoints = undefined; this.multibodyJoints = undefined; - this.queryPipeline = undefined; this.physicsPipeline = undefined; this.serializationPipeline = undefined; this.debugRenderPipeline = undefined; @@ -154,7 +150,6 @@ export class World { rawImpulseJoints?: RawImpulseJointSet, rawMultibodyJoints?: RawMultibodyJointSet, rawCCDSolver?: RawCCDSolver, - rawQueryPipeline?: RawQueryPipeline, rawPhysicsPipeline?: RawPhysicsPipeline, rawSerializationPipeline?: RawSerializationPipeline, rawDebugRenderPipeline?: RawDebugRenderPipeline, @@ -171,7 +166,6 @@ export class World { this.impulseJoints = new ImpulseJointSet(rawImpulseJoints); this.multibodyJoints = new MultibodyJointSet(rawMultibodyJoints); this.ccdSolver = new CCDSolver(rawCCDSolver); - this.queryPipeline = new QueryPipeline(rawQueryPipeline); this.physicsPipeline = new PhysicsPipeline(rawPhysicsPipeline); this.serializationPipeline = new SerializationPipeline( rawSerializationPipeline, @@ -286,7 +280,6 @@ export class World { eventQueue, hooks, ); - this.queryPipeline.update(this.colliders); } /** @@ -302,15 +295,16 @@ export class World { ); } - /** - * Ensure subsequent scene queries take into account the collider positions set before this method is called. - * - * This does not step the physics simulation forward. - */ - public updateSceneQueries() { - this.propagateModifiedBodyPositionsToColliders(); - this.queryPipeline.update(this.colliders); - } + // TODO: This needs to trigger a broad-phase update but without emitting collision events? + // /** + // * Ensure subsequent scene queries take into account the collider positions set before this method is called. + // * + // * This does not step the physics simulation forward. + // */ + // public updateSceneQueries() { + // this.propagateModifiedBodyPositionsToColliders(); + // this.queryPipeline.update(this.colliders); + // } /** * The current simulation timestep. @@ -481,9 +475,10 @@ export class World { let controller = new KinematicCharacterController( offset, this.integrationParameters, + this.broadPhase, + this.narrowPhase, this.bodies, this.colliders, - this.queryPipeline, ); this.characterControllers.add(controller); return controller; @@ -555,9 +550,10 @@ export class World { ): DynamicRayCastVehicleController { let controller = new DynamicRayCastVehicleController( chassis, + this.broadPhase, + this.narrowPhase, this.bodies, this.colliders, - this.queryPipeline, ); this.vehicleControllers.add(controller); return controller; @@ -783,7 +779,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): RayColliderHit | null { - return this.queryPipeline.castRay( + return this.broadPhase.castRay( + this.narrowPhase, this.bodies, this.colliders, ray, @@ -819,7 +816,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): RayColliderIntersection | null { - return this.queryPipeline.castRayAndGetNormal( + return this.broadPhase.castRayAndGetNormal( + this.narrowPhase, this.bodies, this.colliders, ray, @@ -857,7 +855,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ) { - this.queryPipeline.intersectionsWithRay( + this.broadPhase.intersectionsWithRay( + this.narrowPhase, this.bodies, this.colliders, ray, @@ -891,7 +890,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): Collider | null { - let handle = this.queryPipeline.intersectionWithShape( + let handle = this.broadPhase.intersectionWithShape( + this.narrowPhase, this.bodies, this.colliders, shapePos, @@ -927,7 +927,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): PointColliderProjection | null { - return this.queryPipeline.projectPoint( + return this.broadPhase.projectPoint( + this.narrowPhase, this.bodies, this.colliders, point, @@ -955,7 +956,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): PointColliderProjection | null { - return this.queryPipeline.projectPointAndGetFeature( + return this.broadPhase.projectPointAndGetFeature( + this.narrowPhase, this.bodies, this.colliders, point, @@ -985,7 +987,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ) { - this.queryPipeline.intersectionsWithPoint( + this.broadPhase.intersectionsWithPoint( + this.narrowPhase, this.bodies, this.colliders, point, @@ -1031,7 +1034,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ): ColliderShapeCastHit | null { - return this.queryPipeline.castShape( + return this.broadPhase.castShape( + this.narrowPhase, this.bodies, this.colliders, shapePos, @@ -1070,7 +1074,8 @@ export class World { filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean, ) { - this.queryPipeline.intersectionsWithShape( + this.broadPhase.intersectionsWithShape( + this.narrowPhase, this.bodies, this.colliders, shapePos, @@ -1098,7 +1103,10 @@ export class World { aabbHalfExtents: Vector, callback: (handle: Collider) => boolean, ) { - this.queryPipeline.collidersWithAabbIntersectingAabb( + this.broadPhase.collidersWithAabbIntersectingAabb( + this.narrowPhase, + this.bodies, + this.colliders, aabbCenter, aabbHalfExtents, this.colliders.castClosure(callback), diff --git a/src/control/character_controller.rs b/src/control/character_controller.rs index bf0ee943..9ced7db8 100644 --- a/src/control/character_controller.rs +++ b/src/control/character_controller.rs @@ -1,7 +1,6 @@ use crate::dynamics::RawRigidBodySet; -use crate::geometry::RawColliderSet; +use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase}; use crate::math::RawVector; -use crate::pipeline::RawQueryPipeline; use crate::utils::{self, FlatHandle}; use na::{Isometry, Unit}; use rapier::control::{ @@ -144,9 +143,10 @@ impl RawKinematicCharacterController { pub fn computeColliderMovement( &mut self, dt: Real, + broad_phase: &RawBroadPhase, + narrow_phase: &RawNarrowPhase, bodies: &mut RawRigidBodySet, - colliders: &RawColliderSet, - queries: &RawQueryPipeline, + colliders: &mut RawColliderSet, collider_handle: FlatHandle, desired_translation_delta: &RawVector, apply_impulses_to_dynamic_bodies: bool, @@ -157,48 +157,53 @@ impl RawKinematicCharacterController { ) { let handle = crate::utils::collider_handle(collider_handle); if let Some(collider) = colliders.0.get(handle) { + let collider_pose = *collider.position(); + let collider_shape = collider.shared_shape().clone(); + let collider_parent = collider.parent(); + crate::utils::with_filter(filter_predicate, |predicate| { let query_filter = QueryFilter { flags: QueryFilterFlags::from_bits(filter_flags) .unwrap_or(QueryFilterFlags::empty()), groups: filter_groups.map(crate::geometry::unpack_interaction_groups), exclude_collider: Some(handle), - exclude_rigid_body: collider.parent(), + exclude_rigid_body: collider_parent, predicate, }; + let character_mass = character_mass + .or_else(|| { + collider_parent + .and_then(|h| bodies.0.get(h)) + .map(|b| b.mass()) + }) + .unwrap_or(0.0); + + let query_pipeline = broad_phase.0.as_query_pipeline_mut( + narrow_phase.0.query_dispatcher(), + &mut bodies.0, + &mut colliders.0, + query_filter, + ); + self.events.clear(); let events = &mut self.events; self.result = self.controller.move_shape( dt, - &bodies.0, - &colliders.0, - &queries.0, - collider.shape(), - collider.position(), + &query_pipeline.as_ref(), + &*collider_shape, + &collider_pose, desired_translation_delta.0, - query_filter, |event| events.push(event), ); if apply_impulses_to_dynamic_bodies { - let character_mass = character_mass - .or_else(|| { - collider - .parent() - .and_then(|h| bodies.0.get(h)) - .map(|b| b.mass()) - }) - .unwrap_or(0.0); self.controller.solve_character_collision_impulses( dt, - &mut bodies.0, - &colliders.0, - &queries.0, - collider.shape(), + query_pipeline, + &*collider_shape, character_mass, self.events.iter(), - query_filter, ); } }); diff --git a/src/control/ray_cast_vehicle_controller.rs b/src/control/ray_cast_vehicle_controller.rs index e8002bc1..36bd7f61 100644 --- a/src/control/ray_cast_vehicle_controller.rs +++ b/src/control/ray_cast_vehicle_controller.rs @@ -1,7 +1,6 @@ use crate::dynamics::RawRigidBodySet; -use crate::geometry::RawColliderSet; +use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase}; use crate::math::RawVector; -use crate::pipeline::RawQueryPipeline; use crate::utils::{self, FlatHandle}; use rapier::control::{DynamicRayCastVehicleController, WheelTuning}; use rapier::math::Real; @@ -69,9 +68,10 @@ impl RawDynamicRayCastVehicleController { pub fn update_vehicle( &mut self, dt: Real, + broad_phase: &RawBroadPhase, + narrow_phase: &RawNarrowPhase, bodies: &mut RawRigidBodySet, - colliders: &RawColliderSet, - queries: &RawQueryPipeline, + colliders: &mut RawColliderSet, filter_flags: u32, filter_groups: Option, filter_predicate: &js_sys::Function, @@ -86,13 +86,14 @@ impl RawDynamicRayCastVehicleController { exclude_collider: None, }; - self.controller.update_vehicle( - dt, + let query_pipeline = broad_phase.0.as_query_pipeline_mut( + narrow_phase.0.query_dispatcher(), &mut bodies.0, - &colliders.0, - &queries.0, + &mut colliders.0, query_filter, ); + + self.controller.update_vehicle(dt, query_pipeline); }); } diff --git a/src/geometry/broad_phase.rs b/src/geometry/broad_phase.rs index 7f0b947f..218a1cde 100644 --- a/src/geometry/broad_phase.rs +++ b/src/geometry/broad_phase.rs @@ -1,4 +1,16 @@ +use crate::dynamics::RawRigidBodySet; +use crate::geometry::{ + RawColliderSet, RawColliderShapeCastHit, RawNarrowPhase, RawPointColliderProjection, + RawRayColliderHit, RawRayColliderIntersection, RawShape, +}; +use crate::math::{RawRotation, RawVector}; +use crate::utils::{self, FlatHandle}; use rapier::geometry::DefaultBroadPhase; +use rapier::geometry::{Aabb, ColliderHandle, Ray}; +use rapier::math::{Isometry, Point}; +use rapier::parry::query::ShapeCastOptions; +use rapier::pipeline::{QueryFilter, QueryFilterFlags}; +use rapier::prelude::FeatureId; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -10,4 +22,441 @@ impl RawBroadPhase { pub fn new() -> Self { RawBroadPhase(DefaultBroadPhase::new()) } + + pub fn castRay( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + rayOrig: &RawVector, + rayDir: &RawVector, + maxToi: f32, + solid: bool, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + let (handle, timeOfImpact) = utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let ray = Ray::new(rayOrig.0.into(), rayDir.0); + query_pipeline.cast_ray(&ray, maxToi, solid) + })?; + + Some(RawRayColliderHit { + handle, + timeOfImpact, + }) + } + + pub fn castRayAndGetNormal( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + rayOrig: &RawVector, + rayDir: &RawVector, + maxToi: f32, + solid: bool, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + let (handle, inter) = utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let ray = Ray::new(rayOrig.0.into(), rayDir.0); + query_pipeline.cast_ray_and_get_normal(&ray, maxToi, solid) + })?; + + Some(RawRayColliderIntersection { handle, inter }) + } + + // The callback is of type (RawRayColliderIntersection) => bool + pub fn intersectionsWithRay( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + rayOrig: &RawVector, + rayDir: &RawVector, + maxToi: f32, + solid: bool, + callback: &js_sys::Function, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let ray = Ray::new(rayOrig.0.into(), rayDir.0); + let rcallback = |handle, inter| { + let result = RawRayColliderIntersection { handle, inter }; + match callback.call1(&JsValue::null(), &JsValue::from(result)) { + Err(_) => true, + Ok(val) => val.as_bool().unwrap_or(true), + } + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + for (handle, _, inter) in query_pipeline.intersect_ray(&ray, maxToi, solid) { + if !rcallback(handle, inter) { + break; + } + } + }); + } + + pub fn intersectionWithShape( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + shapePos: &RawVector, + shapeRot: &RawRotation, + shape: &RawShape, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); + + // TODO: take a callback as argument so we can yield all the intersecting shapes? + for (handle, _) in query_pipeline.intersect_shape(&pos, &*shape.0) { + // Return the first intersection we find. + return Some(utils::flat_handle(handle.0)); + } + + None + }) + } + + pub fn projectPoint( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + point: &RawVector, + solid: bool, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + query_pipeline + .project_point(&point.0.into(), f32::MAX, solid) + .map(|(handle, proj)| RawPointColliderProjection { + handle, + proj, + feature: FeatureId::Unknown, + }) + }) + } + + pub fn projectPointAndGetFeature( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + point: &RawVector, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + query_pipeline + .project_point_and_get_feature(&point.0.into()) + .map(|(handle, proj, feature)| RawPointColliderProjection { + handle, + proj, + feature, + }) + }) + } + + // The callback is of type (u32) => bool + pub fn intersectionsWithPoint( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + point: &RawVector, + callback: &js_sys::Function, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let rcallback = |handle: ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(utils::flat_handle(handle.0)), + ) { + Err(_) => true, + Ok(val) => val.as_bool().unwrap_or(true), + }; + + for (handle, _) in query_pipeline.intersect_point(&point.0.into()) { + if !rcallback(handle) { + break; + } + } + }); + } + + pub fn castShape( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + shapePos: &RawVector, + shapeRot: &RawRotation, + shapeVel: &RawVector, + shape: &RawShape, + target_distance: f32, + maxToi: f32, + stop_at_penetration: bool, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) -> Option { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); + query_pipeline + .cast_shape( + &pos, + &shapeVel.0, + &*shape.0, + ShapeCastOptions { + max_time_of_impact: maxToi, + stop_at_penetration, + compute_impact_geometry_on_penetration: true, + target_distance, + }, + ) + .map(|(handle, hit)| RawColliderShapeCastHit { handle, hit }) + }) + } + + // The callback has type (u32) => boolean + pub fn intersectionsWithShape( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + shapePos: &RawVector, + shapeRot: &RawRotation, + shape: &RawShape, + callback: &js_sys::Function, + filter_flags: u32, + filter_groups: Option, + filter_exclude_collider: Option, + filter_exclude_rigid_body: Option, + filter_predicate: &js_sys::Function, + ) { + utils::with_filter(filter_predicate, |predicate| { + let query_filter = QueryFilter { + flags: QueryFilterFlags::from_bits(filter_flags) + .unwrap_or(QueryFilterFlags::empty()), + groups: filter_groups.map(crate::geometry::unpack_interaction_groups), + exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), + exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), + predicate, + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + query_filter, + ); + + let rcallback = |handle: ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(utils::flat_handle(handle.0)), + ) { + Err(_) => true, + Ok(val) => val.as_bool().unwrap_or(true), + }; + + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); + for (handle, _) in query_pipeline.intersect_shape(&pos, &*shape.0) { + if !rcallback(handle) { + break; + } + } + }) + } + + pub fn collidersWithAabbIntersectingAabb( + &self, + narrow_phase: &RawNarrowPhase, + bodies: &RawRigidBodySet, + colliders: &RawColliderSet, + aabbCenter: &RawVector, + aabbHalfExtents: &RawVector, + callback: &js_sys::Function, + ) { + let rcallback = |handle: &ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(utils::flat_handle(handle.0)), + ) { + Err(_) => true, + Ok(val) => val.as_bool().unwrap_or(true), + }; + + let query_pipeline = self.0.as_query_pipeline( + narrow_phase.0.query_dispatcher(), + &bodies.0, + &colliders.0, + Default::default(), + ); + + let center = Point::from(aabbCenter.0); + let aabb = Aabb::new(center - aabbHalfExtents.0, center + aabbHalfExtents.0); + + for (handle, _) in query_pipeline.intersect_aabb_conservative(&aabb) { + if !rcallback(&handle) { + break; + } + } + } } diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 5f5d3490..e8019044 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -40,8 +40,7 @@ impl RawColliderSet { /// Returns the `None` if it doesn’t have a parent. pub fn coRotationWrtParent(&self, handle: FlatHandle) -> Option { self.map(handle, |co| { - co.position_wrt_parent() - .map(|pose| pose.rotation.into()) + co.position_wrt_parent().map(|pose| pose.rotation.into()) }) } diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index c751bbf2..0e139001 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -84,8 +84,7 @@ impl RawContactPair { } pub fn contactManifold(&self, i: usize) -> Option { unsafe { - (*self.0) - .manifolds + (&(*self.0).manifolds) .get(i) .map(|m| RawContactManifold(m as *const ContactManifold)) } @@ -123,29 +122,28 @@ impl RawContactManifold { } pub fn contact_local_p1(&self, i: usize) -> Option { - unsafe { (*self.0).points.get(i).map(|c| c.local_p1.coords.into()) } + unsafe { (&(*self.0).points).get(i).map(|c| c.local_p1.coords.into()) } } pub fn contact_local_p2(&self, i: usize) -> Option { - unsafe { (*self.0).points.get(i).map(|c| c.local_p2.coords.into()) } + unsafe { (&(*self.0).points).get(i).map(|c| c.local_p2.coords.into()) } } pub fn contact_dist(&self, i: usize) -> Real { - unsafe { (*self.0).points.get(i).map(|c| c.dist).unwrap_or(0.0) } + unsafe { (&(*self.0).points).get(i).map(|c| c.dist).unwrap_or(0.0) } } pub fn contact_fid1(&self, i: usize) -> u32 { - unsafe { (*self.0).points.get(i).map(|c| c.fid1.0).unwrap_or(0) } + unsafe { (&(*self.0).points).get(i).map(|c| c.fid1.0).unwrap_or(0) } } pub fn contact_fid2(&self, i: usize) -> u32 { - unsafe { (*self.0).points.get(i).map(|c| c.fid2.0).unwrap_or(0) } + unsafe { (&(*self.0).points).get(i).map(|c| c.fid2.0).unwrap_or(0) } } pub fn contact_impulse(&self, i: usize) -> Real { unsafe { - (*self.0) - .points + (&(*self.0).points) .get(i) .map(|c| c.data.impulse) .unwrap_or(0.0) @@ -155,8 +153,8 @@ impl RawContactManifold { #[cfg(feature = "dim2")] pub fn contact_tangent_impulse(&self, i: usize) -> Real { unsafe { - (*self.0) - .points + (&(*self.0) + .points) .get(i) .map(|c| c.data.tangent_impulse.x) .unwrap_or(0.0) @@ -166,8 +164,7 @@ impl RawContactManifold { #[cfg(feature = "dim3")] pub fn contact_tangent_impulse_x(&self, i: usize) -> Real { unsafe { - (*self.0) - .points + (&(*self.0).points) .get(i) .map(|c| c.data.tangent_impulse.x) .unwrap_or(0.0) @@ -177,8 +174,7 @@ impl RawContactManifold { #[cfg(feature = "dim3")] pub fn contact_tangent_impulse_y(&self, i: usize) -> Real { unsafe { - (*self.0) - .points + (&(*self.0).points) .get(i) .map(|c| c.data.tangent_impulse.y) .unwrap_or(0.0) @@ -191,8 +187,7 @@ impl RawContactManifold { pub fn solver_contact_point(&self, i: usize) -> Option { unsafe { - (*self.0) - .data + (&(*self.0).data) .solver_contacts .get(i) .map(|c| c.point.coords.into()) @@ -201,8 +196,7 @@ impl RawContactManifold { pub fn solver_contact_dist(&self, i: usize) -> Real { unsafe { - (*self.0) - .data + (&(*self.0).data) .solver_contacts .get(i) .map(|c| c.dist) @@ -211,14 +205,14 @@ impl RawContactManifold { } pub fn solver_contact_friction(&self, i: usize) -> Real { - unsafe { (*self.0).data.solver_contacts[i].friction } + unsafe { (&(*self.0).data).solver_contacts[i].friction } } pub fn solver_contact_restitution(&self, i: usize) -> Real { - unsafe { (*self.0).data.solver_contacts[i].restitution } + unsafe { (&(*self.0).data).solver_contacts[i].restitution } } pub fn solver_contact_tangent_velocity(&self, i: usize) -> RawVector { - unsafe { (*self.0).data.solver_contacts[i].tangent_velocity.into() } + unsafe { (&(*self.0).data).solver_contacts[i].tangent_velocity.into() } } } diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index eabd9cc7..23e19bfb 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -2,12 +2,10 @@ pub use self::debug_render_pipeline::*; pub use self::event_queue::*; pub use self::physics_hooks::*; pub use self::physics_pipeline::*; -pub use self::query_pipeline::*; pub use self::serialization_pipeline::*; mod debug_render_pipeline; mod event_queue; mod physics_hooks; mod physics_pipeline; -mod query_pipeline; mod serialization_pipeline; diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 8c25e712..81b492f4 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -42,7 +42,6 @@ impl RawPhysicsPipeline { &mut joints.0, &mut articulations.0, &mut ccd_solver.0, - None, &(), &(), ); @@ -86,7 +85,6 @@ impl RawPhysicsPipeline { &mut joints.0, &mut articulations.0, &mut ccd_solver.0, - None, &hooks, &eventQueue.collector, ); diff --git a/src/pipeline/query_pipeline.rs b/src/pipeline/query_pipeline.rs deleted file mode 100644 index 56c77400..00000000 --- a/src/pipeline/query_pipeline.rs +++ /dev/null @@ -1,405 +0,0 @@ -use crate::dynamics::RawRigidBodySet; -use crate::geometry::{ - RawColliderSet, RawColliderShapeCastHit, RawPointColliderProjection, RawRayColliderHit, - RawRayColliderIntersection, RawShape, -}; -use crate::math::{RawRotation, RawVector}; -use crate::utils::{self, FlatHandle}; -use rapier::geometry::{Aabb, ColliderHandle, Ray}; -use rapier::math::{Isometry, Point}; -use rapier::parry::query::ShapeCastOptions; -use rapier::pipeline::{QueryFilter, QueryFilterFlags, QueryPipeline}; -use rapier::prelude::FeatureId; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -pub struct RawQueryPipeline(pub(crate) QueryPipeline); - -#[wasm_bindgen] -impl RawQueryPipeline { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - RawQueryPipeline(QueryPipeline::new()) - } - - pub fn update(&mut self, colliders: &RawColliderSet) { - self.0.update(&colliders.0); - } - - pub fn castRay( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - rayOrig: &RawVector, - rayDir: &RawVector, - maxToi: f32, - solid: bool, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - let (handle, timeOfImpact) = utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let ray = Ray::new(rayOrig.0.into(), rayDir.0); - self.0 - .cast_ray(&bodies.0, &colliders.0, &ray, maxToi, solid, query_filter) - })?; - - Some(RawRayColliderHit { - handle, - timeOfImpact, - }) - } - - pub fn castRayAndGetNormal( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - rayOrig: &RawVector, - rayDir: &RawVector, - maxToi: f32, - solid: bool, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - let (handle, inter) = utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let ray = Ray::new(rayOrig.0.into(), rayDir.0); - self.0.cast_ray_and_get_normal( - &bodies.0, - &colliders.0, - &ray, - maxToi, - solid, - query_filter, - ) - })?; - - Some(RawRayColliderIntersection { handle, inter }) - } - - // The callback is of type (RawRayColliderIntersection) => bool - pub fn intersectionsWithRay( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - rayOrig: &RawVector, - rayDir: &RawVector, - maxToi: f32, - solid: bool, - callback: &js_sys::Function, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let ray = Ray::new(rayOrig.0.into(), rayDir.0); - let rcallback = |handle, inter| { - let result = RawRayColliderIntersection { handle, inter }; - match callback.call1(&JsValue::null(), &JsValue::from(result)) { - Err(_) => true, - Ok(val) => val.as_bool().unwrap_or(true), - } - }; - - self.0.intersections_with_ray( - &bodies.0, - &colliders.0, - &ray, - maxToi, - solid, - query_filter, - rcallback, - ); - }); - } - - pub fn intersectionWithShape( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - shapePos: &RawVector, - shapeRot: &RawRotation, - shape: &RawShape, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); - self.0 - .intersection_with_shape(&bodies.0, &colliders.0, &pos, &*shape.0, query_filter) - .map(|h| utils::flat_handle(h.0)) - }) - } - - pub fn projectPoint( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - point: &RawVector, - solid: bool, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - self.0 - .project_point( - &bodies.0, - &colliders.0, - &point.0.into(), - solid, - query_filter, - ) - .map(|(handle, proj)| RawPointColliderProjection { - handle, - proj, - feature: FeatureId::Unknown, - }) - }) - } - - pub fn projectPointAndGetFeature( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - point: &RawVector, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - self.0 - .project_point_and_get_feature( - &bodies.0, - &colliders.0, - &point.0.into(), - query_filter, - ) - .map(|(handle, proj, feature)| RawPointColliderProjection { - handle, - proj, - feature, - }) - }) - } - - // The callback is of type (u32) => bool - pub fn intersectionsWithPoint( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - point: &RawVector, - callback: &js_sys::Function, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let rcallback = |handle: ColliderHandle| match callback.call1( - &JsValue::null(), - &JsValue::from(utils::flat_handle(handle.0)), - ) { - Err(_) => true, - Ok(val) => val.as_bool().unwrap_or(true), - }; - self.0.intersections_with_point( - &bodies.0, - &colliders.0, - &point.0.into(), - query_filter, - rcallback, - ) - }); - } - - pub fn castShape( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - shapePos: &RawVector, - shapeRot: &RawRotation, - shapeVel: &RawVector, - shape: &RawShape, - target_distance: f32, - maxToi: f32, - stop_at_penetration: bool, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) -> Option { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); - self.0 - .cast_shape( - &bodies.0, - &colliders.0, - &pos, - &shapeVel.0, - &*shape.0, - ShapeCastOptions { - max_time_of_impact: maxToi, - stop_at_penetration, - compute_impact_geometry_on_penetration: true, - target_distance, - }, - query_filter, - ) - .map(|(handle, hit)| RawColliderShapeCastHit { handle, hit }) - }) - } - - // The callback has type (u32) => boolean - pub fn intersectionsWithShape( - &self, - bodies: &RawRigidBodySet, - colliders: &RawColliderSet, - shapePos: &RawVector, - shapeRot: &RawRotation, - shape: &RawShape, - callback: &js_sys::Function, - filter_flags: u32, - filter_groups: Option, - filter_exclude_collider: Option, - filter_exclude_rigid_body: Option, - filter_predicate: &js_sys::Function, - ) { - utils::with_filter(filter_predicate, |predicate| { - let query_filter = QueryFilter { - flags: QueryFilterFlags::from_bits(filter_flags) - .unwrap_or(QueryFilterFlags::empty()), - groups: filter_groups.map(crate::geometry::unpack_interaction_groups), - exclude_collider: filter_exclude_collider.map(crate::utils::collider_handle), - exclude_rigid_body: filter_exclude_rigid_body.map(crate::utils::body_handle), - predicate, - }; - - let rcallback = |handle: ColliderHandle| match callback.call1( - &JsValue::null(), - &JsValue::from(utils::flat_handle(handle.0)), - ) { - Err(_) => true, - Ok(val) => val.as_bool().unwrap_or(true), - }; - - let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); - self.0.intersections_with_shape( - &bodies.0, - &colliders.0, - &pos, - &*shape.0, - query_filter, - rcallback, - ) - }) - } - - pub fn collidersWithAabbIntersectingAabb( - &self, - aabbCenter: &RawVector, - aabbHalfExtents: &RawVector, - callback: &js_sys::Function, - ) { - let rcallback = |handle: &ColliderHandle| match callback.call1( - &JsValue::null(), - &JsValue::from(utils::flat_handle(handle.0)), - ) { - Err(_) => true, - Ok(val) => val.as_bool().unwrap_or(true), - }; - - let center = Point::from(aabbCenter.0); - let aabb = Aabb::new(center - aabbHalfExtents.0, center + aabbHalfExtents.0); - - self.0 - .colliders_with_aabb_intersecting_aabb(&aabb, rcallback) - } -} From 1520f8a9815f703ad591bfa0949af6e3f5fe08b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 12 Jul 2025 00:18:51 +0200 Subject: [PATCH 4/5] Release v0.18.0-beta.0 --- CHANGELOG.md | 2 +- Cargo.lock | 10 +++++----- builds/prepare_builds/templates/Cargo.toml.tera | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 580013bf..d20d0bc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### Unreleased +### 0.18.0-beta.0 (12 July 2025) #### Modified diff --git a/Cargo.lock b/Cargo.lock index ff375001..b2595e35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-deterministic" -version = "0.17.3" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-simd" -version = "0.17.3" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", @@ -411,7 +411,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d" -version = "0.17.3" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-deterministic" -version = "0.17.3" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", @@ -439,7 +439,7 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-simd" -version = "0.17.3" +version = "0.18.0-beta.0" dependencies = [ "bincode", "js-sys", diff --git a/builds/prepare_builds/templates/Cargo.toml.tera b/builds/prepare_builds/templates/Cargo.toml.tera index 6147ccb6..af35c195 100644 --- a/builds/prepare_builds/templates/Cargo.toml.tera +++ b/builds/prepare_builds/templates/Cargo.toml.tera @@ -1,6 +1,6 @@ [package] name = "dimforge_{{ js_package_name }}" # Can't be named rapier{{ dimension }}d which conflicts with the dependency. -version = "0.17.3" +version = "0.18.0-beta.0" authors = ["Sébastien Crozet "] description = "{{ dimension }}-dimensional physics engine in Rust - official JS bindings." documentation = "https://rapier.rs/rustdoc/rapier{{ dimension }}d/index.html" From a8870947f4389189465d1b9e62232e10f0dfeb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 12 Jul 2025 00:22:54 +0200 Subject: [PATCH 5/5] chore: run prettier --- src.ts/control/character_controller.ts | 9 ++++++++- src.ts/control/ray_cast_vehicle_controller.ts | 8 +++++++- src.ts/geometry/broad_phase.ts | 1 - src/geometry/narrow_phase.rs | 3 +-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src.ts/control/character_controller.ts b/src.ts/control/character_controller.ts index e2392ea2..8e536392 100644 --- a/src.ts/control/character_controller.ts +++ b/src.ts/control/character_controller.ts @@ -1,6 +1,13 @@ import {RawKinematicCharacterController, RawCharacterCollision} from "../raw"; import {Rotation, Vector, VectorOps} from "../math"; -import {BroadPhase, Collider, ColliderSet, InteractionGroups, NarrowPhase, Shape} from "../geometry"; +import { + BroadPhase, + Collider, + ColliderSet, + InteractionGroups, + NarrowPhase, + Shape, +} from "../geometry"; import {QueryFilterFlags, World} from "../pipeline"; import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics"; diff --git a/src.ts/control/ray_cast_vehicle_controller.ts b/src.ts/control/ray_cast_vehicle_controller.ts index 15405523..fa65b3e3 100644 --- a/src.ts/control/ray_cast_vehicle_controller.ts +++ b/src.ts/control/ray_cast_vehicle_controller.ts @@ -1,6 +1,12 @@ import {RawDynamicRayCastVehicleController} from "../raw"; import {Vector, VectorOps} from "../math"; -import {BroadPhase, Collider, ColliderSet, InteractionGroups, NarrowPhase} from "../geometry"; +import { + BroadPhase, + Collider, + ColliderSet, + InteractionGroups, + NarrowPhase, +} from "../geometry"; import {QueryFilterFlags} from "../pipeline"; import {RigidBody, RigidBodyHandle, RigidBodySet} from "../dynamics"; diff --git a/src.ts/geometry/broad_phase.ts b/src.ts/geometry/broad_phase.ts index b9aaeec8..addaee92 100644 --- a/src.ts/geometry/broad_phase.ts +++ b/src.ts/geometry/broad_phase.ts @@ -34,7 +34,6 @@ export class BroadPhase { this.raw = raw || new RawBroadPhase(); } - /** * Find the closest intersection between a ray and a set of collider. * diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index 0e139001..be3fee25 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -153,8 +153,7 @@ impl RawContactManifold { #[cfg(feature = "dim2")] pub fn contact_tangent_impulse(&self, i: usize) -> Real { unsafe { - (&(*self.0) - .points) + (&(*self.0).points) .get(i) .map(|c| c.data.tangent_impulse.x) .unwrap_or(0.0)