chore: 添加第三方依赖库

This commit is contained in:
yhh
2025-12-03 16:24:08 +08:00
parent cb1b171216
commit 83aee02540
172 changed files with 27480 additions and 0 deletions

View File

@@ -0,0 +1,462 @@
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]
pub struct RawBroadPhase(pub(crate) DefaultBroadPhase);
#[wasm_bindgen]
impl RawBroadPhase {
#[wasm_bindgen(constructor)]
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<RawRayColliderHit> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<RawRayColliderIntersection> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<FlatHandle> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<RawPointColliderProjection> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<RawPointColliderProjection> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
filter_predicate: &js_sys::Function,
) -> Option<RawColliderShapeCastHit> {
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<u32>,
filter_exclude_collider: Option<FlatHandle>,
filter_exclude_rigid_body: Option<FlatHandle>,
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;
}
}
}
}

View File

@@ -0,0 +1,993 @@
use crate::geometry::shape::SharedShapeUtility;
use crate::geometry::{
RawColliderSet, RawColliderShapeCastHit, RawPointProjection, RawRayIntersection, RawShape,
RawShapeCastHit, RawShapeContact, RawShapeType,
};
use crate::math::{RawRotation, RawVector};
use crate::utils::{self, FlatHandle};
use rapier::dynamics::MassProperties;
use rapier::geometry::{ActiveCollisionTypes, ShapeType};
use rapier::math::{Isometry, Point, Real, Vector};
use rapier::parry::query;
use rapier::parry::query::ShapeCastOptions;
use rapier::pipeline::{ActiveEvents, ActiveHooks};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
impl RawColliderSet {
/// The world-space translation of this collider.
pub fn coTranslation(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |co| co.position().translation.vector.into())
}
/// The world-space orientation of this collider.
pub fn coRotation(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |co| co.position().rotation.into())
}
/// The translation of this collider relative to its parent rigid-body.
///
/// Returns the `None` if it doesnt have a parent.
pub fn coTranslationWrtParent(&self, handle: FlatHandle) -> Option<RawVector> {
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 doesnt have a parent.
pub fn coRotationWrtParent(&self, handle: FlatHandle) -> Option<RawRotation> {
self.map(handle, |co| {
co.position_wrt_parent().map(|pose| pose.rotation.into())
})
}
/// Sets the translation of this collider.
///
/// # Parameters
/// - `x`: the world-space position of the collider along the `x` axis.
/// - `y`: the world-space position of the collider along the `y` axis.
/// - `z`: the world-space position of the collider along the `z` axis.
/// - `wakeUp`: forces the collider to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim3")]
pub fn coSetTranslation(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32) {
self.map_mut(handle, |co| {
co.set_translation(na::Vector3::new(x, y, z));
})
}
/// Sets the translation of this collider.
///
/// # Parameters
/// - `x`: the world-space position of the collider along the `x` axis.
/// - `y`: the world-space position of the collider along the `y` axis.
/// - `wakeUp`: forces the collider to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim2")]
pub fn coSetTranslation(&mut self, handle: FlatHandle, x: f32, y: f32) {
self.map_mut(handle, |co| {
co.set_translation(na::Vector2::new(x, y));
})
}
#[cfg(feature = "dim3")]
pub fn coSetTranslationWrtParent(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32) {
self.map_mut(handle, |co| {
co.set_translation_wrt_parent(na::Vector3::new(x, y, z));
})
}
#[cfg(feature = "dim2")]
pub fn coSetTranslationWrtParent(&mut self, handle: FlatHandle, x: f32, y: f32) {
self.map_mut(handle, |co| {
co.set_translation_wrt_parent(na::Vector2::new(x, y));
})
}
/// Sets the rotation quaternion of this collider.
///
/// This does nothing if a zero quaternion is provided.
///
/// # Parameters
/// - `x`: the first vector component of the quaternion.
/// - `y`: the second vector component of the quaternion.
/// - `z`: the third vector component of the quaternion.
/// - `w`: the scalar component of the quaternion.
/// - `wakeUp`: forces the collider to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim3")]
pub fn coSetRotation(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32, w: f32) {
if let Some(q) = na::Unit::try_new(na::Quaternion::new(w, x, y, z), 0.0) {
self.map_mut(handle, |co| co.set_rotation(q))
}
}
/// Sets the rotation angle of this collider.
///
/// # Parameters
/// - `angle`: the rotation angle, in radians.
/// - `wakeUp`: forces the collider to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim2")]
pub fn coSetRotation(&mut self, handle: FlatHandle, angle: f32) {
self.map_mut(handle, |co| co.set_rotation(na::UnitComplex::new(angle)))
}
#[cfg(feature = "dim3")]
pub fn coSetRotationWrtParent(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32, w: f32) {
if let Some(q) = na::Unit::try_new(na::Quaternion::new(w, x, y, z), 0.0) {
self.map_mut(handle, |co| co.set_rotation_wrt_parent(q.scaled_axis()))
}
}
#[cfg(feature = "dim2")]
pub fn coSetRotationWrtParent(&mut self, handle: FlatHandle, angle: f32) {
self.map_mut(handle, |co| co.set_rotation_wrt_parent(angle))
}
/// Is this collider a sensor?
pub fn coIsSensor(&self, handle: FlatHandle) -> bool {
self.map(handle, |co| co.is_sensor())
}
/// The type of the shape of this collider.
pub fn coShapeType(&self, handle: FlatHandle) -> RawShapeType {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::Ball => RawShapeType::Ball,
ShapeType::Cuboid => RawShapeType::Cuboid,
ShapeType::Capsule => RawShapeType::Capsule,
ShapeType::Segment => RawShapeType::Segment,
ShapeType::Polyline => RawShapeType::Polyline,
ShapeType::Triangle => RawShapeType::Triangle,
ShapeType::TriMesh => RawShapeType::TriMesh,
ShapeType::HeightField => RawShapeType::HeightField,
ShapeType::Compound => RawShapeType::Compound,
ShapeType::HalfSpace => RawShapeType::HalfSpace,
ShapeType::Voxels => RawShapeType::Voxels,
#[cfg(feature = "dim3")]
ShapeType::ConvexPolyhedron => RawShapeType::ConvexPolyhedron,
#[cfg(feature = "dim2")]
ShapeType::ConvexPolygon => RawShapeType::ConvexPolygon,
#[cfg(feature = "dim3")]
ShapeType::Cylinder => RawShapeType::Cylinder,
#[cfg(feature = "dim3")]
ShapeType::Cone => RawShapeType::Cone,
ShapeType::RoundCuboid => RawShapeType::RoundCuboid,
ShapeType::RoundTriangle => RawShapeType::RoundTriangle,
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => RawShapeType::RoundCylinder,
#[cfg(feature = "dim3")]
ShapeType::RoundCone => RawShapeType::RoundCone,
#[cfg(feature = "dim3")]
ShapeType::RoundConvexPolyhedron => RawShapeType::RoundConvexPolyhedron,
#[cfg(feature = "dim2")]
ShapeType::RoundConvexPolygon => RawShapeType::RoundConvexPolygon,
ShapeType::Custom => panic!("Not yet implemented."),
})
}
pub fn coHalfspaceNormal(&self, handle: FlatHandle) -> Option<RawVector> {
self.map(handle, |co| {
co.shape()
.as_halfspace()
.map(|h| h.normal.into_inner().into())
})
}
/// The half-extents of this collider if it is has a cuboid shape.
pub fn coHalfExtents(&self, handle: FlatHandle) -> Option<RawVector> {
self.map(handle, |co| {
co.shape()
.as_cuboid()
.map(|c| c.half_extents.into())
.or_else(|| {
co.shape()
.as_round_cuboid()
.map(|c| c.inner_shape.half_extents.into())
})
})
}
/// Set the half-extents of this collider if it has a cuboid shape.
pub fn coSetHalfExtents(&mut self, handle: FlatHandle, newHalfExtents: &RawVector) {
self.map_mut(handle, |co| match co.shape().shape_type() {
ShapeType::Cuboid => co
.shape_mut()
.as_cuboid_mut()
.map(|b| b.half_extents = newHalfExtents.0.into()),
ShapeType::RoundCuboid => co
.shape_mut()
.as_round_cuboid_mut()
.map(|b| b.inner_shape.half_extents = newHalfExtents.0.into()),
_ => None,
});
}
/// The radius of this collider if it is a ball, capsule, cylinder, or cone shape.
pub fn coRadius(&self, handle: FlatHandle) -> Option<f32> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::Ball => co.shape().as_ball().map(|b| b.radius),
ShapeType::Capsule => co.shape().as_capsule().map(|b| b.radius),
#[cfg(feature = "dim3")]
ShapeType::Cylinder => co.shape().as_cylinder().map(|b| b.radius),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => {
co.shape().as_round_cylinder().map(|b| b.inner_shape.radius)
}
#[cfg(feature = "dim3")]
ShapeType::Cone => co.shape().as_cone().map(|b| b.radius),
_ => None,
})
}
/// Set the radius of this collider if it is a ball, capsule, cylinder, or cone shape.
pub fn coSetRadius(&mut self, handle: FlatHandle, newRadius: Real) {
self.map_mut(handle, |co| match co.shape().shape_type() {
ShapeType::Ball => co.shape_mut().as_ball_mut().map(|b| b.radius = newRadius),
ShapeType::Capsule => co
.shape_mut()
.as_capsule_mut()
.map(|b| b.radius = newRadius),
#[cfg(feature = "dim3")]
ShapeType::Cylinder => co
.shape_mut()
.as_cylinder_mut()
.map(|b| b.radius = newRadius),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => co
.shape_mut()
.as_round_cylinder_mut()
.map(|b| b.inner_shape.radius = newRadius),
#[cfg(feature = "dim3")]
ShapeType::Cone => co.shape_mut().as_cone_mut().map(|b| b.radius = newRadius),
_ => None,
});
}
/// The half height of this collider if it is a capsule, cylinder, or cone shape.
pub fn coHalfHeight(&self, handle: FlatHandle) -> Option<f32> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::Capsule => co.shape().as_capsule().map(|b| b.half_height()),
#[cfg(feature = "dim3")]
ShapeType::Cylinder => co.shape().as_cylinder().map(|b| b.half_height),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => co
.shape()
.as_round_cylinder()
.map(|b| b.inner_shape.half_height),
#[cfg(feature = "dim3")]
ShapeType::Cone => co.shape().as_cone().map(|b| b.half_height),
_ => None,
})
}
/// Set the half height of this collider if it is a capsule, cylinder, or cone shape.
pub fn coSetHalfHeight(&mut self, handle: FlatHandle, newHalfheight: Real) {
self.map_mut(handle, |co| match co.shape().shape_type() {
ShapeType::Capsule => {
let point = Point::from(Vector::y() * newHalfheight);
co.shape_mut().as_capsule_mut().map(|b| {
b.segment.a = -point;
b.segment.b = point;
})
}
#[cfg(feature = "dim3")]
ShapeType::Cylinder => co
.shape_mut()
.as_cylinder_mut()
.map(|b| b.half_height = newHalfheight),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => co
.shape_mut()
.as_round_cylinder_mut()
.map(|b| b.inner_shape.half_height = newHalfheight),
#[cfg(feature = "dim3")]
ShapeType::Cone => co
.shape_mut()
.as_cone_mut()
.map(|b| b.half_height = newHalfheight),
_ => None,
});
}
/// The radius of the round edges of this collider.
pub fn coRoundRadius(&self, handle: FlatHandle) -> Option<f32> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::RoundCuboid => co.shape().as_round_cuboid().map(|b| b.border_radius),
ShapeType::RoundTriangle => co.shape().as_round_triangle().map(|b| b.border_radius),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => co.shape().as_round_cylinder().map(|b| b.border_radius),
#[cfg(feature = "dim3")]
ShapeType::RoundCone => co.shape().as_round_cone().map(|b| b.border_radius),
#[cfg(feature = "dim3")]
ShapeType::RoundConvexPolyhedron => co
.shape()
.as_round_convex_polyhedron()
.map(|b| b.border_radius),
#[cfg(feature = "dim2")]
ShapeType::RoundConvexPolygon => co
.shape()
.as_round_convex_polygon()
.map(|b| b.border_radius),
_ => None,
})
}
/// Set the radius of the round edges of this collider.
pub fn coSetRoundRadius(&mut self, handle: FlatHandle, newBorderRadius: Real) {
self.map_mut(handle, |co| match co.shape().shape_type() {
ShapeType::RoundCuboid => co
.shape_mut()
.as_round_cuboid_mut()
.map(|b| b.border_radius = newBorderRadius),
ShapeType::RoundTriangle => co
.shape_mut()
.as_round_triangle_mut()
.map(|b| b.border_radius = newBorderRadius),
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => co
.shape_mut()
.as_round_cylinder_mut()
.map(|b| b.border_radius = newBorderRadius),
#[cfg(feature = "dim3")]
ShapeType::RoundCone => co
.shape_mut()
.as_round_cone_mut()
.map(|b| b.border_radius = newBorderRadius),
#[cfg(feature = "dim3")]
ShapeType::RoundConvexPolyhedron => co
.shape_mut()
.as_round_convex_polyhedron_mut()
.map(|b| b.border_radius = newBorderRadius),
#[cfg(feature = "dim2")]
ShapeType::RoundConvexPolygon => co
.shape_mut()
.as_round_convex_polygon_mut()
.map(|b| b.border_radius = newBorderRadius),
_ => None,
});
}
pub fn coVoxelData(&self, handle: FlatHandle) -> Option<Vec<i32>> {
self.map(handle, |co| {
let vox = co.shape().as_voxels()?;
let coords = vox
.voxels()
.filter_map(|vox| (!vox.state.is_empty()).then_some(vox.grid_coords))
.flat_map(|ids| ids.coords.data.0[0])
.collect();
Some(coords)
})
}
pub fn coVoxelSize(&self, handle: FlatHandle) -> Option<RawVector> {
self.map(handle, |co| {
let vox = co.shape().as_voxels()?;
Some(RawVector(vox.voxel_size()))
})
}
#[cfg(feature = "dim2")]
pub fn coSetVoxel(&mut self, handle: FlatHandle, ix: i32, iy: i32, filled: bool) {
self.map_mut(handle, |co| {
if let Some(vox) = co.shape_mut().as_voxels_mut() {
vox.set_voxel(Point::new(ix, iy), filled);
}
})
}
#[cfg(feature = "dim3")]
pub fn coSetVoxel(&mut self, handle: FlatHandle, ix: i32, iy: i32, iz: i32, filled: bool) {
self.map_mut(handle, |co| {
if let Some(vox) = co.shape_mut().as_voxels_mut() {
vox.set_voxel(Point::new(ix, iy, iz), filled);
}
})
}
#[cfg(feature = "dim2")]
pub fn coPropagateVoxelChange(
&mut self,
handle1: FlatHandle,
handle2: FlatHandle,
ix: i32,
iy: i32,
shift_x: i32,
shift_y: i32,
) {
self.map_pair_mut(handle1, handle2, |co1, co2| {
if let (Some(co1), Some(co2)) = (co1, co2) {
if let (Some(vox1), Some(vox2)) = (
co1.shape_mut().as_voxels_mut(),
co2.shape_mut().as_voxels_mut(),
) {
vox1.propagate_voxel_change(
vox2,
Point::new(ix, iy),
Vector::new(shift_x, shift_y),
);
}
}
})
}
#[cfg(feature = "dim3")]
pub fn coPropagateVoxelChange(
&mut self,
handle1: FlatHandle,
handle2: FlatHandle,
ix: i32,
iy: i32,
iz: i32,
shift_x: i32,
shift_y: i32,
shift_z: i32,
) {
self.map_pair_mut(handle1, handle2, |co1, co2| {
if let (Some(co1), Some(co2)) = (co1, co2) {
if let (Some(vox1), Some(vox2)) = (
co1.shape_mut().as_voxels_mut(),
co2.shape_mut().as_voxels_mut(),
) {
vox1.propagate_voxel_change(
vox2,
Point::new(ix, iy, iz),
Vector::new(shift_x, shift_y, shift_z),
);
}
}
})
}
#[cfg(feature = "dim2")]
pub fn coCombineVoxelStates(
&mut self,
handle1: FlatHandle,
handle2: FlatHandle,
shift_x: i32,
shift_y: i32,
) {
self.map_pair_mut(handle1, handle2, |co1, co2| {
if let (Some(co1), Some(co2)) = (co1, co2) {
if let (Some(vox1), Some(vox2)) = (
co1.shape_mut().as_voxels_mut(),
co2.shape_mut().as_voxels_mut(),
) {
vox1.combine_voxel_states(vox2, Vector::new(shift_x, shift_y));
}
}
})
}
#[cfg(feature = "dim3")]
pub fn coCombineVoxelStates(
&mut self,
handle1: FlatHandle,
handle2: FlatHandle,
shift_x: i32,
shift_y: i32,
shift_z: i32,
) {
self.map_pair_mut(handle1, handle2, |co1, co2| {
if let (Some(co1), Some(co2)) = (co1, co2) {
if let (Some(vox1), Some(vox2)) = (
co1.shape_mut().as_voxels_mut(),
co2.shape_mut().as_voxels_mut(),
) {
vox1.combine_voxel_states(vox2, Vector::new(shift_x, shift_y, shift_z));
}
}
})
}
/// The vertices of this triangle mesh, polyline, convex polyhedron, segment, triangle or convex polyhedron, if it is one.
pub fn coVertices(&self, handle: FlatHandle) -> Option<Vec<f32>> {
let flatten =
|vertices: &[Point<f32>]| vertices.iter().flat_map(|p| p.iter()).copied().collect();
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::TriMesh => co.shape().as_trimesh().map(|t| flatten(t.vertices())),
#[cfg(feature = "dim2")]
ShapeType::Polyline => co.shape().as_polyline().map(|p| flatten(p.vertices())),
#[cfg(feature = "dim3")]
ShapeType::ConvexPolyhedron => co
.shape()
.as_convex_polyhedron()
.map(|p| flatten(p.points())),
#[cfg(feature = "dim3")]
ShapeType::RoundConvexPolyhedron => co
.shape()
.as_round_convex_polyhedron()
.map(|p| flatten(p.inner_shape.points())),
#[cfg(feature = "dim2")]
ShapeType::ConvexPolygon => co.shape().as_convex_polygon().map(|p| flatten(p.points())),
#[cfg(feature = "dim2")]
ShapeType::RoundConvexPolygon => co
.shape()
.as_round_convex_polygon()
.map(|p| flatten(p.inner_shape.points())),
ShapeType::Segment => co.shape().as_segment().map(|s| flatten(&[s.a, s.b])),
ShapeType::RoundTriangle => co
.shape()
.as_round_triangle()
.map(|t| flatten(&[t.inner_shape.a, t.inner_shape.b, t.inner_shape.c])),
ShapeType::Triangle => co.shape().as_triangle().map(|t| flatten(&[t.a, t.b, t.c])),
_ => None,
})
}
/// The indices of this triangle mesh, polyline, or convex polyhedron, if it is one.
pub fn coIndices(&self, handle: FlatHandle) -> Option<Vec<u32>> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::TriMesh => co
.shape()
.as_trimesh()
.map(|t| t.indices().iter().flat_map(|p| p.iter()).copied().collect()),
ShapeType::Polyline => co
.shape()
.as_polyline()
.map(|p| p.indices().iter().flat_map(|p| p.iter()).copied().collect()),
#[cfg(feature = "dim3")]
ShapeType::ConvexPolyhedron => co.shape().as_convex_polyhedron().map(|p| {
// TODO: avoid the `.to_trimesh()`.
p.to_trimesh()
.1
.iter()
.flat_map(|p| p.iter())
.copied()
.collect()
}),
#[cfg(feature = "dim3")]
ShapeType::RoundConvexPolyhedron => co.shape().as_round_convex_polyhedron().map(|p| {
// TODO: avoid the `.to_trimesh()`.
p.inner_shape
.to_trimesh()
.1
.iter()
.flat_map(|p| p.iter())
.copied()
.collect()
}),
_ => None,
})
}
pub fn coTriMeshFlags(&self, handle: FlatHandle) -> Option<u32> {
self.map(handle, |co| {
co.shape().as_trimesh().map(|tri| tri.flags().bits() as u32)
})
}
#[cfg(feature = "dim3")]
pub fn coHeightFieldFlags(&self, handle: FlatHandle) -> Option<u32> {
self.map(handle, |co| {
co.shape()
.as_heightfield()
.map(|hf| hf.flags().bits() as u32)
})
}
/// The height of this heightfield if it is one.
pub fn coHeightfieldHeights(&self, handle: FlatHandle) -> Option<Vec<f32>> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::HeightField => co
.shape()
.as_heightfield()
.map(|h| h.heights().as_slice().to_vec()),
_ => None,
})
}
/// The scaling factor applied of this heightfield if it is one.
pub fn coHeightfieldScale(&self, handle: FlatHandle) -> Option<RawVector> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::HeightField => co.shape().as_heightfield().map(|h| RawVector(*h.scale())),
_ => None,
})
}
/// The number of rows on this heightfield's height matrix, if it is one.
#[cfg(feature = "dim3")]
pub fn coHeightfieldNRows(&self, handle: FlatHandle) -> Option<usize> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::HeightField => co.shape().as_heightfield().map(|h| h.nrows()),
_ => None,
})
}
/// The number of columns on this heightfield's height matrix, if it is one.
#[cfg(feature = "dim3")]
pub fn coHeightfieldNCols(&self, handle: FlatHandle) -> Option<usize> {
self.map(handle, |co| match co.shape().shape_type() {
ShapeType::HeightField => co.shape().as_heightfield().map(|h| h.ncols()),
_ => None,
})
}
/// The unique integer identifier of the collider this collider is attached to.
pub fn coParent(&self, handle: FlatHandle) -> Option<FlatHandle> {
self.map(handle, |co| co.parent().map(|p| utils::flat_handle(p.0)))
}
pub fn coSetEnabled(&mut self, handle: FlatHandle, enabled: bool) {
self.map_mut(handle, |co| co.set_enabled(enabled))
}
pub fn coIsEnabled(&self, handle: FlatHandle) -> bool {
self.map(handle, |co| co.is_enabled())
}
pub fn coSetContactSkin(&mut self, handle: FlatHandle, contact_skin: f32) {
self.map_mut(handle, |co| co.set_contact_skin(contact_skin))
}
pub fn coContactSkin(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.contact_skin())
}
/// The friction coefficient of this collider.
pub fn coFriction(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.material().friction)
}
/// The restitution coefficient of this collider.
pub fn coRestitution(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.material().restitution)
}
/// The density of this collider.
pub fn coDensity(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.density())
}
/// The mass of this collider.
pub fn coMass(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.mass())
}
/// The volume of this collider.
pub fn coVolume(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.volume())
}
/// The collision groups of this collider.
pub fn coCollisionGroups(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| {
super::pack_interaction_groups(co.collision_groups())
})
}
/// The solver groups of this collider.
pub fn coSolverGroups(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| {
super::pack_interaction_groups(co.solver_groups())
})
}
/// The physics hooks enabled for this collider.
pub fn coActiveHooks(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| co.active_hooks().bits())
}
/// The collision types enabled for this collider.
pub fn coActiveCollisionTypes(&self, handle: FlatHandle) -> u16 {
self.map(handle, |co| co.active_collision_types().bits())
}
/// The events enabled for this collider.
pub fn coActiveEvents(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| co.active_events().bits())
}
/// The total force magnitude beyond which a contact force event can be emitted.
pub fn coContactForceEventThreshold(&self, handle: FlatHandle) -> f32 {
self.map(handle, |co| co.contact_force_event_threshold())
}
pub fn coContainsPoint(&self, handle: FlatHandle, point: &RawVector) -> bool {
self.map(handle, |co| {
co.shared_shape()
.containsPoint(co.position(), &point.0.into())
})
}
pub fn coCastShape(
&self,
handle: FlatHandle,
colliderVel: &RawVector,
shape2: &RawShape,
shape2Pos: &RawVector,
shape2Rot: &RawRotation,
shape2Vel: &RawVector,
target_distance: f32,
maxToi: f32,
stop_at_penetration: bool,
) -> Option<RawShapeCastHit> {
let pos2 = Isometry::from_parts(shape2Pos.0.into(), shape2Rot.0);
self.map(handle, |co| {
let pos1 = co.position();
co.shared_shape().castShape(
pos1,
&colliderVel.0.into(),
&*shape2.0,
&pos2,
&shape2Vel.0.into(),
target_distance,
maxToi,
stop_at_penetration,
)
})
}
pub fn coCastCollider(
&self,
handle: FlatHandle,
collider1Vel: &RawVector,
collider2handle: FlatHandle,
collider2Vel: &RawVector,
target_distance: f32,
max_toi: f32,
stop_at_penetration: bool,
) -> Option<RawColliderShapeCastHit> {
let handle2 = utils::collider_handle(collider2handle);
let co2 = self
.0
.get(handle2)
.expect("Invalid Collider reference. It may have been removed from the physics World.");
self.map(handle, |co| {
query::cast_shapes(
co.position(),
&collider1Vel.0,
co.shape(),
co2.position(),
&collider2Vel.0,
co2.shape(),
ShapeCastOptions {
max_time_of_impact: max_toi,
stop_at_penetration,
target_distance,
compute_impact_geometry_on_penetration: true,
},
)
.unwrap_or(None)
.map_or(None, |hit| {
Some(RawColliderShapeCastHit {
handle: handle2,
hit,
})
})
})
}
pub fn coIntersectsShape(
&self,
handle: FlatHandle,
shape2: &RawShape,
shapePos2: &RawVector,
shapeRot2: &RawRotation,
) -> bool {
let pos2 = Isometry::from_parts(shapePos2.0.into(), shapeRot2.0);
self.map(handle, |co| {
co.shared_shape()
.intersectsShape(co.position(), &*shape2.0, &pos2)
})
}
pub fn coContactShape(
&self,
handle: FlatHandle,
shape2: &RawShape,
shapePos2: &RawVector,
shapeRot2: &RawRotation,
prediction: f32,
) -> Option<RawShapeContact> {
let pos2 = Isometry::from_parts(shapePos2.0.into(), shapeRot2.0);
self.map(handle, |co| {
co.shared_shape()
.contactShape(co.position(), &*shape2.0, &pos2, prediction)
})
}
pub fn coContactCollider(
&self,
handle: FlatHandle,
collider2handle: FlatHandle,
prediction: f32,
) -> Option<RawShapeContact> {
let co2 = self
.0
.get(utils::collider_handle(collider2handle))
.expect("Invalid Collider reference. It may have been removed from the physics World.");
self.map(handle, |co| {
query::contact(
co.position(),
co.shape(),
&co2.position(),
co2.shape(),
prediction,
)
.ok()
.flatten()
.map(|contact| RawShapeContact { contact })
})
}
pub fn coProjectPoint(
&self,
handle: FlatHandle,
point: &RawVector,
solid: bool,
) -> RawPointProjection {
self.map(handle, |co| {
co.shared_shape()
.projectPoint(co.position(), &point.0.into(), solid)
})
}
pub fn coIntersectsRay(
&self,
handle: FlatHandle,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
) -> bool {
self.map(handle, |co| {
co.shared_shape().intersectsRay(
co.position(),
rayOrig.0.into(),
rayDir.0.into(),
maxToi,
)
})
}
pub fn coCastRay(
&self,
handle: FlatHandle,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
solid: bool,
) -> f32 {
self.map(handle, |co| {
co.shared_shape().castRay(
co.position(),
rayOrig.0.into(),
rayDir.0.into(),
maxToi,
solid,
)
})
}
pub fn coCastRayAndGetNormal(
&self,
handle: FlatHandle,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
solid: bool,
) -> Option<RawRayIntersection> {
self.map(handle, |co| {
co.shared_shape().castRayAndGetNormal(
co.position(),
rayOrig.0.into(),
rayDir.0.into(),
maxToi,
solid,
)
})
}
pub fn coSetSensor(&mut self, handle: FlatHandle, is_sensor: bool) {
self.map_mut(handle, |co| co.set_sensor(is_sensor))
}
pub fn coSetRestitution(&mut self, handle: FlatHandle, restitution: f32) {
self.map_mut(handle, |co| co.set_restitution(restitution))
}
pub fn coSetFriction(&mut self, handle: FlatHandle, friction: f32) {
self.map_mut(handle, |co| co.set_friction(friction))
}
pub fn coFrictionCombineRule(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| co.friction_combine_rule() as u32)
}
pub fn coSetFrictionCombineRule(&mut self, handle: FlatHandle, rule: u32) {
let rule = super::combine_rule_from_u32(rule);
self.map_mut(handle, |co| co.set_friction_combine_rule(rule))
}
pub fn coRestitutionCombineRule(&self, handle: FlatHandle) -> u32 {
self.map(handle, |co| co.restitution_combine_rule() as u32)
}
pub fn coSetRestitutionCombineRule(&mut self, handle: FlatHandle, rule: u32) {
let rule = super::combine_rule_from_u32(rule);
self.map_mut(handle, |co| co.set_restitution_combine_rule(rule))
}
pub fn coSetCollisionGroups(&mut self, handle: FlatHandle, groups: u32) {
let groups = super::unpack_interaction_groups(groups);
self.map_mut(handle, |co| co.set_collision_groups(groups))
}
pub fn coSetSolverGroups(&mut self, handle: FlatHandle, groups: u32) {
let groups = super::unpack_interaction_groups(groups);
self.map_mut(handle, |co| co.set_solver_groups(groups))
}
pub fn coSetActiveHooks(&mut self, handle: FlatHandle, hooks: u32) {
let hooks = ActiveHooks::from_bits(hooks).unwrap_or(ActiveHooks::empty());
self.map_mut(handle, |co| co.set_active_hooks(hooks));
}
pub fn coSetActiveEvents(&mut self, handle: FlatHandle, events: u32) {
let events = ActiveEvents::from_bits(events).unwrap_or(ActiveEvents::empty());
self.map_mut(handle, |co| co.set_active_events(events))
}
pub fn coSetActiveCollisionTypes(&mut self, handle: FlatHandle, types: u16) {
let types = ActiveCollisionTypes::from_bits(types).unwrap_or(ActiveCollisionTypes::empty());
self.map_mut(handle, |co| co.set_active_collision_types(types));
}
pub fn coSetShape(&mut self, handle: FlatHandle, shape: &RawShape) {
self.map_mut(handle, |co| co.set_shape(shape.0.clone()));
}
pub fn coSetContactForceEventThreshold(&mut self, handle: FlatHandle, threshold: f32) {
self.map_mut(handle, |co| co.set_contact_force_event_threshold(threshold))
}
pub fn coSetDensity(&mut self, handle: FlatHandle, density: f32) {
self.map_mut(handle, |co| co.set_density(density))
}
pub fn coSetMass(&mut self, handle: FlatHandle, mass: f32) {
self.map_mut(handle, |co| co.set_mass(mass))
}
#[cfg(feature = "dim3")]
pub fn coSetMassProperties(
&mut self,
handle: FlatHandle,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: &RawVector,
angularInertiaFrame: &RawRotation,
) {
self.map_mut(handle, |co| {
let mprops = MassProperties::with_principal_inertia_frame(
centerOfMass.0.into(),
mass,
principalAngularInertia.0,
angularInertiaFrame.0,
);
co.set_mass_properties(mprops)
})
}
#[cfg(feature = "dim2")]
pub fn coSetMassProperties(
&mut self,
handle: FlatHandle,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: f32,
) {
self.map_mut(handle, |co| {
let props = MassProperties::new(centerOfMass.0.into(), mass, principalAngularInertia);
co.set_mass_properties(props)
})
}
}

View File

@@ -0,0 +1,293 @@
use crate::dynamics::{RawIslandManager, RawRigidBodySet};
use crate::geometry::RawShape;
use crate::math::{RawRotation, RawVector};
use crate::utils::{self, FlatHandle};
use rapier::prelude::*;
use wasm_bindgen::prelude::*;
// NOTE: this MUST match the same enum on the TS side.
enum MassPropsMode {
Density = 0,
Mass,
MassProps,
}
#[wasm_bindgen]
pub struct RawColliderSet(pub(crate) ColliderSet);
impl RawColliderSet {
pub(crate) fn map<T>(&self, handle: FlatHandle, f: impl FnOnce(&Collider) -> T) -> T {
let collider = self
.0
.get(utils::collider_handle(handle))
.expect("Invalid Collider reference. It may have been removed from the physics World.");
f(collider)
}
pub(crate) fn map_mut<T>(
&mut self,
handle: FlatHandle,
f: impl FnOnce(&mut Collider) -> T,
) -> T {
let collider = self
.0
.get_mut(utils::collider_handle(handle))
.expect("Invalid Collider reference. It may have been removed from the physics World.");
f(collider)
}
pub(crate) fn map_pair_mut<T>(
&mut self,
handle1: FlatHandle,
handle2: FlatHandle,
f: impl FnOnce(Option<&mut Collider>, Option<&mut Collider>) -> T,
) -> T {
let (collider1, collider2) = self.0.get_pair_mut(
utils::collider_handle(handle1),
utils::collider_handle(handle2),
);
f(collider1, collider2)
}
}
impl RawColliderSet {
// This is a workaround because wasm-bindgen doesn't support the `cfg(feature = ...)`
// for the method arguments.
pub fn do_create_collider(
&mut self,
enabled: bool,
shape: &RawShape,
translation: &RawVector,
rotation: &RawRotation,
massPropsMode: u32,
mass: f32,
centerOfMass: &RawVector,
#[cfg(feature = "dim2")] principalAngularInertia: f32,
#[cfg(feature = "dim3")] principalAngularInertia: &RawVector,
#[cfg(feature = "dim3")] angularInertiaFrame: &RawRotation,
density: f32,
friction: f32,
restitution: f32,
frictionCombineRule: u32,
restitutionCombineRule: u32,
isSensor: bool,
collisionGroups: u32,
solverGroups: u32,
activeCollisionTypes: u16,
activeHooks: u32,
activeEvents: u32,
contactForceEventThreshold: f32,
contactSkin: f32,
hasParent: bool,
parent: FlatHandle,
bodies: &mut RawRigidBodySet,
) -> Option<FlatHandle> {
let pos = Isometry::from_parts(translation.0.into(), rotation.0);
let mut builder = ColliderBuilder::new(shape.0.clone())
.enabled(enabled)
.position(pos)
.friction(friction)
.restitution(restitution)
.collision_groups(super::unpack_interaction_groups(collisionGroups))
.solver_groups(super::unpack_interaction_groups(solverGroups))
.active_hooks(ActiveHooks::from_bits(activeHooks).unwrap_or(ActiveHooks::empty()))
.active_events(ActiveEvents::from_bits(activeEvents).unwrap_or(ActiveEvents::empty()))
.active_collision_types(
ActiveCollisionTypes::from_bits(activeCollisionTypes)
.unwrap_or(ActiveCollisionTypes::empty()),
)
.sensor(isSensor)
.friction_combine_rule(super::combine_rule_from_u32(frictionCombineRule))
.restitution_combine_rule(super::combine_rule_from_u32(restitutionCombineRule))
.contact_force_event_threshold(contactForceEventThreshold)
.contact_skin(contactSkin);
if massPropsMode == MassPropsMode::MassProps as u32 {
#[cfg(feature = "dim2")]
let mprops = MassProperties::new(centerOfMass.0.into(), mass, principalAngularInertia);
#[cfg(feature = "dim3")]
let mprops = MassProperties::with_principal_inertia_frame(
centerOfMass.0.into(),
mass,
principalAngularInertia.0,
angularInertiaFrame.0,
);
builder = builder.mass_properties(mprops);
} else if massPropsMode == MassPropsMode::Density as u32 {
builder = builder.density(density);
} else {
assert_eq!(massPropsMode, MassPropsMode::Mass as u32);
builder = builder.mass(mass);
};
let collider = builder.build();
if hasParent {
Some(utils::flat_handle(
self.0
.insert_with_parent(collider, utils::body_handle(parent), &mut bodies.0)
.0,
))
} else {
Some(utils::flat_handle(self.0.insert(collider).0))
}
}
}
#[wasm_bindgen]
impl RawColliderSet {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawColliderSet(ColliderSet::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn contains(&self, handle: FlatHandle) -> bool {
self.0.get(utils::collider_handle(handle)).is_some()
}
#[cfg(feature = "dim2")]
pub fn createCollider(
&mut self,
enabled: bool,
shape: &RawShape,
translation: &RawVector,
rotation: &RawRotation,
massPropsMode: u32,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: f32,
density: f32,
friction: f32,
restitution: f32,
frictionCombineRule: u32,
restitutionCombineRule: u32,
isSensor: bool,
collisionGroups: u32,
solverGroups: u32,
activeCollisionTypes: u16,
activeHooks: u32,
activeEvents: u32,
contactForceEventThreshold: f32,
contactSkin: f32,
hasParent: bool,
parent: FlatHandle,
bodies: &mut RawRigidBodySet,
) -> Option<FlatHandle> {
self.do_create_collider(
enabled,
shape,
translation,
rotation,
massPropsMode,
mass,
centerOfMass,
principalAngularInertia,
density,
friction,
restitution,
frictionCombineRule,
restitutionCombineRule,
isSensor,
collisionGroups,
solverGroups,
activeCollisionTypes,
activeHooks,
activeEvents,
contactForceEventThreshold,
contactSkin,
hasParent,
parent,
bodies,
)
}
#[cfg(feature = "dim3")]
pub fn createCollider(
&mut self,
enabled: bool,
shape: &RawShape,
translation: &RawVector,
rotation: &RawRotation,
massPropsMode: u32,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: &RawVector,
angularInertiaFrame: &RawRotation,
density: f32,
friction: f32,
restitution: f32,
frictionCombineRule: u32,
restitutionCombineRule: u32,
isSensor: bool,
collisionGroups: u32,
solverGroups: u32,
activeCollisionTypes: u16,
activeHooks: u32,
activeEvents: u32,
contactForceEventThreshold: f32,
contactSkin: f32,
hasParent: bool,
parent: FlatHandle,
bodies: &mut RawRigidBodySet,
) -> Option<FlatHandle> {
self.do_create_collider(
enabled,
shape,
translation,
rotation,
massPropsMode,
mass,
centerOfMass,
principalAngularInertia,
angularInertiaFrame,
density,
friction,
restitution,
frictionCombineRule,
restitutionCombineRule,
isSensor,
collisionGroups,
solverGroups,
activeCollisionTypes,
activeHooks,
activeEvents,
contactForceEventThreshold,
contactSkin,
hasParent,
parent,
bodies,
)
}
/// Removes a collider from this set and wake-up the rigid-body it is attached to.
pub fn remove(
&mut self,
handle: FlatHandle,
islands: &mut RawIslandManager,
bodies: &mut RawRigidBodySet,
wakeUp: bool,
) {
let handle = utils::collider_handle(handle);
self.0.remove(handle, &mut islands.0, &mut bodies.0, wakeUp);
}
/// Checks if a collider with the given integer handle exists.
pub fn isHandleValid(&self, handle: FlatHandle) -> bool {
self.0.get(utils::collider_handle(handle)).is_some()
}
/// Applies the given JavaScript function to the integer handle of each collider managed by this collider set.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each collider managed by this collider set. Called as `f(handle)`.
pub fn forEachColliderHandle(&self, f: &js_sys::Function) {
let this = JsValue::null();
for (handle, _) in self.0.iter() {
let _ = f.call1(&this, &JsValue::from(utils::flat_handle(handle.0)));
}
}
}

View File

@@ -0,0 +1,31 @@
use crate::math::RawVector;
use rapier::parry::query;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawShapeContact {
pub(crate) contact: query::Contact,
}
#[wasm_bindgen]
impl RawShapeContact {
pub fn distance(&self) -> f32 {
self.contact.dist
}
pub fn point1(&self) -> RawVector {
self.contact.point1.coords.into()
}
pub fn point2(&self) -> RawVector {
self.contact.point2.coords.into()
}
pub fn normal1(&self) -> RawVector {
self.contact.normal1.into_inner().into()
}
pub fn normal2(&self) -> RawVector {
self.contact.normal2.into_inner().into()
}
}

View File

@@ -0,0 +1,47 @@
use rapier::prelude::FeatureId;
use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(feature = "dim2")]
#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum RawFeatureType {
Vertex,
Face,
Unknown,
}
#[cfg(feature = "dim3")]
#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum RawFeatureType {
Vertex,
Edge,
Face,
Unknown,
}
pub trait IntoTypeValue {
fn into_type(self) -> RawFeatureType;
fn into_value(self) -> Option<u32>;
}
impl IntoTypeValue for FeatureId {
fn into_type(self) -> RawFeatureType {
match self {
FeatureId::Vertex(_) => RawFeatureType::Vertex,
#[cfg(feature = "dim3")]
FeatureId::Edge(_) => RawFeatureType::Edge,
FeatureId::Face(_) => RawFeatureType::Face,
_ => RawFeatureType::Unknown,
}
}
fn into_value(self) -> Option<u32> {
match self {
FeatureId::Vertex(id) | FeatureId::Face(id) => Some(id),
#[cfg(feature = "dim3")]
FeatureId::Edge(id) => Some(id),
_ => None,
}
}
}

View File

@@ -0,0 +1,49 @@
//! Structures related to geometry: colliders, shapes, etc.
pub use self::broad_phase::*;
pub use self::collider_set::*;
pub use self::contact::*;
pub use self::feature::*;
pub use self::narrow_phase::*;
pub use self::point::*;
pub use self::ray::*;
pub use self::shape::*;
pub use self::toi::*;
mod broad_phase;
mod collider;
mod collider_set;
mod contact;
mod feature;
mod narrow_phase;
mod point;
mod ray;
mod shape;
mod toi;
use rapier::dynamics::CoefficientCombineRule;
use rapier::geometry::InteractionGroups;
use rapier::prelude::Group;
pub const fn unpack_interaction_groups(memberships_filter: u32) -> InteractionGroups {
InteractionGroups::new(
Group::from_bits_retain((memberships_filter >> 16) as u32),
Group::from_bits_retain((memberships_filter & 0x0000_ffff) as u32),
)
}
pub const fn pack_interaction_groups(groups: InteractionGroups) -> u32 {
(groups.memberships.bits() << 16) | groups.filter.bits()
}
pub const fn combine_rule_from_u32(rule: u32) -> CoefficientCombineRule {
if rule == CoefficientCombineRule::Average as u32 {
CoefficientCombineRule::Average
} else if rule == CoefficientCombineRule::Min as u32 {
CoefficientCombineRule::Min
} else if rule == CoefficientCombineRule::Multiply as u32 {
CoefficientCombineRule::Multiply
} else {
CoefficientCombineRule::Max
}
}

View File

@@ -0,0 +1,217 @@
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::geometry::{ContactManifold, ContactPair, NarrowPhase};
use rapier::math::Real;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawNarrowPhase(pub(crate) NarrowPhase);
#[wasm_bindgen]
impl RawNarrowPhase {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawNarrowPhase(NarrowPhase::new())
}
pub fn contact_pairs_with(&self, handle1: FlatHandle, f: js_sys::Function) {
let this = JsValue::null();
let handle1 = utils::collider_handle(handle1);
for pair in self.0.contact_pairs_with(handle1) {
let handle2 = if pair.collider1 == handle1 {
utils::flat_handle(pair.collider2.0)
} else {
utils::flat_handle(pair.collider1.0)
};
let _ = f.call1(&this, &JsValue::from(handle2));
}
}
pub fn contact_pair(&self, handle1: FlatHandle, handle2: FlatHandle) -> Option<RawContactPair> {
let handle1 = utils::collider_handle(handle1);
let handle2 = utils::collider_handle(handle2);
self.0
.contact_pair(handle1, handle2)
.map(|p| RawContactPair(p as *const ContactPair))
}
pub fn intersection_pairs_with(&self, handle1: FlatHandle, f: js_sys::Function) {
let this = JsValue::null();
let handle1 = utils::collider_handle(handle1);
for (h1, h2, inter) in self.0.intersection_pairs_with(handle1) {
if inter {
let handle2 = if h1 == handle1 {
utils::flat_handle(h2.0)
} else {
utils::flat_handle(h1.0)
};
let _ = f.call1(&this, &JsValue::from(handle2));
}
}
}
pub fn intersection_pair(&self, handle1: FlatHandle, handle2: FlatHandle) -> bool {
let handle1 = utils::collider_handle(handle1);
let handle2 = utils::collider_handle(handle2);
self.0.intersection_pair(handle1, handle2) == Some(true)
}
}
#[wasm_bindgen]
pub struct RawContactPair(*const ContactPair);
#[wasm_bindgen]
pub struct RawContactManifold(*const ContactManifold);
// SAFETY: the use of a raw pointer is very unsafe.
// We need this because wasm-bindgen doesn't support
// lifetimes. So for the moment, we have to make sure
// that our TypeScript wrapper properly free the pair
// before the user has a chance to invalidate this pointer.
#[wasm_bindgen]
impl RawContactPair {
pub fn collider1(&self) -> FlatHandle {
unsafe { utils::flat_handle((*self.0).collider1.0) }
}
pub fn collider2(&self) -> FlatHandle {
unsafe { utils::flat_handle((*self.0).collider2.0) }
}
pub fn numContactManifolds(&self) -> usize {
unsafe { (*self.0).manifolds.len() }
}
pub fn contactManifold(&self, i: usize) -> Option<RawContactManifold> {
unsafe {
(&(*self.0).manifolds)
.get(i)
.map(|m| RawContactManifold(m as *const ContactManifold))
}
}
}
#[wasm_bindgen]
impl RawContactManifold {
pub fn normal(&self) -> RawVector {
unsafe { RawVector((*self.0).data.normal) }
}
// pub fn user_data(&self) -> u32 {
// unsafe { (*self.0).data.user_data }
// }
pub fn local_n1(&self) -> RawVector {
unsafe { (*self.0).local_n1.into() }
}
pub fn local_n2(&self) -> RawVector {
unsafe { (*self.0).local_n2.into() }
}
pub fn subshape1(&self) -> u32 {
unsafe { (*self.0).subshape1 }
}
pub fn subshape2(&self) -> u32 {
unsafe { (*self.0).subshape2 }
}
pub fn num_contacts(&self) -> usize {
unsafe { (*self.0).points.len() }
}
pub fn contact_local_p1(&self, i: usize) -> Option<RawVector> {
unsafe { (&(*self.0).points).get(i).map(|c| c.local_p1.coords.into()) }
}
pub fn contact_local_p2(&self, i: usize) -> Option<RawVector> {
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) }
}
pub fn contact_fid1(&self, i: usize) -> u32 {
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) }
}
pub fn contact_impulse(&self, i: usize) -> Real {
unsafe {
(&(*self.0).points)
.get(i)
.map(|c| c.data.impulse)
.unwrap_or(0.0)
}
}
#[cfg(feature = "dim2")]
pub fn contact_tangent_impulse(&self, i: usize) -> Real {
unsafe {
(&(*self.0).points)
.get(i)
.map(|c| c.data.tangent_impulse.x)
.unwrap_or(0.0)
}
}
#[cfg(feature = "dim3")]
pub fn contact_tangent_impulse_x(&self, i: usize) -> Real {
unsafe {
(&(*self.0).points)
.get(i)
.map(|c| c.data.tangent_impulse.x)
.unwrap_or(0.0)
}
}
#[cfg(feature = "dim3")]
pub fn contact_tangent_impulse_y(&self, i: usize) -> Real {
unsafe {
(&(*self.0).points)
.get(i)
.map(|c| c.data.tangent_impulse.y)
.unwrap_or(0.0)
}
}
pub fn num_solver_contacts(&self) -> usize {
unsafe { (*self.0).data.solver_contacts.len() }
}
pub fn solver_contact_point(&self, i: usize) -> Option<RawVector> {
unsafe {
(&(*self.0).data)
.solver_contacts
.get(i)
.map(|c| c.point.coords.into())
}
}
pub fn solver_contact_dist(&self, i: usize) -> Real {
unsafe {
(&(*self.0).data)
.solver_contacts
.get(i)
.map(|c| c.dist)
.unwrap_or(0.0)
}
}
pub fn solver_contact_friction(&self, i: usize) -> Real {
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 }
}
pub fn solver_contact_tangent_velocity(&self, i: usize) -> RawVector {
unsafe { (&(*self.0).data).solver_contacts[i].tangent_velocity.into() }
}
}

View File

@@ -0,0 +1,53 @@
use crate::geometry::feature::IntoTypeValue;
use crate::geometry::RawFeatureType;
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::{
geometry::{ColliderHandle, PointProjection},
prelude::FeatureId,
};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawPointProjection(pub(crate) PointProjection);
#[wasm_bindgen]
impl RawPointProjection {
pub fn point(&self) -> RawVector {
self.0.point.coords.into()
}
pub fn isInside(&self) -> bool {
self.0.is_inside
}
}
#[wasm_bindgen]
pub struct RawPointColliderProjection {
pub(crate) handle: ColliderHandle,
pub(crate) proj: PointProjection,
pub(crate) feature: FeatureId,
}
#[wasm_bindgen]
impl RawPointColliderProjection {
pub fn colliderHandle(&self) -> FlatHandle {
utils::flat_handle(self.handle.0)
}
pub fn point(&self) -> RawVector {
self.proj.point.coords.into()
}
pub fn isInside(&self) -> bool {
self.proj.is_inside
}
pub fn featureType(&self) -> RawFeatureType {
self.feature.into_type()
}
pub fn featureId(&self) -> Option<u32> {
self.feature.into_value()
}
}

View File

@@ -0,0 +1,74 @@
use crate::geometry::feature::IntoTypeValue;
use crate::geometry::RawFeatureType;
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::geometry::{ColliderHandle, RayIntersection};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawRayIntersection(pub(crate) RayIntersection);
#[wasm_bindgen]
impl RawRayIntersection {
pub fn normal(&self) -> RawVector {
self.0.normal.into()
}
pub fn time_of_impact(&self) -> f32 {
self.0.time_of_impact
}
pub fn featureType(&self) -> RawFeatureType {
self.0.feature.into_type()
}
pub fn featureId(&self) -> Option<u32> {
self.0.feature.into_value()
}
}
#[wasm_bindgen]
pub struct RawRayColliderIntersection {
pub(crate) handle: ColliderHandle,
pub(crate) inter: RayIntersection,
}
#[wasm_bindgen]
impl RawRayColliderIntersection {
pub fn colliderHandle(&self) -> FlatHandle {
utils::flat_handle(self.handle.0)
}
pub fn normal(&self) -> RawVector {
self.inter.normal.into()
}
pub fn time_of_impact(&self) -> f32 {
self.inter.time_of_impact
}
pub fn featureType(&self) -> RawFeatureType {
self.inter.feature.into_type()
}
pub fn featureId(&self) -> Option<u32> {
self.inter.feature.into_value()
}
}
#[wasm_bindgen]
pub struct RawRayColliderHit {
pub(crate) handle: ColliderHandle,
pub(crate) timeOfImpact: f32,
}
#[wasm_bindgen]
impl RawRayColliderHit {
pub fn colliderHandle(&self) -> FlatHandle {
utils::flat_handle(self.handle.0)
}
pub fn timeOfImpact(&self) -> f32 {
self.timeOfImpact
}
}

View File

@@ -0,0 +1,527 @@
use crate::geometry::{RawPointProjection, RawRayIntersection, RawShapeCastHit, RawShapeContact};
use crate::math::{RawRotation, RawVector};
#[cfg(feature = "dim3")]
use na::DMatrix;
#[cfg(feature = "dim2")]
use na::DVector;
use na::Unit;
use rapier::geometry::{Shape, SharedShape, TriMeshFlags};
use rapier::math::{Isometry, Point, Real, Vector, DIM};
use rapier::parry::query;
use rapier::parry::query::{Ray, ShapeCastOptions};
use wasm_bindgen::prelude::*;
pub trait SharedShapeUtility {
fn castShape(
&self,
shapePos1: &Isometry<Real>,
shapeVel1: &Vector<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
shapeVel2: &Vector<Real>,
target_distance: f32,
maxToi: f32,
stop_at_penetration: bool,
) -> Option<RawShapeCastHit>;
fn intersectsShape(
&self,
shapePos1: &Isometry<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
) -> bool;
fn contactShape(
&self,
shapePos1: &Isometry<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
prediction: f32,
) -> Option<RawShapeContact>;
fn containsPoint(&self, shapePos: &Isometry<Real>, point: &Point<Real>) -> bool;
fn projectPoint(
&self,
shapePos: &Isometry<Real>,
point: &Point<Real>,
solid: bool,
) -> RawPointProjection;
fn intersectsRay(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
) -> bool;
fn castRay(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
solid: bool,
) -> f32;
fn castRayAndGetNormal(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
solid: bool,
) -> Option<RawRayIntersection>;
}
// for RawShape & Collider
impl SharedShapeUtility for SharedShape {
fn castShape(
&self,
shapePos1: &Isometry<Real>,
shapeVel1: &Vector<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
shapeVel2: &Vector<Real>,
target_distance: f32,
maxToi: f32,
stop_at_penetration: bool,
) -> Option<RawShapeCastHit> {
query::cast_shapes(
shapePos1,
shapeVel1,
&*self.0,
shapePos2,
&shapeVel2,
shape2,
ShapeCastOptions {
max_time_of_impact: maxToi,
target_distance,
stop_at_penetration,
compute_impact_geometry_on_penetration: true,
},
)
.ok()
.flatten()
.map(|hit| RawShapeCastHit { hit })
}
fn intersectsShape(
&self,
shapePos1: &Isometry<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
) -> bool {
query::intersection_test(shapePos1, &*self.0, shapePos2, shape2).unwrap_or(false)
}
fn contactShape(
&self,
shapePos1: &Isometry<Real>,
shape2: &dyn Shape,
shapePos2: &Isometry<Real>,
prediction: f32,
) -> Option<RawShapeContact> {
query::contact(shapePos1, &*self.0, shapePos2, shape2, prediction)
.ok()
.flatten()
.map(|contact| RawShapeContact { contact })
}
fn containsPoint(&self, shapePos: &Isometry<Real>, point: &Point<Real>) -> bool {
self.as_ref().contains_point(shapePos, point)
}
fn projectPoint(
&self,
shapePos: &Isometry<Real>,
point: &Point<Real>,
solid: bool,
) -> RawPointProjection {
RawPointProjection(self.as_ref().project_point(shapePos, point, solid))
}
fn intersectsRay(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
) -> bool {
self.as_ref()
.intersects_ray(shapePos, &Ray::new(rayOrig, rayDir), maxToi)
}
fn castRay(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
solid: bool,
) -> f32 {
self.as_ref()
.cast_ray(shapePos, &Ray::new(rayOrig, rayDir), maxToi, solid)
.unwrap_or(-1.0) // Negative value = no hit.
}
fn castRayAndGetNormal(
&self,
shapePos: &Isometry<Real>,
rayOrig: Point<Real>,
rayDir: Vector<Real>,
maxToi: f32,
solid: bool,
) -> Option<RawRayIntersection> {
self.as_ref()
.cast_ray_and_get_normal(shapePos, &Ray::new(rayOrig, rayDir), maxToi, solid)
.map(|inter| RawRayIntersection(inter))
}
}
#[wasm_bindgen]
#[cfg(feature = "dim2")]
pub enum RawShapeType {
Ball = 0,
Cuboid = 1,
Capsule = 2,
Segment = 3,
Polyline = 4,
Triangle = 5,
TriMesh = 6,
HeightField = 7,
Compound = 8,
ConvexPolygon = 9,
RoundCuboid = 10,
RoundTriangle = 11,
RoundConvexPolygon = 12,
HalfSpace = 13,
Voxels = 14,
}
#[wasm_bindgen]
#[cfg(feature = "dim3")]
pub enum RawShapeType {
Ball = 0,
Cuboid = 1,
Capsule = 2,
Segment = 3,
Polyline = 4,
Triangle = 5,
TriMesh = 6,
HeightField = 7,
Compound = 8,
ConvexPolyhedron = 9,
Cylinder = 10,
Cone = 11,
RoundCuboid = 12,
RoundTriangle = 13,
RoundCylinder = 14,
RoundCone = 15,
RoundConvexPolyhedron = 16,
HalfSpace = 17,
Voxels = 18,
}
#[wasm_bindgen]
pub struct RawShape(pub(crate) SharedShape);
#[wasm_bindgen]
impl RawShape {
#[cfg(feature = "dim2")]
pub fn cuboid(hx: f32, hy: f32) -> Self {
Self(SharedShape::cuboid(hx, hy))
}
#[cfg(feature = "dim3")]
pub fn cuboid(hx: f32, hy: f32, hz: f32) -> Self {
Self(SharedShape::cuboid(hx, hy, hz))
}
#[cfg(feature = "dim2")]
pub fn roundCuboid(hx: f32, hy: f32, borderRadius: f32) -> Self {
Self(SharedShape::round_cuboid(hx, hy, borderRadius))
}
#[cfg(feature = "dim3")]
pub fn roundCuboid(hx: f32, hy: f32, hz: f32, borderRadius: f32) -> Self {
Self(SharedShape::round_cuboid(hx, hy, hz, borderRadius))
}
pub fn ball(radius: f32) -> Self {
Self(SharedShape::ball(radius))
}
pub fn halfspace(normal: &RawVector) -> Self {
Self(SharedShape::halfspace(Unit::new_normalize(normal.0)))
}
pub fn capsule(halfHeight: f32, radius: f32) -> Self {
let p2 = Point::from(Vector::y() * halfHeight);
let p1 = -p2;
Self(SharedShape::capsule(p1, p2, radius))
}
#[cfg(feature = "dim3")]
pub fn cylinder(halfHeight: f32, radius: f32) -> Self {
Self(SharedShape::cylinder(halfHeight, radius))
}
#[cfg(feature = "dim3")]
pub fn roundCylinder(halfHeight: f32, radius: f32, borderRadius: f32) -> Self {
Self(SharedShape::round_cylinder(
halfHeight,
radius,
borderRadius,
))
}
#[cfg(feature = "dim3")]
pub fn cone(halfHeight: f32, radius: f32) -> Self {
Self(SharedShape::cone(halfHeight, radius))
}
#[cfg(feature = "dim3")]
pub fn roundCone(halfHeight: f32, radius: f32, borderRadius: f32) -> Self {
Self(SharedShape::round_cone(halfHeight, radius, borderRadius))
}
pub fn voxels(voxel_size: &RawVector, grid_coords: Vec<i32>) -> Self {
let grid_coords: Vec<_> = grid_coords
.chunks_exact(DIM)
.map(Point::from_slice)
.collect();
Self(SharedShape::voxels(voxel_size.0, &grid_coords))
}
pub fn voxelsFromPoints(voxel_size: &RawVector, points: Vec<f32>) -> Self {
let points: Vec<_> = points.chunks_exact(DIM).map(Point::from_slice).collect();
Self(SharedShape::voxels_from_points(voxel_size.0, &points))
}
pub fn polyline(vertices: Vec<f32>, indices: Vec<u32>) -> Self {
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
let indices: Vec<_> = indices.chunks(2).map(|v| [v[0], v[1]]).collect();
if indices.is_empty() {
Self(SharedShape::polyline(vertices, None))
} else {
Self(SharedShape::polyline(vertices, Some(indices)))
}
}
pub fn trimesh(vertices: Vec<f32>, indices: Vec<u32>, flags: u32) -> Option<RawShape> {
let flags = TriMeshFlags::from_bits(flags as u16).unwrap_or_default();
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
let indices = indices.chunks(3).map(|v| [v[0], v[1], v[2]]).collect();
SharedShape::trimesh_with_flags(vertices, indices, flags)
.ok()
.map(Self)
}
#[cfg(feature = "dim2")]
pub fn heightfield(heights: Vec<f32>, scale: &RawVector) -> Self {
let heights = DVector::from_vec(heights);
Self(SharedShape::heightfield(heights, scale.0))
}
#[cfg(feature = "dim3")]
pub fn heightfield(
nrows: u32,
ncols: u32,
heights: Vec<f32>,
scale: &RawVector,
flags: u32,
) -> Self {
let flags =
rapier::parry::shape::HeightFieldFlags::from_bits(flags as u8).unwrap_or_default();
let heights = DMatrix::from_vec(nrows as usize + 1, ncols as usize + 1, heights);
Self(SharedShape::heightfield_with_flags(heights, scale.0, flags))
}
pub fn segment(p1: &RawVector, p2: &RawVector) -> Self {
Self(SharedShape::segment(p1.0.into(), p2.0.into()))
}
pub fn triangle(p1: &RawVector, p2: &RawVector, p3: &RawVector) -> Self {
Self(SharedShape::triangle(p1.0.into(), p2.0.into(), p3.0.into()))
}
pub fn roundTriangle(
p1: &RawVector,
p2: &RawVector,
p3: &RawVector,
borderRadius: f32,
) -> Self {
Self(SharedShape::round_triangle(
p1.0.into(),
p2.0.into(),
p3.0.into(),
borderRadius,
))
}
pub fn convexHull(points: Vec<f32>) -> Option<RawShape> {
let points: Vec<_> = points.chunks(DIM).map(|v| Point::from_slice(v)).collect();
SharedShape::convex_hull(&points).map(|s| Self(s))
}
pub fn roundConvexHull(points: Vec<f32>, borderRadius: f32) -> Option<RawShape> {
let points: Vec<_> = points.chunks(DIM).map(|v| Point::from_slice(v)).collect();
SharedShape::round_convex_hull(&points, borderRadius).map(|s| Self(s))
}
#[cfg(feature = "dim2")]
pub fn convexPolyline(vertices: Vec<f32>) -> Option<RawShape> {
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
SharedShape::convex_polyline(vertices).map(|s| Self(s))
}
#[cfg(feature = "dim2")]
pub fn roundConvexPolyline(vertices: Vec<f32>, borderRadius: f32) -> Option<RawShape> {
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
SharedShape::round_convex_polyline(vertices, borderRadius).map(|s| Self(s))
}
#[cfg(feature = "dim3")]
pub fn convexMesh(vertices: Vec<f32>, indices: Vec<u32>) -> Option<RawShape> {
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
let indices: Vec<_> = indices.chunks(3).map(|v| [v[0], v[1], v[2]]).collect();
SharedShape::convex_mesh(vertices, &indices).map(|s| Self(s))
}
#[cfg(feature = "dim3")]
pub fn roundConvexMesh(
vertices: Vec<f32>,
indices: Vec<u32>,
borderRadius: f32,
) -> Option<RawShape> {
let vertices = vertices.chunks(DIM).map(|v| Point::from_slice(v)).collect();
let indices: Vec<_> = indices.chunks(3).map(|v| [v[0], v[1], v[2]]).collect();
SharedShape::round_convex_mesh(vertices, &indices, borderRadius).map(|s| Self(s))
}
pub fn castShape(
&self,
shapePos1: &RawVector,
shapeRot1: &RawRotation,
shapeVel1: &RawVector,
shape2: &RawShape,
shapePos2: &RawVector,
shapeRot2: &RawRotation,
shapeVel2: &RawVector,
target_distance: f32,
maxToi: f32,
stop_at_penetration: bool,
) -> Option<RawShapeCastHit> {
let pos1 = Isometry::from_parts(shapePos1.0.into(), shapeRot1.0);
let pos2 = Isometry::from_parts(shapePos2.0.into(), shapeRot2.0);
self.0.castShape(
&pos1,
&shapeVel1.0,
&*shape2.0,
&pos2,
&shapeVel2.0,
target_distance,
maxToi,
stop_at_penetration,
)
}
pub fn intersectsShape(
&self,
shapePos1: &RawVector,
shapeRot1: &RawRotation,
shape2: &RawShape,
shapePos2: &RawVector,
shapeRot2: &RawRotation,
) -> bool {
let pos1 = Isometry::from_parts(shapePos1.0.into(), shapeRot1.0);
let pos2 = Isometry::from_parts(shapePos2.0.into(), shapeRot2.0);
self.0.intersectsShape(&pos1, &*shape2.0, &pos2)
}
pub fn contactShape(
&self,
shapePos1: &RawVector,
shapeRot1: &RawRotation,
shape2: &RawShape,
shapePos2: &RawVector,
shapeRot2: &RawRotation,
prediction: f32,
) -> Option<RawShapeContact> {
let pos1 = Isometry::from_parts(shapePos1.0.into(), shapeRot1.0);
let pos2 = Isometry::from_parts(shapePos2.0.into(), shapeRot2.0);
self.0.contactShape(&pos1, &*shape2.0, &pos2, prediction)
}
pub fn containsPoint(
&self,
shapePos: &RawVector,
shapeRot: &RawRotation,
point: &RawVector,
) -> bool {
let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0);
self.0.containsPoint(&pos, &point.0.into())
}
pub fn projectPoint(
&self,
shapePos: &RawVector,
shapeRot: &RawRotation,
point: &RawVector,
solid: bool,
) -> RawPointProjection {
let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0);
self.0.projectPoint(&pos, &point.0.into(), solid)
}
pub fn intersectsRay(
&self,
shapePos: &RawVector,
shapeRot: &RawRotation,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
) -> bool {
let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0);
self.0
.intersectsRay(&pos, rayOrig.0.into(), rayDir.0.into(), maxToi)
}
pub fn castRay(
&self,
shapePos: &RawVector,
shapeRot: &RawRotation,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
solid: bool,
) -> f32 {
let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0);
self.0
.castRay(&pos, rayOrig.0.into(), rayDir.0.into(), maxToi, solid)
}
pub fn castRayAndGetNormal(
&self,
shapePos: &RawVector,
shapeRot: &RawRotation,
rayOrig: &RawVector,
rayDir: &RawVector,
maxToi: f32,
solid: bool,
) -> Option<RawRayIntersection> {
let pos = Isometry::from_parts(shapePos.0.into(), shapeRot.0);
self.0
.castRayAndGetNormal(&pos, rayOrig.0.into(), rayDir.0.into(), maxToi, solid)
}
}

View File

@@ -0,0 +1,65 @@
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::geometry::{ColliderHandle, ShapeCastHit};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawShapeCastHit {
pub(crate) hit: ShapeCastHit,
}
#[wasm_bindgen]
impl RawShapeCastHit {
pub fn time_of_impact(&self) -> f32 {
self.hit.time_of_impact
}
pub fn witness1(&self) -> RawVector {
self.hit.witness1.coords.into()
}
pub fn witness2(&self) -> RawVector {
self.hit.witness2.coords.into()
}
pub fn normal1(&self) -> RawVector {
self.hit.normal1.into_inner().into()
}
pub fn normal2(&self) -> RawVector {
self.hit.normal2.into_inner().into()
}
}
#[wasm_bindgen]
pub struct RawColliderShapeCastHit {
pub(crate) handle: ColliderHandle,
pub(crate) hit: ShapeCastHit,
}
#[wasm_bindgen]
impl RawColliderShapeCastHit {
pub fn colliderHandle(&self) -> FlatHandle {
utils::flat_handle(self.handle.0)
}
pub fn time_of_impact(&self) -> f32 {
self.hit.time_of_impact
}
pub fn witness1(&self) -> RawVector {
self.hit.witness1.coords.into()
}
pub fn witness2(&self) -> RawVector {
self.hit.witness2.coords.into()
}
pub fn normal1(&self) -> RawVector {
self.hit.normal1.into_inner().into()
}
pub fn normal2(&self) -> RawVector {
self.hit.normal2.into_inner().into()
}
}