diff --git a/src.ts/pipeline/query_pipeline.ts b/src.ts/pipeline/query_pipeline.ts index 978ef709..82b05dd6 100644 --- a/src.ts/pipeline/query_pipeline.ts +++ b/src.ts/pipeline/query_pipeline.ts @@ -5,7 +5,7 @@ import { InteractionGroups, PointColliderProjection, Ray, RayColliderIntersection, - RayColliderToi, Shape, ShapeColliderTOI, ShapeTOI + RayColliderToi, Shape, ShapeColliderTOI } from "../geometry"; import { IslandManager, RigidBodySet } from "../dynamics"; import { Rotation, RotationOps, Vector, VectorOps } from "../math"; @@ -51,13 +51,15 @@ export class QueryPipeline { * 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( colliders: ColliderSet, ray: Ray, maxToi: number, solid: boolean, - groups: InteractionGroups + groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): RayColliderToi | null { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); @@ -68,6 +70,7 @@ export class QueryPipeline { maxToi, solid, groups, + filter )); rawOrig.free(); @@ -95,7 +98,8 @@ export class QueryPipeline { ray: Ray, maxToi: number, solid: boolean, - groups: InteractionGroups + groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): RayColliderIntersection | null { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); @@ -106,6 +110,7 @@ export class QueryPipeline { maxToi, solid, groups, + filter )); rawOrig.free(); @@ -136,6 +141,7 @@ export class QueryPipeline { solid: boolean, groups: InteractionGroups, callback: (intersect: RayColliderIntersection) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); @@ -151,6 +157,7 @@ export class QueryPipeline { solid, groups, rawCallback, + filter ); rawOrig.free(); @@ -173,6 +180,7 @@ export class QueryPipeline { shapeRot: Rotation, shape: Shape, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): ColliderHandle | null { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); @@ -183,6 +191,7 @@ export class QueryPipeline { rawRot, rawShape, groups, + filter ); rawPos.free(); @@ -210,6 +219,7 @@ export class QueryPipeline { point: Vector, solid: boolean, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): PointColliderProjection | null { let rawPoint = VectorOps.intoRaw(point); let result = PointColliderProjection.fromRaw(this.raw.projectPoint( @@ -217,6 +227,7 @@ export class QueryPipeline { rawPoint, solid, groups, + filter )); rawPoint.free(); @@ -239,6 +250,7 @@ export class QueryPipeline { point: Vector, groups: InteractionGroups, callback: (handle: ColliderHandle) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { let rawPoint = VectorOps.intoRaw(point); @@ -247,6 +259,7 @@ export class QueryPipeline { rawPoint, groups, callback, + filter ); rawPoint.free(); @@ -275,6 +288,7 @@ export class QueryPipeline { shape: Shape, maxToi: number, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): ShapeColliderTOI | null { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); @@ -289,6 +303,7 @@ export class QueryPipeline { rawShape, maxToi, groups, + filter )); rawPos.free(); @@ -317,6 +332,7 @@ export class QueryPipeline { shape: Shape, groups: InteractionGroups, callback: (handle: ColliderHandle) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); @@ -329,6 +345,7 @@ export class QueryPipeline { rawShape, groups, callback, + filter ); rawPos.free(); diff --git a/src.ts/pipeline/world.ts b/src.ts/pipeline/world.ts index 4bb44572..aaa2f126 100644 --- a/src.ts/pipeline/world.ts +++ b/src.ts/pipeline/world.ts @@ -14,7 +14,7 @@ import { NarrowPhase, PointColliderProjection, Ray, RayColliderIntersection, - RayColliderToi, Shape, ShapeColliderTOI, ShapeTOI, TempContactManifold + RayColliderToi, Shape, ShapeColliderTOI, TempContactManifold } from "../geometry"; import { CCDSolver, @@ -507,14 +507,16 @@ export class World { * 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( ray: Ray, maxToi: number, solid: boolean, - groups: InteractionGroups + groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): RayColliderToi | null { - return this.queryPipeline.castRay(this.colliders, ray, maxToi, solid, groups); + return this.queryPipeline.castRay(this.colliders, ray, maxToi, solid, groups, filter); } /** @@ -533,9 +535,10 @@ export class World { ray: Ray, maxToi: number, solid: boolean, - groups: InteractionGroups + groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): RayColliderIntersection | null { - return this.queryPipeline.castRayAndGetNormal(this.colliders, ray, maxToi, solid, groups); + return this.queryPipeline.castRayAndGetNormal(this.colliders, ray, maxToi, solid, groups, filter); } @@ -558,8 +561,9 @@ export class World { solid: boolean, groups: InteractionGroups, callback: (intersect: RayColliderIntersection) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { - this.queryPipeline.intersectionsWithRay(this.colliders, ray, maxToi, solid, groups, callback) + this.queryPipeline.intersectionsWithRay(this.colliders, ray, maxToi, solid, groups, callback, filter) } /** @@ -576,8 +580,9 @@ export class World { shapeRot: Rotation, shape: Shape, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): ColliderHandle | null { - return this.queryPipeline.intersectionWithShape(this.colliders, shapePos, shapeRot, shape, groups); + return this.queryPipeline.intersectionWithShape(this.colliders, shapePos, shapeRot, shape, groups, filter); } /** @@ -596,8 +601,9 @@ export class World { point: Vector, solid: boolean, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): PointColliderProjection | null { - return this.queryPipeline.projectPoint(this.colliders, point, solid, groups); + return this.queryPipeline.projectPoint(this.colliders, point, solid, groups, filter); } /** @@ -613,8 +619,9 @@ export class World { point: Vector, groups: InteractionGroups, callback: (handle: ColliderHandle) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { - this.queryPipeline.intersectionsWithPoint(this.colliders, point, groups, callback); + this.queryPipeline.intersectionsWithPoint(this.colliders, point, groups, callback, filter); } /** @@ -638,8 +645,9 @@ export class World { shape: Shape, maxToi: number, groups: InteractionGroups, + filter?: (collider: ColliderHandle) => boolean ): ShapeColliderTOI | null { - return this.queryPipeline.castShape(this.colliders, shapePos, shapeRot, shapeVel, shape, maxToi, groups); + return this.queryPipeline.castShape(this.colliders, shapePos, shapeRot, shapeVel, shape, maxToi, groups, filter); } /** @@ -658,8 +666,9 @@ export class World { shape: Shape, groups: InteractionGroups, callback: (handle: ColliderHandle) => boolean, + filter?: (collider: ColliderHandle) => boolean ) { - this.queryPipeline.intersectionsWithShape(this.colliders, shapePos, shapeRot, shape, groups, callback); + this.queryPipeline.intersectionsWithShape(this.colliders, shapePos, shapeRot, shape, groups, callback, filter); } /** @@ -673,7 +682,7 @@ export class World { public collidersWithAabbIntersectingAabb( aabbCenter: Vector, aabbHalfExtents: Vector, - callback: (handle: ColliderHandle) => boolean, + callback: (handle: ColliderHandle) => boolean ) { this.queryPipeline.collidersWithAabbIntersectingAabb(aabbCenter, aabbHalfExtents, callback); } @@ -717,5 +726,4 @@ export class World { public intersectionPair(collider1: ColliderHandle, collider2: ColliderHandle): boolean { return this.narrowPhase.intersectionPair(collider1, collider2); } - } diff --git a/src/pipeline/query_pipeline.rs b/src/pipeline/query_pipeline.rs index b00802a0..b807982e 100644 --- a/src/pipeline/query_pipeline.rs +++ b/src/pipeline/query_pipeline.rs @@ -36,15 +36,21 @@ impl RawQueryPipeline { maxToi: f32, solid: bool, groups: u32, + filter: &js_sys::Function, ) -> Option { let ray = Ray::new(rayOrig.0.into(), rayDir.0); + let filter = wrap_filter(filter); + let filter = filter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + let (handle, toi) = self.0.cast_ray( &colliders.0, &ray, maxToi, solid, crate::geometry::unpack_interaction_groups(groups), - None, + filter, )?; Some(RawRayColliderToi { handle, toi }) } @@ -57,15 +63,21 @@ impl RawQueryPipeline { maxToi: f32, solid: bool, groups: u32, + filter: &js_sys::Function, ) -> Option { let ray = Ray::new(rayOrig.0.into(), rayDir.0); + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + let (handle, inter) = self.0.cast_ray_and_get_normal( &colliders.0, &ray, maxToi, solid, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, )?; Some(RawRayColliderIntersection { handle, inter }) } @@ -80,26 +92,31 @@ impl RawQueryPipeline { solid: bool, groups: u32, callback: &js_sys::Function, + filter: &js_sys::Function, ) { let ray = Ray::new(rayOrig.0.into(), rayDir.0); - let this = JsValue::null(); let rcallback = |handle, inter| { let result = RawRayColliderIntersection { handle, inter }; - match callback.call1(&this, &JsValue::from(result)) { + match callback.call1(&JsValue::null(), &JsValue::from(result)) { Err(_) => true, Ok(val) => val.as_bool().unwrap_or(true), } }; + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + self.0.intersections_with_ray( &colliders.0, &ray, maxToi, solid, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, rcallback, - ) + ); } pub fn intersectionWithShape( @@ -109,7 +126,13 @@ impl RawQueryPipeline { shapeRot: &RawRotation, shape: &RawShape, groups: u32, + filter: &js_sys::Function, ) -> Option { + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); self.0 .intersection_with_shape( @@ -117,7 +140,7 @@ impl RawQueryPipeline { &pos, &*shape.0, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, ) .map(|h| h.into_raw_parts().0) } @@ -128,14 +151,20 @@ impl RawQueryPipeline { point: &RawVector, solid: bool, groups: u32, + filter: &js_sys::Function, ) -> Option { + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + self.0 .project_point( &colliders.0, &point.0.into(), solid, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, ) .map(|(handle, proj)| RawPointColliderProjection { handle, proj }) } @@ -147,20 +176,25 @@ impl RawQueryPipeline { point: &RawVector, groups: u32, callback: &js_sys::Function, + filter: &js_sys::Function, ) { - let this = JsValue::null(); - let rcallback = |handle: ColliderHandle| match callback - .call1(&this, &JsValue::from(handle.into_raw_parts().0 as u32)) - { + let rcallback = |handle: ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(handle.into_raw_parts().0 as u32), + ) { Err(_) => true, Ok(val) => val.as_bool().unwrap_or(true), }; + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); self.0.intersections_with_point( &colliders.0, &point.0.into(), crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, rcallback, ) } @@ -183,7 +217,13 @@ impl RawQueryPipeline { shape: &RawShape, maxToi: f32, groups: u32, + filter: &js_sys::Function, ) -> Option { + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); self.0 .cast_shape( @@ -193,7 +233,7 @@ impl RawQueryPipeline { &*shape.0, maxToi, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, ) .map(|(handle, toi)| RawShapeColliderTOI { handle, toi }) } @@ -207,22 +247,28 @@ impl RawQueryPipeline { shape: &RawShape, groups: u32, callback: &js_sys::Function, + filter: &js_sys::Function, ) { - let this = JsValue::null(); - let rcallback = |handle: ColliderHandle| match callback - .call1(&this, &JsValue::from(handle.into_raw_parts().0 as u32)) - { + let rcallback = |handle: ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(handle.into_raw_parts().0 as u32), + ) { Err(_) => true, Ok(val) => val.as_bool().unwrap_or(true), }; + let rfilter = wrap_filter(filter); + let rfilter = rfilter + .as_ref() + .map(|f| f as &dyn Fn(ColliderHandle) -> bool); + let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0); self.0.intersections_with_shape( &colliders.0, &pos, &*shape.0, crate::geometry::unpack_interaction_groups(groups), - None, + rfilter, rcallback, ) } @@ -233,10 +279,10 @@ impl RawQueryPipeline { aabbHalfExtents: &RawVector, callback: &js_sys::Function, ) { - let this = JsValue::null(); - let rcallback = |handle: &ColliderHandle| match callback - .call1(&this, &JsValue::from(handle.into_raw_parts().0 as u32)) - { + let rcallback = |handle: &ColliderHandle| match callback.call1( + &JsValue::null(), + &JsValue::from(handle.into_raw_parts().0 as u32), + ) { Err(_) => true, Ok(val) => val.as_bool().unwrap_or(true), }; @@ -248,3 +294,19 @@ impl RawQueryPipeline { .colliders_with_aabb_intersecting_aabb(&aabb, rcallback) } } + +fn wrap_filter(filter: &js_sys::Function) -> Option bool + '_> { + if filter.is_function() { + let filtercb = move |handle: ColliderHandle| match filter.call1( + &JsValue::null(), + &JsValue::from(handle.into_raw_parts().0 as u32), + ) { + Err(_) => true, + Ok(val) => val.as_bool().unwrap_or(true), + }; + + Some(filtercb) + } else { + None + } +}