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,290 @@
use crate::dynamics::RawRigidBodySet;
use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase};
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use na::{Isometry, Unit};
use rapier::control::{
CharacterAutostep, CharacterCollision, CharacterLength, EffectiveCharacterMovement,
KinematicCharacterController,
};
use rapier::geometry::{ColliderHandle, ShapeCastHit};
use rapier::math::{Point, Real, Vector};
use rapier::parry::query::ShapeCastStatus;
use rapier::pipeline::{QueryFilter, QueryFilterFlags};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawKinematicCharacterController {
controller: KinematicCharacterController,
result: EffectiveCharacterMovement,
events: Vec<CharacterCollision>,
}
fn length_value(length: CharacterLength) -> Real {
match length {
CharacterLength::Absolute(val) => val,
CharacterLength::Relative(val) => val,
}
}
#[wasm_bindgen]
impl RawKinematicCharacterController {
#[wasm_bindgen(constructor)]
pub fn new(offset: Real) -> Self {
let controller = KinematicCharacterController {
offset: CharacterLength::Absolute(offset),
autostep: None,
snap_to_ground: None,
..KinematicCharacterController::default()
};
Self {
controller,
result: EffectiveCharacterMovement {
translation: Vector::zeros(),
grounded: false,
is_sliding_down_slope: false,
},
events: vec![],
}
}
pub fn up(&self) -> RawVector {
self.controller.up.into_inner().into()
}
pub fn setUp(&mut self, vector: &RawVector) {
self.controller.up = Unit::new_normalize(vector.0);
}
pub fn normalNudgeFactor(&self) -> Real {
self.controller.normal_nudge_factor
}
pub fn setNormalNudgeFactor(&mut self, value: Real) {
self.controller.normal_nudge_factor = value;
}
pub fn offset(&self) -> Real {
length_value(self.controller.offset)
}
pub fn setOffset(&mut self, value: Real) {
self.controller.offset = CharacterLength::Absolute(value);
}
pub fn slideEnabled(&self) -> bool {
self.controller.slide
}
pub fn setSlideEnabled(&mut self, enabled: bool) {
self.controller.slide = enabled
}
pub fn autostepMaxHeight(&self) -> Option<Real> {
self.controller.autostep.map(|e| length_value(e.max_height))
}
pub fn autostepMinWidth(&self) -> Option<Real> {
self.controller.autostep.map(|e| length_value(e.min_width))
}
pub fn autostepIncludesDynamicBodies(&self) -> Option<bool> {
self.controller.autostep.map(|e| e.include_dynamic_bodies)
}
pub fn autostepEnabled(&self) -> bool {
self.controller.autostep.is_some()
}
pub fn enableAutostep(&mut self, maxHeight: Real, minWidth: Real, includeDynamicBodies: bool) {
self.controller.autostep = Some(CharacterAutostep {
min_width: CharacterLength::Absolute(minWidth),
max_height: CharacterLength::Absolute(maxHeight),
include_dynamic_bodies: includeDynamicBodies,
})
}
pub fn disableAutostep(&mut self) {
self.controller.autostep = None;
}
pub fn maxSlopeClimbAngle(&self) -> Real {
self.controller.max_slope_climb_angle
}
pub fn setMaxSlopeClimbAngle(&mut self, angle: Real) {
self.controller.max_slope_climb_angle = angle;
}
pub fn minSlopeSlideAngle(&self) -> Real {
self.controller.min_slope_slide_angle
}
pub fn setMinSlopeSlideAngle(&mut self, angle: Real) {
self.controller.min_slope_slide_angle = angle
}
pub fn snapToGroundDistance(&self) -> Option<Real> {
self.controller.snap_to_ground.map(length_value)
}
pub fn enableSnapToGround(&mut self, distance: Real) {
self.controller.snap_to_ground = Some(CharacterLength::Absolute(distance));
}
pub fn disableSnapToGround(&mut self) {
self.controller.snap_to_ground = None;
}
pub fn snapToGroundEnabled(&self) -> bool {
self.controller.snap_to_ground.is_some()
}
pub fn computeColliderMovement(
&mut self,
dt: Real,
broad_phase: &RawBroadPhase,
narrow_phase: &RawNarrowPhase,
bodies: &mut RawRigidBodySet,
colliders: &mut RawColliderSet,
collider_handle: FlatHandle,
desired_translation_delta: &RawVector,
apply_impulses_to_dynamic_bodies: bool,
character_mass: Option<Real>,
filter_flags: u32,
filter_groups: Option<u32>,
filter_predicate: &js_sys::Function,
) {
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,
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 mut 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,
&query_pipeline.as_ref(),
&*collider_shape,
&collider_pose,
desired_translation_delta.0,
|event| events.push(event),
);
if apply_impulses_to_dynamic_bodies {
self.controller.solve_character_collision_impulses(
dt,
&mut query_pipeline,
&*collider_shape,
character_mass,
self.events.iter(),
);
}
});
} else {
self.result.translation.fill(0.0);
}
}
pub fn computedMovement(&self) -> RawVector {
self.result.translation.into()
}
pub fn computedGrounded(&self) -> bool {
self.result.grounded
}
pub fn numComputedCollisions(&self) -> usize {
self.events.len()
}
pub fn computedCollision(&self, i: usize, collision: &mut RawCharacterCollision) -> bool {
if let Some(coll) = self.events.get(i) {
collision.0 = *coll;
}
i < self.events.len()
}
}
#[wasm_bindgen]
pub struct RawCharacterCollision(CharacterCollision);
#[wasm_bindgen]
impl RawCharacterCollision {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self(CharacterCollision {
handle: ColliderHandle::invalid(),
character_pos: Isometry::identity(),
translation_applied: Vector::zeros(),
translation_remaining: Vector::zeros(),
hit: ShapeCastHit {
time_of_impact: 0.0,
witness1: Point::origin(),
witness2: Point::origin(),
normal1: Vector::y_axis(),
normal2: Vector::y_axis(),
status: ShapeCastStatus::Failed,
},
})
}
pub fn handle(&self) -> FlatHandle {
utils::flat_handle(self.0.handle.0)
}
pub fn translationDeltaApplied(&self) -> RawVector {
self.0.translation_applied.into()
}
pub fn translationDeltaRemaining(&self) -> RawVector {
self.0.translation_remaining.into()
}
pub fn toi(&self) -> Real {
self.0.hit.time_of_impact
}
pub fn worldWitness1(&self) -> RawVector {
self.0.hit.witness1.coords.into() // Already in world-space.
}
pub fn worldWitness2(&self) -> RawVector {
(self.0.character_pos * self.0.hit.witness2).coords.into()
}
pub fn worldNormal1(&self) -> RawVector {
self.0.hit.normal1.into_inner().into() // Already in world-space.
}
pub fn worldNormal2(&self) -> RawVector {
(self.0.character_pos * self.0.hit.normal2.into_inner()).into()
}
}

11
thirdparty/rapier.js/src/control/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
pub use self::character_controller::RawKinematicCharacterController;
pub use self::pid_controller::RawPidController;
#[cfg(feature = "dim3")]
pub use self::ray_cast_vehicle_controller::RawDynamicRayCastVehicleController;
mod character_controller;
mod pid_controller;
#[cfg(feature = "dim3")]
mod ray_cast_vehicle_controller;

View File

@@ -0,0 +1,264 @@
use crate::dynamics::RawRigidBodySet;
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::control::PidController;
use rapier::dynamics::AxesMask;
use rapier::math::Vector;
use wasm_bindgen::prelude::*;
#[cfg(feature = "dim3")]
use crate::math::RawRotation;
#[cfg(feature = "dim2")]
use rapier::math::Rotation;
#[wasm_bindgen]
pub struct RawPidController {
controller: PidController,
}
#[wasm_bindgen]
impl RawPidController {
#[wasm_bindgen(constructor)]
pub fn new(kp: f32, ki: f32, kd: f32, axes_mask: u8) -> Self {
let controller = PidController::new(
kp,
ki,
kd,
AxesMask::from_bits(axes_mask).unwrap_or(AxesMask::all()),
);
Self { controller }
}
pub fn set_kp(&mut self, kp: f32, axes: u8) {
let axes = AxesMask::from_bits(axes).unwrap_or(AxesMask::all());
if axes.contains(AxesMask::LIN_X) {
self.controller.pd.lin_kp.x = kp;
}
if axes.contains(AxesMask::LIN_Y) {
self.controller.pd.lin_kp.y = kp;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::LIN_Z) {
self.controller.pd.lin_kp.z = kp;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_X) {
self.controller.pd.ang_kp.x = kp;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_Y) {
self.controller.pd.ang_kp.y = kp;
}
if axes.contains(AxesMask::ANG_Z) {
#[cfg(feature = "dim2")]
{
self.controller.pd.ang_kp = kp;
}
#[cfg(feature = "dim3")]
{
self.controller.pd.ang_kp.z = kp;
}
}
}
pub fn set_ki(&mut self, ki: f32, axes: u8) {
let axes = AxesMask::from_bits(axes).unwrap_or(AxesMask::all());
if axes.contains(AxesMask::LIN_X) {
self.controller.lin_ki.x = ki;
}
if axes.contains(AxesMask::LIN_Y) {
self.controller.lin_ki.y = ki;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::LIN_Z) {
self.controller.lin_ki.z = ki;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_X) {
self.controller.ang_ki.x = ki;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_Y) {
self.controller.ang_ki.y = ki;
}
if axes.contains(AxesMask::ANG_Z) {
#[cfg(feature = "dim2")]
{
self.controller.ang_ki = ki;
}
#[cfg(feature = "dim3")]
{
self.controller.ang_ki.z = ki;
}
}
}
pub fn set_kd(&mut self, kd: f32, axes: u8) {
let axes = AxesMask::from_bits(axes).unwrap_or(AxesMask::all());
if axes.contains(AxesMask::LIN_X) {
self.controller.pd.lin_kd.x = kd;
}
if axes.contains(AxesMask::LIN_Y) {
self.controller.pd.lin_kd.x = kd;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::LIN_Z) {
self.controller.pd.lin_kd.x = kd;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_X) {
self.controller.pd.ang_kd.x = kd;
}
#[cfg(feature = "dim3")]
if axes.contains(AxesMask::ANG_Y) {
self.controller.pd.ang_kd.y = kd;
}
if axes.contains(AxesMask::ANG_Z) {
#[cfg(feature = "dim2")]
{
self.controller.pd.ang_kd = kd;
}
#[cfg(feature = "dim3")]
{
self.controller.pd.ang_kd.z = kd;
}
}
}
pub fn set_axes_mask(&mut self, axes_mask: u8) {
if let Some(mask) = AxesMask::from_bits(axes_mask) {
self.controller.pd.axes = mask;
}
}
pub fn reset_integrals(&mut self) {
self.controller.reset_integrals();
}
pub fn apply_linear_correction(
&mut self,
dt: f32,
bodies: &mut RawRigidBodySet,
rb_handle: FlatHandle,
target_translation: &RawVector,
target_linvel: &RawVector,
) {
let rb_handle = utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get_mut(rb_handle) else {
return;
};
let correction = self.controller.linear_rigid_body_correction(
dt,
rb,
target_translation.0.into(),
target_linvel.0,
);
rb.set_linvel(*rb.linvel() + correction, true);
}
#[cfg(feature = "dim2")]
pub fn apply_angular_correction(
&mut self,
dt: f32,
bodies: &mut RawRigidBodySet,
rb_handle: FlatHandle,
target_rotation: f32,
target_angvel: f32,
) {
let rb_handle = crate::utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get_mut(rb_handle) else {
return;
};
let correction = self.controller.angular_rigid_body_correction(
dt,
rb,
Rotation::new(target_rotation),
target_angvel,
);
rb.set_angvel(rb.angvel() + correction, true);
}
#[cfg(feature = "dim3")]
pub fn apply_angular_correction(
&mut self,
dt: f32,
bodies: &mut RawRigidBodySet,
rb_handle: FlatHandle,
target_rotation: &RawRotation,
target_angvel: &RawVector,
) {
let rb_handle = crate::utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get_mut(rb_handle) else {
return;
};
let correction = self.controller.angular_rigid_body_correction(
dt,
rb,
target_rotation.0,
target_angvel.0,
);
rb.set_angvel(rb.angvel() + correction, true);
}
pub fn linear_correction(
&mut self,
dt: f32,
bodies: &RawRigidBodySet,
rb_handle: FlatHandle,
target_translation: &RawVector,
target_linvel: &RawVector,
) -> RawVector {
let rb_handle = crate::utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get(rb_handle) else {
return RawVector(Vector::zeros());
};
self.controller
.linear_rigid_body_correction(dt, rb, target_translation.0.into(), target_linvel.0)
.into()
}
#[cfg(feature = "dim2")]
pub fn angular_correction(
&mut self,
dt: f32,
bodies: &RawRigidBodySet,
rb_handle: FlatHandle,
target_rotation: f32,
target_angvel: f32,
) -> f32 {
let rb_handle = crate::utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get(rb_handle) else {
return 0.0;
};
self.controller.angular_rigid_body_correction(
dt,
rb,
Rotation::new(target_rotation),
target_angvel,
)
}
#[cfg(feature = "dim3")]
pub fn angular_correction(
&mut self,
dt: f32,
bodies: &RawRigidBodySet,
rb_handle: FlatHandle,
target_rotation: &RawRotation,
target_angvel: &RawVector,
) -> RawVector {
let rb_handle = crate::utils::body_handle(rb_handle);
let Some(rb) = bodies.0.get(rb_handle) else {
return RawVector(Vector::zeros());
};
self.controller
.angular_rigid_body_correction(dt, rb, target_rotation.0, target_angvel.0)
.into()
}
}

View File

@@ -0,0 +1,336 @@
use crate::dynamics::RawRigidBodySet;
use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase};
use crate::math::RawVector;
use crate::utils::{self, FlatHandle};
use rapier::control::{DynamicRayCastVehicleController, WheelTuning};
use rapier::math::Real;
use rapier::pipeline::{QueryFilter, QueryFilterFlags};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawDynamicRayCastVehicleController {
controller: DynamicRayCastVehicleController,
}
#[wasm_bindgen]
impl RawDynamicRayCastVehicleController {
#[wasm_bindgen(constructor)]
pub fn new(chassis: FlatHandle) -> Self {
Self {
controller: DynamicRayCastVehicleController::new(utils::body_handle(chassis)),
}
}
pub fn current_vehicle_speed(&self) -> Real {
self.controller.current_vehicle_speed
}
pub fn chassis(&self) -> FlatHandle {
utils::flat_handle(self.controller.chassis.0)
}
pub fn index_up_axis(&self) -> usize {
self.controller.index_up_axis
}
pub fn set_index_up_axis(&mut self, axis: usize) {
self.controller.index_up_axis = axis;
}
pub fn index_forward_axis(&self) -> usize {
self.controller.index_forward_axis
}
pub fn set_index_forward_axis(&mut self, axis: usize) {
self.controller.index_forward_axis = axis;
}
pub fn add_wheel(
&mut self,
chassis_connection_cs: &RawVector,
direction_cs: &RawVector,
axle_cs: &RawVector,
suspension_rest_length: Real,
radius: Real,
) {
self.controller.add_wheel(
chassis_connection_cs.0.into(),
direction_cs.0,
axle_cs.0,
suspension_rest_length,
radius,
&WheelTuning::default(),
);
}
pub fn num_wheels(&self) -> usize {
self.controller.wheels().len()
}
pub fn update_vehicle(
&mut self,
dt: Real,
broad_phase: &RawBroadPhase,
narrow_phase: &RawNarrowPhase,
bodies: &mut RawRigidBodySet,
colliders: &mut RawColliderSet,
filter_flags: u32,
filter_groups: Option<u32>,
filter_predicate: &js_sys::Function,
) {
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),
predicate,
exclude_rigid_body: Some(self.controller.chassis),
exclude_collider: None,
};
let query_pipeline = broad_phase.0.as_query_pipeline_mut(
narrow_phase.0.query_dispatcher(),
&mut bodies.0,
&mut colliders.0,
query_filter,
);
self.controller.update_vehicle(dt, query_pipeline);
});
}
/*
*
* Access to wheel properties.
*
*/
/*
* Getters + setters
*/
pub fn wheel_chassis_connection_point_cs(&self, i: usize) -> Option<RawVector> {
self.controller
.wheels()
.get(i)
.map(|w| w.chassis_connection_point_cs.into())
}
pub fn set_wheel_chassis_connection_point_cs(&mut self, i: usize, value: &RawVector) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.chassis_connection_point_cs = value.0.into();
}
}
pub fn wheel_suspension_rest_length(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.suspension_rest_length)
}
pub fn set_wheel_suspension_rest_length(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.suspension_rest_length = value;
}
}
pub fn wheel_max_suspension_travel(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.max_suspension_travel)
}
pub fn set_wheel_max_suspension_travel(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.max_suspension_travel = value;
}
}
pub fn wheel_radius(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.radius)
}
pub fn set_wheel_radius(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.radius = value;
}
}
pub fn wheel_suspension_stiffness(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.suspension_stiffness)
}
pub fn set_wheel_suspension_stiffness(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.suspension_stiffness = value;
}
}
pub fn wheel_suspension_compression(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.damping_compression)
}
pub fn set_wheel_suspension_compression(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.damping_compression = value;
}
}
pub fn wheel_suspension_relaxation(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.damping_relaxation)
}
pub fn set_wheel_suspension_relaxation(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.damping_relaxation = value;
}
}
pub fn wheel_max_suspension_force(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.max_suspension_force)
}
pub fn set_wheel_max_suspension_force(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.max_suspension_force = value;
}
}
pub fn wheel_brake(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.brake)
}
pub fn set_wheel_brake(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.brake = value;
}
}
pub fn wheel_steering(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.steering)
}
pub fn set_wheel_steering(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.steering = value;
}
}
pub fn wheel_engine_force(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.engine_force)
}
pub fn set_wheel_engine_force(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.engine_force = value;
}
}
pub fn wheel_direction_cs(&self, i: usize) -> Option<RawVector> {
self.controller
.wheels()
.get(i)
.map(|w| w.direction_cs.into())
}
pub fn set_wheel_direction_cs(&mut self, i: usize, value: &RawVector) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.direction_cs = value.0;
}
}
pub fn wheel_axle_cs(&self, i: usize) -> Option<RawVector> {
self.controller.wheels().get(i).map(|w| w.axle_cs.into())
}
pub fn set_wheel_axle_cs(&mut self, i: usize, value: &RawVector) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.axle_cs = value.0;
}
}
pub fn wheel_friction_slip(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.friction_slip)
}
pub fn set_wheel_friction_slip(&mut self, i: usize, value: Real) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.friction_slip = value;
}
}
pub fn wheel_side_friction_stiffness(&self, i: usize) -> Option<f32> {
self.controller
.wheels()
.get(i)
.map(|w| w.side_friction_stiffness)
}
pub fn set_wheel_side_friction_stiffness(&mut self, i: usize, stiffness: f32) {
if let Some(wheel) = self.controller.wheels_mut().get_mut(i) {
wheel.side_friction_stiffness = stiffness;
}
}
/*
* Getters only.
*/
pub fn wheel_rotation(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.rotation)
}
pub fn wheel_forward_impulse(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.forward_impulse)
}
pub fn wheel_side_impulse(&self, i: usize) -> Option<Real> {
self.controller.wheels().get(i).map(|w| w.side_impulse)
}
pub fn wheel_suspension_force(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.wheel_suspension_force)
}
pub fn wheel_contact_normal_ws(&self, i: usize) -> Option<RawVector> {
self.controller
.wheels()
.get(i)
.map(|w| w.raycast_info().contact_normal_ws.into())
}
pub fn wheel_contact_point_ws(&self, i: usize) -> Option<RawVector> {
self.controller
.wheels()
.get(i)
.map(|w| w.raycast_info().contact_point_ws.into())
}
pub fn wheel_suspension_length(&self, i: usize) -> Option<Real> {
self.controller
.wheels()
.get(i)
.map(|w| w.raycast_info().suspension_length)
}
pub fn wheel_hard_point_ws(&self, i: usize) -> Option<RawVector> {
self.controller
.wheels()
.get(i)
.map(|w| w.raycast_info().hard_point_ws.into())
}
pub fn wheel_is_in_contact(&self, i: usize) -> bool {
self.controller
.wheels()
.get(i)
.map(|w| w.raycast_info().is_in_contact)
.unwrap_or(false)
}
pub fn wheel_ground_object(&self, i: usize) -> Option<FlatHandle> {
self.controller
.wheels()
.get(i)
.and_then(|w| w.raycast_info().ground_object)
.map(|h| utils::flat_handle(h.0))
}
}

View File

@@ -0,0 +1,13 @@
use rapier::dynamics::CCDSolver;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawCCDSolver(pub(crate) CCDSolver);
#[wasm_bindgen]
impl RawCCDSolver {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawCCDSolver(CCDSolver::new())
}
}

View File

@@ -0,0 +1,215 @@
use crate::dynamics::{RawImpulseJointSet, RawJointAxis, RawJointType, RawMotorModel};
use crate::math::{RawRotation, RawVector};
use crate::utils::{self, FlatHandle};
use rapier::dynamics::JointAxis;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
impl RawImpulseJointSet {
/// The type of this joint.
pub fn jointType(&self, handle: FlatHandle) -> RawJointType {
self.map(handle, |j| j.data.locked_axes.into())
}
/// The unique integer identifier of the first rigid-body this joint it attached to.
pub fn jointBodyHandle1(&self, handle: FlatHandle) -> FlatHandle {
self.map(handle, |j| utils::flat_handle(j.body1.0))
}
/// The unique integer identifier of the second rigid-body this joint is attached to.
pub fn jointBodyHandle2(&self, handle: FlatHandle) -> FlatHandle {
self.map(handle, |j| utils::flat_handle(j.body2.0))
}
/// The angular part of the joints local frame relative to the first rigid-body it is attached to.
pub fn jointFrameX1(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |j| j.data.local_frame1.rotation.into())
}
/// The angular part of the joints local frame relative to the second rigid-body it is attached to.
pub fn jointFrameX2(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |j| j.data.local_frame2.rotation.into())
}
/// The position of the first anchor of this joint.
///
/// The first anchor gives the position of the points application point on the
/// local frame of the first rigid-body it is attached to.
pub fn jointAnchor1(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |j| j.data.local_frame1.translation.vector.into())
}
/// The position of the second anchor of this joint.
///
/// The second anchor gives the position of the points application point on the
/// local frame of the second rigid-body it is attached to.
pub fn jointAnchor2(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |j| j.data.local_frame2.translation.vector.into())
}
/// Sets the position of the first local anchor
pub fn jointSetAnchor1(&mut self, handle: FlatHandle, newPos: &RawVector) {
self.map_mut(handle, |j| {
j.data.set_local_anchor1(newPos.0.into());
});
}
/// Sets the position of the second local anchor
pub fn jointSetAnchor2(&mut self, handle: FlatHandle, newPos: &RawVector) {
self.map_mut(handle, |j| {
j.data.set_local_anchor2(newPos.0.into());
})
}
/// Are contacts between the rigid-bodies attached by this joint enabled?
pub fn jointContactsEnabled(&self, handle: FlatHandle) -> bool {
self.map(handle, |j| j.data.contacts_enabled)
}
/// Sets whether contacts are enabled between the rigid-bodies attached by this joint.
pub fn jointSetContactsEnabled(&mut self, handle: FlatHandle, enabled: bool) {
self.map_mut(handle, |j| {
j.data.contacts_enabled = enabled;
});
}
/// Are the limits for this joint enabled?
pub fn jointLimitsEnabled(&self, handle: FlatHandle, axis: RawJointAxis) -> bool {
self.map(handle, |j| {
j.data.limit_axes.contains(JointAxis::from(axis).into())
})
}
/// Return the lower limit along the given joint axis.
pub fn jointLimitsMin(&self, handle: FlatHandle, axis: RawJointAxis) -> f32 {
self.map(handle, |j| j.data.limits[axis as usize].min)
}
/// If this is a prismatic joint, returns its upper limit.
pub fn jointLimitsMax(&self, handle: FlatHandle, axis: RawJointAxis) -> f32 {
self.map(handle, |j| j.data.limits[axis as usize].max)
}
/// Enables and sets the joint limits
pub fn jointSetLimits(&mut self, handle: FlatHandle, axis: RawJointAxis, min: f32, max: f32) {
self.map_mut(handle, |j| {
j.data.set_limits(axis.into(), [min, max]);
});
}
pub fn jointConfigureMotorModel(
&mut self,
handle: FlatHandle,
axis: RawJointAxis,
model: RawMotorModel,
) {
self.map_mut(handle, |j| {
j.data.motors[axis as usize].model = model.into()
})
}
/*
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotorVelocity(
&mut self,
handle: FlatHandle,
vx: f32,
vy: f32,
vz: f32,
factor: f32,
) {
let targetVel = Vector3::new(vx, vy, vz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => j.configure_motor_velocity(targetVel, factor),
_ => {}
})
}
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotorPosition(
&mut self,
handle: FlatHandle,
qw: f32,
qx: f32,
qy: f32,
qz: f32,
stiffness: f32,
damping: f32,
) {
let quat = Quaternion::new(qw, qx, qy, qz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => {
if let Some(unit_quat) = UnitQuaternion::try_new(quat, 1.0e-5) {
j.configure_motor_position(unit_quat, stiffness, damping)
}
}
_ => {}
})
}
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotor(
&mut self,
handle: FlatHandle,
qw: f32,
qx: f32,
qy: f32,
qz: f32,
vx: f32,
vy: f32,
vz: f32,
stiffness: f32,
damping: f32,
) {
let quat = Quaternion::new(qw, qx, qy, qz);
let vel = Vector3::new(vx, vy, vz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => {
if let Some(unit_quat) = UnitQuaternion::try_new(quat, 1.0e-5) {
j.configure_motor(unit_quat, vel, stiffness, damping)
}
}
_ => {}
})
}
*/
pub fn jointConfigureMotorVelocity(
&mut self,
handle: FlatHandle,
axis: RawJointAxis,
targetVel: f32,
factor: f32,
) {
self.jointConfigureMotor(handle, axis, 0.0, targetVel, 0.0, factor)
}
pub fn jointConfigureMotorPosition(
&mut self,
handle: FlatHandle,
axis: RawJointAxis,
targetPos: f32,
stiffness: f32,
damping: f32,
) {
self.jointConfigureMotor(handle, axis, targetPos, 0.0, stiffness, damping)
}
pub fn jointConfigureMotor(
&mut self,
handle: FlatHandle,
axis: RawJointAxis,
targetPos: f32,
targetVel: f32,
stiffness: f32,
damping: f32,
) {
self.map_mut(handle, |j| {
j.data
.set_motor(axis.into(), targetPos, targetVel, stiffness, damping);
})
}
}

View File

@@ -0,0 +1,92 @@
use crate::dynamics::RawGenericJoint;
use crate::utils::{self, FlatHandle};
use rapier::dynamics::{ImpulseJoint, ImpulseJointSet};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawImpulseJointSet(pub(crate) ImpulseJointSet);
impl RawImpulseJointSet {
pub(crate) fn map<T>(&self, handle: FlatHandle, f: impl FnOnce(&ImpulseJoint) -> T) -> T {
let body = self.0.get(utils::impulse_joint_handle(handle)).expect(
"Invalid ImpulseJoint reference. It may have been removed from the physics World.",
);
f(body)
}
pub(crate) fn map_mut<T>(
&mut self,
handle: FlatHandle,
f: impl FnOnce(&mut ImpulseJoint) -> T,
) -> T {
let body = self
.0
.get_mut(utils::impulse_joint_handle(handle), true)
.expect(
"Invalid ImpulseJoint reference. It may have been removed from the physics World.",
);
f(body)
}
}
#[wasm_bindgen]
impl RawImpulseJointSet {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawImpulseJointSet(ImpulseJointSet::new())
}
pub fn createJoint(
&mut self,
params: &RawGenericJoint,
parent1: FlatHandle,
parent2: FlatHandle,
wake_up: bool,
) -> FlatHandle {
utils::flat_handle(
self.0
.insert(
utils::body_handle(parent1),
utils::body_handle(parent2),
params.0.clone(),
wake_up,
)
.0,
)
}
pub fn remove(&mut self, handle: FlatHandle, wakeUp: bool) {
let handle = utils::impulse_joint_handle(handle);
self.0.remove(handle, wakeUp);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn contains(&self, handle: FlatHandle) -> bool {
self.0.get(utils::impulse_joint_handle(handle)).is_some()
}
/// Applies the given JavaScript function to the integer handle of each joint managed by this physics world.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each joint managed by this set. Called as `f(collider)`.
pub fn forEachJointHandle(&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)));
}
}
/// Applies the given JavaScript function to the integer handle of each joint attached to the given rigid-body.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each joint attached to the rigid-body. Called as `f(collider)`.
pub fn forEachJointAttachedToRigidBody(&self, body: FlatHandle, f: &js_sys::Function) {
let this = JsValue::null();
for (_, _, handle, _) in self.0.attached_joints(utils::body_handle(body)) {
let _ = f.call1(&this, &JsValue::from(utils::flat_handle(handle.0)));
}
}
}

View File

@@ -0,0 +1,101 @@
use rapier::dynamics::IntegrationParameters;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawIntegrationParameters(pub(crate) IntegrationParameters);
#[wasm_bindgen]
impl RawIntegrationParameters {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawIntegrationParameters(IntegrationParameters::default())
}
#[wasm_bindgen(getter)]
pub fn dt(&self) -> f32 {
self.0.dt
}
#[wasm_bindgen(getter)]
pub fn contact_erp(&self) -> f32 {
self.0.contact_erp()
}
#[wasm_bindgen(getter)]
pub fn normalizedAllowedLinearError(&self) -> f32 {
self.0.normalized_allowed_linear_error
}
#[wasm_bindgen(getter)]
pub fn normalizedPredictionDistance(&self) -> f32 {
self.0.normalized_prediction_distance
}
#[wasm_bindgen(getter)]
pub fn numSolverIterations(&self) -> usize {
self.0.num_solver_iterations
}
#[wasm_bindgen(getter)]
pub fn numInternalPgsIterations(&self) -> usize {
self.0.num_internal_pgs_iterations
}
#[wasm_bindgen(getter)]
pub fn minIslandSize(&self) -> usize {
self.0.min_island_size
}
#[wasm_bindgen(getter)]
pub fn maxCcdSubsteps(&self) -> usize {
self.0.max_ccd_substeps
}
#[wasm_bindgen(getter)]
pub fn lengthUnit(&self) -> f32 {
self.0.length_unit
}
#[wasm_bindgen(setter)]
pub fn set_dt(&mut self, value: f32) {
self.0.dt = value;
}
#[wasm_bindgen(setter)]
pub fn set_contact_natural_frequency(&mut self, value: f32) {
self.0.contact_natural_frequency = value
}
#[wasm_bindgen(setter)]
pub fn set_normalizedAllowedLinearError(&mut self, value: f32) {
self.0.normalized_allowed_linear_error = value
}
#[wasm_bindgen(setter)]
pub fn set_normalizedPredictionDistance(&mut self, value: f32) {
self.0.normalized_prediction_distance = value
}
#[wasm_bindgen(setter)]
pub fn set_numSolverIterations(&mut self, value: usize) {
self.0.num_solver_iterations = value;
}
#[wasm_bindgen(setter)]
pub fn set_numInternalPgsIterations(&mut self, value: usize) {
self.0.num_internal_pgs_iterations = value;
}
#[wasm_bindgen(setter)]
pub fn set_minIslandSize(&mut self, value: usize) {
self.0.min_island_size = value
}
#[wasm_bindgen(setter)]
pub fn set_maxCcdSubsteps(&mut self, value: usize) {
self.0.max_ccd_substeps = value
}
#[wasm_bindgen(setter)]
pub fn set_lengthUnit(&mut self, value: f32) {
self.0.length_unit = value
}
}

View File

@@ -0,0 +1,31 @@
use crate::utils;
use rapier::dynamics::IslandManager;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawIslandManager(pub(crate) IslandManager);
#[wasm_bindgen]
impl RawIslandManager {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawIslandManager(IslandManager::new())
}
/// Applies the given JavaScript function to the integer handle of each active rigid-body
/// managed by this island manager.
///
/// After a short time of inactivity, a rigid-body is automatically deactivated ("asleep") by
/// the physics engine in order to save computational power. A sleeping rigid-body never moves
/// unless it is moved manually by the user.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each active rigid-body managed by this
/// set. Called as `f(collider)`.
pub fn forEachActiveRigidBodyHandle(&self, f: &js_sys::Function) {
let this = JsValue::null();
for handle in self.0.active_bodies() {
let _ = f.call1(&this, &JsValue::from(utils::flat_handle(handle.0)));
}
}
}

View File

@@ -0,0 +1,314 @@
use crate::math::{RawRotation, RawVector};
use na::Unit;
use rapier::dynamics::{
FixedJointBuilder, GenericJoint, JointAxesMask, JointAxis, MotorModel, PrismaticJointBuilder,
RevoluteJointBuilder, RopeJointBuilder, SpringJointBuilder,
};
#[cfg(feature = "dim3")]
use rapier::dynamics::{GenericJointBuilder, SphericalJointBuilder};
use rapier::math::Isometry;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[cfg(feature = "dim2")]
pub enum RawJointType {
Revolute,
Fixed,
Prismatic,
Rope,
Spring,
Generic,
}
#[wasm_bindgen]
#[cfg(feature = "dim3")]
pub enum RawJointType {
Revolute,
Fixed,
Prismatic,
Rope,
Spring,
Spherical,
Generic,
}
/// The type of this joint.
#[cfg(feature = "dim2")]
impl From<JointAxesMask> for RawJointType {
fn from(ty: JointAxesMask) -> RawJointType {
let rev_axes = JointAxesMask::LIN_X | JointAxesMask::LIN_Y;
let pri_axes = JointAxesMask::LIN_Y | JointAxesMask::ANG_X;
let fix_axes = JointAxesMask::LIN_X | JointAxesMask::LIN_Y | JointAxesMask::ANG_X;
if ty == rev_axes {
RawJointType::Revolute
} else if ty == pri_axes {
RawJointType::Prismatic
} else if ty == fix_axes {
RawJointType::Fixed
} else {
RawJointType::Generic
}
}
}
/// The type of this joint.
#[cfg(feature = "dim3")]
impl From<JointAxesMask> for RawJointType {
fn from(ty: JointAxesMask) -> RawJointType {
let rev_axes = JointAxesMask::LIN_X
| JointAxesMask::LIN_Y
| JointAxesMask::LIN_Z
| JointAxesMask::ANG_Y
| JointAxesMask::ANG_Z;
let pri_axes = JointAxesMask::LIN_Y
| JointAxesMask::LIN_Z
| JointAxesMask::ANG_X
| JointAxesMask::ANG_Y
| JointAxesMask::ANG_Z;
let sph_axes = JointAxesMask::ANG_X | JointAxesMask::ANG_Y | JointAxesMask::ANG_Z;
let fix_axes = JointAxesMask::LIN_X
| JointAxesMask::LIN_Y
| JointAxesMask::LIN_Z
| JointAxesMask::ANG_X
| JointAxesMask::ANG_Y
| JointAxesMask::ANG_Z;
if ty == rev_axes {
RawJointType::Revolute
} else if ty == pri_axes {
RawJointType::Prismatic
} else if ty == sph_axes {
RawJointType::Spherical
} else if ty == fix_axes {
RawJointType::Fixed
} else {
RawJointType::Generic
}
}
}
#[wasm_bindgen]
pub enum RawMotorModel {
AccelerationBased,
ForceBased,
}
impl From<RawMotorModel> for MotorModel {
fn from(model: RawMotorModel) -> MotorModel {
match model {
RawMotorModel::AccelerationBased => MotorModel::AccelerationBased,
RawMotorModel::ForceBased => MotorModel::ForceBased,
}
}
}
#[cfg(feature = "dim2")]
#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum RawJointAxis {
LinX,
LinY,
AngX,
}
#[cfg(feature = "dim3")]
#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum RawJointAxis {
LinX,
LinY,
LinZ,
AngX,
AngY,
AngZ,
}
impl From<RawJointAxis> for JointAxis {
fn from(axis: RawJointAxis) -> JointAxis {
match axis {
RawJointAxis::LinX => JointAxis::LinX,
RawJointAxis::LinY => JointAxis::LinY,
#[cfg(feature = "dim3")]
RawJointAxis::LinZ => JointAxis::LinZ,
RawJointAxis::AngX => JointAxis::AngX,
#[cfg(feature = "dim3")]
RawJointAxis::AngY => JointAxis::AngY,
#[cfg(feature = "dim3")]
RawJointAxis::AngZ => JointAxis::AngZ,
}
}
}
#[wasm_bindgen]
pub struct RawGenericJoint(pub(crate) GenericJoint);
#[wasm_bindgen]
impl RawGenericJoint {
/// Creates a new joint descriptor that builds generic joints.
///
/// Generic joints allow arbitrary axes of freedom to be selected
/// for the joint from the available 6 degrees of freedom.
#[cfg(feature = "dim3")]
pub fn generic(
anchor1: &RawVector,
anchor2: &RawVector,
axis: &RawVector,
lockedAxes: u8,
) -> Option<RawGenericJoint> {
let axesMask: JointAxesMask = JointAxesMask::from_bits(lockedAxes)?;
let axis = Unit::try_new(axis.0, 0.0)?;
let joint: GenericJoint = GenericJointBuilder::new(axesMask)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.local_axis1(axis)
.local_axis2(axis)
.into();
Some(Self(joint))
}
pub fn spring(
rest_length: f32,
stiffness: f32,
damping: f32,
anchor1: &RawVector,
anchor2: &RawVector,
) -> Self {
Self(
SpringJointBuilder::new(rest_length, stiffness, damping)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.into(),
)
}
pub fn rope(length: f32, anchor1: &RawVector, anchor2: &RawVector) -> Self {
Self(
RopeJointBuilder::new(length)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.into(),
)
}
/// Create a new joint descriptor that builds spherical joints.
///
/// A spherical joints allows three relative rotational degrees of freedom
/// by preventing any relative translation between the anchors of the
/// two attached rigid-bodies.
#[cfg(feature = "dim3")]
pub fn spherical(anchor1: &RawVector, anchor2: &RawVector) -> Self {
Self(
SphericalJointBuilder::new()
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.into(),
)
}
/// Creates a new joint descriptor that builds a Prismatic joint.
///
/// A prismatic joint removes all the degrees of freedom between the
/// affected bodies, except for the translation along one axis.
///
/// Returns `None` if any of the provided axes cannot be normalized.
#[cfg(feature = "dim2")]
pub fn prismatic(
anchor1: &RawVector,
anchor2: &RawVector,
axis: &RawVector,
limitsEnabled: bool,
limitsMin: f32,
limitsMax: f32,
) -> Option<RawGenericJoint> {
let axis = Unit::try_new(axis.0, 0.0)?;
let mut joint = PrismaticJointBuilder::new(axis)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into());
if limitsEnabled {
joint = joint.limits([limitsMin, limitsMax]);
}
Some(Self(joint.into()))
}
/// Creates a new joint descriptor that builds a Prismatic joint.
///
/// A prismatic joint removes all the degrees of freedom between the
/// affected bodies, except for the translation along one axis.
///
/// Returns `None` if any of the provided axes cannot be normalized.
#[cfg(feature = "dim3")]
pub fn prismatic(
anchor1: &RawVector,
anchor2: &RawVector,
axis: &RawVector,
limitsEnabled: bool,
limitsMin: f32,
limitsMax: f32,
) -> Option<RawGenericJoint> {
let axis = Unit::try_new(axis.0, 0.0)?;
let mut joint = PrismaticJointBuilder::new(axis)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into());
if limitsEnabled {
joint = joint.limits([limitsMin, limitsMax]);
}
Some(Self(joint.into()))
}
/// Creates a new joint descriptor that builds a Fixed joint.
///
/// A fixed joint removes all the degrees of freedom between the affected bodies.
pub fn fixed(
anchor1: &RawVector,
axes1: &RawRotation,
anchor2: &RawVector,
axes2: &RawRotation,
) -> RawGenericJoint {
let pos1 = Isometry::from_parts(anchor1.0.into(), axes1.0);
let pos2 = Isometry::from_parts(anchor2.0.into(), axes2.0);
Self(
FixedJointBuilder::new()
.local_frame1(pos1)
.local_frame2(pos2)
.into(),
)
}
/// Create a new joint descriptor that builds Revolute joints.
///
/// A revolute joint removes all degrees of freedom between the affected
/// bodies except for the rotation.
#[cfg(feature = "dim2")]
pub fn revolute(anchor1: &RawVector, anchor2: &RawVector) -> Option<RawGenericJoint> {
Some(Self(
RevoluteJointBuilder::new()
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.into(),
))
}
/// Create a new joint descriptor that builds Revolute joints.
///
/// A revolute joint removes all degrees of freedom between the affected
/// bodies except for the rotation along one axis.
#[cfg(feature = "dim3")]
pub fn revolute(
anchor1: &RawVector,
anchor2: &RawVector,
axis: &RawVector,
) -> Option<RawGenericJoint> {
let axis = Unit::try_new(axis.0, 0.0)?;
Some(Self(
RevoluteJointBuilder::new(axis)
.local_anchor1(anchor1.0.into())
.local_anchor2(anchor2.0.into())
.into(),
))
}
}

View File

@@ -0,0 +1,20 @@
//! Structures related to dynamics: bodies, joints, etc.
pub use self::ccd_solver::*;
pub use self::impulse_joint_set::*;
pub use self::integration_parameters::*;
pub use self::island_manager::*;
pub use self::joint::*;
pub use self::multibody_joint_set::*;
pub use self::rigid_body_set::*;
mod ccd_solver;
mod impulse_joint;
mod impulse_joint_set;
mod integration_parameters;
mod island_manager;
mod joint;
mod multibody_joint;
mod multibody_joint_set;
mod rigid_body;
mod rigid_body_set;

View File

@@ -0,0 +1,196 @@
use crate::dynamics::{RawJointAxis, RawJointType, RawMultibodyJointSet};
use crate::math::{RawRotation, RawVector};
use crate::utils::FlatHandle;
use rapier::dynamics::JointAxis;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
impl RawMultibodyJointSet {
/// The type of this joint.
pub fn jointType(&self, handle: FlatHandle) -> RawJointType {
self.map(handle, |j| j.data.locked_axes.into())
}
// /// The unique integer identifier of the first rigid-body this joint it attached to.
// pub fn jointBodyHandle1(&self, handle: FlatHandle) -> u32 {
// self.map(handle, |j| j.body1.into_raw_parts().0)
// }
//
// /// The unique integer identifier of the second rigid-body this joint is attached to.
// pub fn jointBodyHandle2(&self, handle: FlatHandle) -> u32 {
// self.map(handle, |j| j.body2.into_raw_parts().0)
// }
/// The angular part of the joints local frame relative to the first rigid-body it is attached to.
pub fn jointFrameX1(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |j| j.data.local_frame1.rotation.into())
}
/// The angular part of the joints local frame relative to the second rigid-body it is attached to.
pub fn jointFrameX2(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |j| j.data.local_frame2.rotation.into())
}
/// The position of the first anchor of this joint.
///
/// The first anchor gives the position of the points application point on the
/// local frame of the first rigid-body it is attached to.
pub fn jointAnchor1(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |j| j.data.local_frame1.translation.vector.into())
}
/// The position of the second anchor of this joint.
///
/// The second anchor gives the position of the points application point on the
/// local frame of the second rigid-body it is attached to.
pub fn jointAnchor2(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |j| j.data.local_frame2.translation.vector.into())
}
/// Are contacts between the rigid-bodies attached by this joint enabled?
pub fn jointContactsEnabled(&self, handle: FlatHandle) -> bool {
self.map(handle, |j| j.data.contacts_enabled)
}
/// Sets whether contacts are enabled between the rigid-bodies attached by this joint.
pub fn jointSetContactsEnabled(&mut self, handle: FlatHandle, enabled: bool) {
self.map_mut(handle, |j| {
j.data.contacts_enabled = enabled;
});
}
/// Are the limits for this joint enabled?
pub fn jointLimitsEnabled(&self, handle: FlatHandle, axis: RawJointAxis) -> bool {
self.map(handle, |j| {
j.data.limit_axes.contains(JointAxis::from(axis).into())
})
}
/// Return the lower limit along the given joint axis.
pub fn jointLimitsMin(&self, handle: FlatHandle, axis: RawJointAxis) -> f32 {
self.map(handle, |j| j.data.limits[axis as usize].min)
}
/// If this is a prismatic joint, returns its upper limit.
pub fn jointLimitsMax(&self, handle: FlatHandle, axis: RawJointAxis) -> f32 {
self.map(handle, |j| j.data.limits[axis as usize].max)
}
// pub fn jointConfigureMotorModel(
// &mut self,
// handle: FlatHandle,
// axis: RawJointAxis,
// model: RawMotorModel,
// ) {
// self.map_mut(handle, |j| {
// j.data.motors[axis as usize].model = model.into()
// })
// }
/*
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotorVelocity(
&mut self,
handle: FlatHandle,
vx: f32,
vy: f32,
vz: f32,
factor: f32,
) {
let targetVel = Vector3::new(vx, vy, vz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => j.configure_motor_velocity(targetVel, factor),
_ => {}
})
}
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotorPosition(
&mut self,
handle: FlatHandle,
qw: f32,
qx: f32,
qy: f32,
qz: f32,
stiffness: f32,
damping: f32,
) {
let quat = Quaternion::new(qw, qx, qy, qz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => {
if let Some(unit_quat) = UnitQuaternion::try_new(quat, 1.0e-5) {
j.configure_motor_position(unit_quat, stiffness, damping)
}
}
_ => {}
})
}
#[cfg(feature = "dim3")]
pub fn jointConfigureBallMotor(
&mut self,
handle: FlatHandle,
qw: f32,
qx: f32,
qy: f32,
qz: f32,
vx: f32,
vy: f32,
vz: f32,
stiffness: f32,
damping: f32,
) {
let quat = Quaternion::new(qw, qx, qy, qz);
let vel = Vector3::new(vx, vy, vz);
self.map_mut(handle, |j| match &mut j.params {
JointData::SphericalJoint(j) => {
if let Some(unit_quat) = UnitQuaternion::try_new(quat, 1.0e-5) {
j.configure_motor(unit_quat, vel, stiffness, damping)
}
}
_ => {}
})
}
*/
// pub fn jointConfigureMotorVelocity(
// &mut self,
// handle: FlatHandle,
// axis: RawJointAxis,
// targetVel: f32,
// factor: f32,
// ) {
// self.jointConfigureMotor(handle, axis, 0.0, targetVel, 0.0, factor)
// }
//
// pub fn jointConfigureMotorPosition(
// &mut self,
// handle: FlatHandle,
// axis: RawJointAxis,
// targetPos: f32,
// stiffness: f32,
// damping: f32,
// ) {
// self.jointConfigureMotor(handle, axis, targetPos, 0.0, stiffness, damping)
// }
// pub fn jointConfigureMotor(
// &mut self,
// handle: FlatHandle,
// axis: RawJointAxis,
// targetPos: f32,
// targetVel: f32,
// stiffness: f32,
// damping: f32,
// ) {
// self.map_mut(handle, |j| {
// j.data.motors[axis as usize].target_pos = targetPos;
// j.data.motors[axis as usize].target_vel = targetVel;
// j.data.motors[axis as usize].stiffness = stiffness;
// j.data.motors[axis as usize].damping = damping;
// })
// }
}

View File

@@ -0,0 +1,85 @@
use crate::dynamics::RawGenericJoint;
use crate::utils::{self, FlatHandle};
use rapier::dynamics::{MultibodyJoint, MultibodyJointSet};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawMultibodyJointSet(pub(crate) MultibodyJointSet);
impl RawMultibodyJointSet {
pub(crate) fn map<T>(&self, handle: FlatHandle, f: impl FnOnce(&MultibodyJoint) -> T) -> T {
let (body, link_id) = self
.0
.get(utils::multibody_joint_handle(handle))
.expect("Invalid Joint reference. It may have been removed from the physics World.");
f(body.link(link_id).unwrap().joint())
}
pub(crate) fn map_mut<T>(
&mut self,
handle: FlatHandle,
f: impl FnOnce(&mut MultibodyJoint) -> T,
) -> T {
let (body, link_id) = self
.0
.get_mut(utils::multibody_joint_handle(handle))
.expect("Invalid Joint reference. It may have been removed from the physics World.");
f(&mut body.link_mut(link_id).unwrap().joint)
}
}
#[wasm_bindgen]
impl RawMultibodyJointSet {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawMultibodyJointSet(MultibodyJointSet::new())
}
pub fn createJoint(
&mut self,
params: &RawGenericJoint,
parent1: FlatHandle,
parent2: FlatHandle,
wakeUp: bool,
) -> FlatHandle {
// TODO: avoid the unwrap?
let parent1 = utils::body_handle(parent1);
let parent2 = utils::body_handle(parent2);
self.0
.insert(parent1, parent2, params.0.clone(), wakeUp)
.map(|h| utils::flat_handle(h.0))
.unwrap_or(FlatHandle::MAX)
}
pub fn remove(&mut self, handle: FlatHandle, wakeUp: bool) {
let handle = utils::multibody_joint_handle(handle);
self.0.remove(handle, wakeUp);
}
pub fn contains(&self, handle: FlatHandle) -> bool {
self.0.get(utils::multibody_joint_handle(handle)).is_some()
}
/// Applies the given JavaScript function to the integer handle of each joint managed by this physics world.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each joint managed by this set. Called as `f(collider)`.
pub fn forEachJointHandle(&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)));
}
}
/// Applies the given JavaScript function to the integer handle of each joint attached to the given rigid-body.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each joint attached to the rigid-body. Called as `f(collider)`.
pub fn forEachJointAttachedToRigidBody(&self, body: FlatHandle, f: &js_sys::Function) {
let this = JsValue::null();
for (_, _, handle) in self.0.attached_joints(utils::body_handle(body)) {
let _ = f.call1(&this, &JsValue::from(utils::flat_handle(handle.0)));
}
}
}

View File

@@ -0,0 +1,753 @@
use crate::dynamics::{RawRigidBodySet, RawRigidBodyType};
use crate::geometry::RawColliderSet;
#[cfg(feature = "dim3")]
use crate::math::RawSdpMatrix3;
use crate::math::{RawRotation, RawVector};
use crate::utils::{self, FlatHandle};
use na::Point;
use rapier::dynamics::MassProperties;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
impl RawRigidBodySet {
/// The world-space translation of this rigid-body.
pub fn rbTranslation(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| RawVector(rb.position().translation.vector))
}
/// The world-space orientation of this rigid-body.
pub fn rbRotation(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |rb| RawRotation(rb.position().rotation))
}
/// Put the given rigid-body to sleep.
pub fn rbSleep(&mut self, handle: FlatHandle) {
self.map_mut(handle, |rb| rb.sleep());
}
/// Is this rigid-body sleeping?
pub fn rbIsSleeping(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_sleeping())
}
/// Is the velocity of this rigid-body not zero?
pub fn rbIsMoving(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_moving())
}
/// The world-space predicted translation of this rigid-body.
///
/// If this rigid-body is kinematic this value is set by the `setNextKinematicTranslation`
/// method and is used for estimating the kinematic body velocity at the next timestep.
/// For non-kinematic bodies, this value is currently unspecified.
pub fn rbNextTranslation(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| {
RawVector(rb.next_position().translation.vector)
})
}
/// The world-space predicted orientation of this rigid-body.
///
/// If this rigid-body is kinematic this value is set by the `setNextKinematicRotation`
/// method and is used for estimating the kinematic body velocity at the next timestep.
/// For non-kinematic bodies, this value is currently unspecified.
pub fn rbNextRotation(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |rb| RawRotation(rb.next_position().rotation))
}
/// Sets the translation of this rigid-body.
///
/// # Parameters
/// - `x`: the world-space position of the rigid-body along the `x` axis.
/// - `y`: the world-space position of the rigid-body along the `y` axis.
/// - `z`: the world-space position of the rigid-body along the `z` axis.
/// - `wakeUp`: forces the rigid-body to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim3")]
pub fn rbSetTranslation(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_translation(na::Vector3::new(x, y, z), wakeUp);
})
}
/// Sets the translation of this rigid-body.
///
/// # Parameters
/// - `x`: the world-space position of the rigid-body along the `x` axis.
/// - `y`: the world-space position of the rigid-body along the `y` axis.
/// - `wakeUp`: forces the rigid-body to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim2")]
pub fn rbSetTranslation(&mut self, handle: FlatHandle, x: f32, y: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_translation(na::Vector2::new(x, y), wakeUp);
})
}
/// Sets the rotation quaternion of this rigid-body.
///
/// 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 rigid-body to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim3")]
pub fn rbSetRotation(
&mut self,
handle: FlatHandle,
x: f32,
y: f32,
z: f32,
w: f32,
wakeUp: bool,
) {
if let Some(q) = na::Unit::try_new(na::Quaternion::new(w, x, y, z), 0.0) {
self.map_mut(handle, |rb| rb.set_rotation(q, wakeUp))
}
}
/// Sets the rotation angle of this rigid-body.
///
/// # Parameters
/// - `angle`: the rotation angle, in radians.
/// - `wakeUp`: forces the rigid-body to wake-up so it is properly affected by forces if it
/// wasn't moving before modifying its position.
#[cfg(feature = "dim2")]
pub fn rbSetRotation(&mut self, handle: FlatHandle, angle: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_rotation(na::UnitComplex::new(angle), wakeUp)
})
}
/// Sets the linear velocity of this rigid-body.
pub fn rbSetLinvel(&mut self, handle: FlatHandle, linvel: &RawVector, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_linvel(linvel.0, wakeUp);
});
}
/// Sets the angular velocity of this rigid-body.
#[cfg(feature = "dim2")]
pub fn rbSetAngvel(&mut self, handle: FlatHandle, angvel: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_angvel(angvel, wakeUp);
});
}
/// Sets the angular velocity of this rigid-body.
#[cfg(feature = "dim3")]
pub fn rbSetAngvel(&mut self, handle: FlatHandle, angvel: &RawVector, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.set_angvel(angvel.0, wakeUp);
});
}
/// If this rigid body is kinematic, sets its future translation after the next timestep integration.
///
/// This should be used instead of `rigidBody.setTranslation` to make the dynamic object
/// interacting with this kinematic body behave as expected. Internally, Rapier will compute
/// an artificial velocity for this rigid-body from its current position and its next kinematic
/// position. This velocity will be used to compute forces on dynamic bodies interacting with
/// this body.
///
/// # Parameters
/// - `x`: the world-space position of the rigid-body along the `x` axis.
/// - `y`: the world-space position of the rigid-body along the `y` axis.
/// - `z`: the world-space position of the rigid-body along the `z` axis.
#[cfg(feature = "dim3")]
pub fn rbSetNextKinematicTranslation(&mut self, handle: FlatHandle, x: f32, y: f32, z: f32) {
self.map_mut(handle, |rb| {
rb.set_next_kinematic_translation(na::Vector3::new(x, y, z));
})
}
/// If this rigid body is kinematic, sets its future translation after the next timestep integration.
///
/// This should be used instead of `rigidBody.setTranslation` to make the dynamic object
/// interacting with this kinematic body behave as expected. Internally, Rapier will compute
/// an artificial velocity for this rigid-body from its current position and its next kinematic
/// position. This velocity will be used to compute forces on dynamic bodies interacting with
/// this body.
///
/// # Parameters
/// - `x`: the world-space position of the rigid-body along the `x` axis.
/// - `y`: the world-space position of the rigid-body along the `y` axis.
#[cfg(feature = "dim2")]
pub fn rbSetNextKinematicTranslation(&mut self, handle: FlatHandle, x: f32, y: f32) {
self.map_mut(handle, |rb| {
rb.set_next_kinematic_translation(na::Vector2::new(x, y));
})
}
/// If this rigid body is kinematic, sets its future rotation after the next timestep integration.
///
/// This should be used instead of `rigidBody.setRotation` to make the dynamic object
/// interacting with this kinematic body behave as expected. Internally, Rapier will compute
/// an artificial velocity for this rigid-body from its current position and its next kinematic
/// position. This velocity will be used to compute forces on dynamic bodies interacting with
/// this body.
///
/// # 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.
#[cfg(feature = "dim3")]
pub fn rbSetNextKinematicRotation(
&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, |rb| {
rb.set_next_kinematic_rotation(q);
})
}
}
/// If this rigid body is kinematic, sets its future rotation after the next timestep integration.
///
/// This should be used instead of `rigidBody.setRotation` to make the dynamic object
/// interacting with this kinematic body behave as expected. Internally, Rapier will compute
/// an artificial velocity for this rigid-body from its current position and its next kinematic
/// position. This velocity will be used to compute forces on dynamic bodies interacting with
/// this body.
///
/// # Parameters
/// - `angle`: the rotation angle, in radians.
#[cfg(feature = "dim2")]
pub fn rbSetNextKinematicRotation(&mut self, handle: FlatHandle, angle: f32) {
self.map_mut(handle, |rb| {
rb.set_next_kinematic_rotation(na::UnitComplex::new(angle));
})
}
pub fn rbRecomputeMassPropertiesFromColliders(
&mut self,
handle: FlatHandle,
colliders: &RawColliderSet,
) {
self.map_mut(handle, |rb| {
rb.recompute_mass_properties_from_colliders(&colliders.0)
})
}
pub fn rbSetAdditionalMass(&mut self, handle: FlatHandle, mass: f32, wake_up: bool) {
self.map_mut(handle, |rb| {
rb.set_additional_mass(mass, wake_up);
})
}
#[cfg(feature = "dim3")]
pub fn rbSetAdditionalMassProperties(
&mut self,
handle: FlatHandle,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: &RawVector,
angularInertiaFrame: &RawRotation,
wake_up: bool,
) {
self.map_mut(handle, |rb| {
let mprops = MassProperties::with_principal_inertia_frame(
centerOfMass.0.into(),
mass,
principalAngularInertia.0,
angularInertiaFrame.0,
);
rb.set_additional_mass_properties(mprops, wake_up)
})
}
#[cfg(feature = "dim2")]
pub fn rbSetAdditionalMassProperties(
&mut self,
handle: FlatHandle,
mass: f32,
centerOfMass: &RawVector,
principalAngularInertia: f32,
wake_up: bool,
) {
self.map_mut(handle, |rb| {
let props = MassProperties::new(centerOfMass.0.into(), mass, principalAngularInertia);
rb.set_additional_mass_properties(props, wake_up)
})
}
/// The linear velocity of this rigid-body.
pub fn rbLinvel(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| RawVector(*rb.linvel()))
}
/// The angular velocity of this rigid-body.
#[cfg(feature = "dim2")]
pub fn rbAngvel(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.angvel())
}
/// The angular velocity of this rigid-body.
#[cfg(feature = "dim3")]
pub fn rbAngvel(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| RawVector(*rb.angvel()))
}
/// The velocity of the given world-space point on this rigid-body.
pub fn rbVelocityAtPoint(&self, handle: FlatHandle, point: &RawVector) -> RawVector {
self.map(handle, |rb| {
rb.velocity_at_point(&Point::from(point.0)).into()
})
}
pub fn rbLockTranslations(&mut self, handle: FlatHandle, locked: bool, wake_up: bool) {
self.map_mut(handle, |rb| rb.lock_translations(locked, wake_up))
}
#[cfg(feature = "dim2")]
pub fn rbSetEnabledTranslations(
&mut self,
handle: FlatHandle,
allow_x: bool,
allow_y: bool,
wake_up: bool,
) {
self.map_mut(handle, |rb| {
rb.set_enabled_translations(allow_x, allow_y, wake_up)
})
}
#[cfg(feature = "dim3")]
pub fn rbSetEnabledTranslations(
&mut self,
handle: FlatHandle,
allow_x: bool,
allow_y: bool,
allow_z: bool,
wake_up: bool,
) {
self.map_mut(handle, |rb| {
rb.set_enabled_translations(allow_x, allow_y, allow_z, wake_up)
})
}
pub fn rbLockRotations(&mut self, handle: FlatHandle, locked: bool, wake_up: bool) {
self.map_mut(handle, |rb| rb.lock_rotations(locked, wake_up))
}
#[cfg(feature = "dim3")]
pub fn rbSetEnabledRotations(
&mut self,
handle: FlatHandle,
allow_x: bool,
allow_y: bool,
allow_z: bool,
wake_up: bool,
) {
self.map_mut(handle, |rb| {
rb.set_enabled_rotations(allow_x, allow_y, allow_z, wake_up)
})
}
pub fn rbDominanceGroup(&self, handle: FlatHandle) -> i8 {
self.map(handle, |rb| rb.dominance_group())
}
pub fn rbSetDominanceGroup(&mut self, handle: FlatHandle, group: i8) {
self.map_mut(handle, |rb| rb.set_dominance_group(group))
}
pub fn rbEnableCcd(&mut self, handle: FlatHandle, enabled: bool) {
self.map_mut(handle, |rb| rb.enable_ccd(enabled))
}
pub fn rbSetSoftCcdPrediction(&mut self, handle: FlatHandle, prediction: f32) {
self.map_mut(handle, |rb| rb.set_soft_ccd_prediction(prediction))
}
/// The mass of this rigid-body.
pub fn rbMass(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.mass())
}
/// The inverse of the mass of a rigid-body.
///
/// If this is zero, the rigid-body is assumed to have infinite mass.
pub fn rbInvMass(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.mass_properties().local_mprops.inv_mass)
}
/// The inverse mass taking into account translation locking.
pub fn rbEffectiveInvMass(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| rb.mass_properties().effective_inv_mass.into())
}
/// The center of mass of a rigid-body expressed in its local-space.
pub fn rbLocalCom(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| {
rb.mass_properties().local_mprops.local_com.into()
})
}
/// The world-space center of mass of the rigid-body.
pub fn rbWorldCom(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| rb.mass_properties().world_com.into())
}
/// The inverse of the principal angular inertia of the rigid-body.
///
/// Components set to zero are assumed to be infinite along the corresponding principal axis.
#[cfg(feature = "dim2")]
pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| {
rb.mass_properties()
.local_mprops
.inv_principal_inertia
.into()
})
}
/// The inverse of the principal angular inertia of the rigid-body.
///
/// Components set to zero are assumed to be infinite along the corresponding principal axis.
#[cfg(feature = "dim3")]
pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| {
rb.mass_properties()
.local_mprops
.inv_principal_inertia
.into()
})
}
#[cfg(feature = "dim3")]
/// The principal vectors of the local angular inertia tensor of the rigid-body.
pub fn rbPrincipalInertiaLocalFrame(&self, handle: FlatHandle) -> RawRotation {
self.map(handle, |rb| {
RawRotation::from(
rb.mass_properties()
.local_mprops
.principal_inertia_local_frame,
)
})
}
/// The angular inertia along the principal inertia axes of the rigid-body.
#[cfg(feature = "dim2")]
pub fn rbPrincipalInertia(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| {
rb.mass_properties().local_mprops.principal_inertia().into()
})
}
/// The angular inertia along the principal inertia axes of the rigid-body.
#[cfg(feature = "dim3")]
pub fn rbPrincipalInertia(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| {
rb.mass_properties().local_mprops.principal_inertia().into()
})
}
/// The world-space inverse angular inertia tensor of the rigid-body,
/// taking into account rotation locking.
#[cfg(feature = "dim2")]
pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| {
rb.mass_properties().effective_world_inv_inertia.into()
})
}
/// The world-space inverse angular inertia tensor of the rigid-body,
/// taking into account rotation locking.
#[cfg(feature = "dim3")]
pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 {
self.map(handle, |rb| {
rb.mass_properties().effective_world_inv_inertia.into()
})
}
/// The effective world-space angular inertia (that takes the potential rotation locking into account) of
/// this rigid-body.
#[cfg(feature = "dim2")]
pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| {
rb.mass_properties().effective_angular_inertia().into()
})
}
/// The effective world-space angular inertia (that takes the potential rotation locking into account) of
/// this rigid-body.
#[cfg(feature = "dim3")]
pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 {
self.map(handle, |rb| {
rb.mass_properties().effective_angular_inertia().into()
})
}
/// Wakes this rigid-body up.
///
/// A dynamic rigid-body that does not move during several consecutive frames will
/// be put to sleep by the physics engine, i.e., it will stop being simulated in order
/// to avoid useless computations.
/// This method forces a sleeping rigid-body to wake-up. This is useful, e.g., before modifying
/// the position of a dynamic body so that it is properly simulated afterwards.
pub fn rbWakeUp(&mut self, handle: FlatHandle) {
self.map_mut(handle, |rb| rb.wake_up(true))
}
/// Is Continuous Collision Detection enabled for this rigid-body?
pub fn rbIsCcdEnabled(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_ccd_enabled())
}
pub fn rbSoftCcdPrediction(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.soft_ccd_prediction())
}
/// The number of colliders attached to this rigid-body.
pub fn rbNumColliders(&self, handle: FlatHandle) -> usize {
self.map(handle, |rb| rb.colliders().len())
}
/// Retrieves the `i-th` collider attached to this rigid-body.
///
/// # Parameters
/// - `at`: The index of the collider to retrieve. Must be a number in `[0, this.numColliders()[`.
/// This index is **not** the same as the unique identifier of the collider.
pub fn rbCollider(&self, handle: FlatHandle, at: usize) -> FlatHandle {
self.map(handle, |rb| utils::flat_handle(rb.colliders()[at].0))
}
/// The status of this rigid-body: fixed, dynamic, or kinematic.
pub fn rbBodyType(&self, handle: FlatHandle) -> RawRigidBodyType {
self.map(handle, |rb| rb.body_type().into())
}
/// Set a new status for this rigid-body: fixed, dynamic, or kinematic.
pub fn rbSetBodyType(&mut self, handle: FlatHandle, status: RawRigidBodyType, wake_up: bool) {
self.map_mut(handle, |rb| rb.set_body_type(status.into(), wake_up));
}
/// Is this rigid-body fixed?
pub fn rbIsFixed(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_fixed())
}
/// Is this rigid-body kinematic?
pub fn rbIsKinematic(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_kinematic())
}
/// Is this rigid-body dynamic?
pub fn rbIsDynamic(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_dynamic())
}
/// The linear damping coefficient of this rigid-body.
pub fn rbLinearDamping(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.linear_damping())
}
/// The angular damping coefficient of this rigid-body.
pub fn rbAngularDamping(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.angular_damping())
}
pub fn rbSetLinearDamping(&mut self, handle: FlatHandle, factor: f32) {
self.map_mut(handle, |rb| rb.set_linear_damping(factor));
}
pub fn rbSetAngularDamping(&mut self, handle: FlatHandle, factor: f32) {
self.map_mut(handle, |rb| rb.set_angular_damping(factor));
}
pub fn rbSetEnabled(&mut self, handle: FlatHandle, enabled: bool) {
self.map_mut(handle, |rb| rb.set_enabled(enabled))
}
pub fn rbIsEnabled(&self, handle: FlatHandle) -> bool {
self.map(handle, |rb| rb.is_enabled())
}
pub fn rbGravityScale(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.gravity_scale())
}
pub fn rbSetGravityScale(&mut self, handle: FlatHandle, factor: f32, wakeUp: bool) {
self.map_mut(handle, |rb| rb.set_gravity_scale(factor, wakeUp));
}
/// Resets to zero all user-added forces added to this rigid-body.
pub fn rbResetForces(&mut self, handle: FlatHandle, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.reset_forces(wakeUp);
})
}
/// Resets to zero all user-added torques added to this rigid-body.
pub fn rbResetTorques(&mut self, handle: FlatHandle, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.reset_torques(wakeUp);
})
}
/// Adds a force at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `force`: the world-space force to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
pub fn rbAddForce(&mut self, handle: FlatHandle, force: &RawVector, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.add_force(force.0, wakeUp);
})
}
/// Applies an impulse at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `impulse`: the world-space impulse to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
pub fn rbApplyImpulse(&mut self, handle: FlatHandle, impulse: &RawVector, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.apply_impulse(impulse.0, wakeUp);
})
}
/// Adds a torque at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `torque`: the torque to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
#[cfg(feature = "dim2")]
pub fn rbAddTorque(&mut self, handle: FlatHandle, torque: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.add_torque(torque, wakeUp);
})
}
/// Adds a torque at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `torque`: the world-space torque to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
#[cfg(feature = "dim3")]
pub fn rbAddTorque(&mut self, handle: FlatHandle, torque: &RawVector, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.add_torque(torque.0, wakeUp);
})
}
/// Applies an impulsive torque at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `torque impulse`: the torque impulse to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
#[cfg(feature = "dim2")]
pub fn rbApplyTorqueImpulse(&mut self, handle: FlatHandle, torque_impulse: f32, wakeUp: bool) {
self.map_mut(handle, |rb| {
rb.apply_torque_impulse(torque_impulse, wakeUp);
})
}
/// Applies an impulsive torque at the center-of-mass of this rigid-body.
///
/// # Parameters
/// - `torque impulse`: the world-space torque impulse to apply on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
#[cfg(feature = "dim3")]
pub fn rbApplyTorqueImpulse(
&mut self,
handle: FlatHandle,
torque_impulse: &RawVector,
wakeUp: bool,
) {
self.map_mut(handle, |rb| {
rb.apply_torque_impulse(torque_impulse.0, wakeUp);
})
}
/// Adds a force at the given world-space point of this rigid-body.
///
/// # Parameters
/// - `force`: the world-space force to apply on the rigid-body.
/// - `point`: the world-space point where the impulse is to be applied on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
pub fn rbAddForceAtPoint(
&mut self,
handle: FlatHandle,
force: &RawVector,
point: &RawVector,
wakeUp: bool,
) {
self.map_mut(handle, |rb| {
rb.add_force_at_point(force.0, point.0.into(), wakeUp);
})
}
/// Applies an impulse at the given world-space point of this rigid-body.
///
/// # Parameters
/// - `impulse`: the world-space impulse to apply on the rigid-body.
/// - `point`: the world-space point where the impulse is to be applied on the rigid-body.
/// - `wakeUp`: should the rigid-body be automatically woken-up?
pub fn rbApplyImpulseAtPoint(
&mut self,
handle: FlatHandle,
impulse: &RawVector,
point: &RawVector,
wakeUp: bool,
) {
self.map_mut(handle, |rb| {
rb.apply_impulse_at_point(impulse.0, point.0.into(), wakeUp);
})
}
pub fn rbAdditionalSolverIterations(&self, handle: FlatHandle) -> usize {
self.map(handle, |rb| rb.additional_solver_iterations())
}
pub fn rbSetAdditionalSolverIterations(&mut self, handle: FlatHandle, iters: usize) {
self.map_mut(handle, |rb| {
rb.set_additional_solver_iterations(iters as usize);
})
}
/// An arbitrary user-defined 32-bit integer
pub fn rbUserData(&self, handle: FlatHandle) -> u32 {
self.map(handle, |rb| rb.user_data as u32)
}
/// Sets the user-defined 32-bit integer of this rigid-body.
///
/// # Parameters
/// - `data`: an arbitrary user-defined 32-bit integer.
pub fn rbSetUserData(&mut self, handle: FlatHandle, data: u32) {
self.map_mut(handle, |rb| {
rb.user_data = data as u128;
})
}
/// Retrieves the constant force(s) the user added to this rigid-body.
/// Returns zero if the rigid-body is not dynamic.
pub fn rbUserForce(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| rb.user_force().into())
}
/// Retrieves the constant torque(s) the user added to this rigid-body.
/// Returns zero if the rigid-body is not dynamic.
#[cfg(feature = "dim2")]
pub fn rbUserTorque(&self, handle: FlatHandle) -> f32 {
self.map(handle, |rb| rb.user_torque())
}
/// Retrieves the constant torque(s) the user added to this rigid-body.
/// Returns zero if the rigid-body is not dynamic.
#[cfg(feature = "dim3")]
pub fn rbUserTorque(&self, handle: FlatHandle) -> RawVector {
self.map(handle, |rb| rb.user_torque().into())
}
}

View File

@@ -0,0 +1,237 @@
use crate::dynamics::{RawImpulseJointSet, RawIslandManager, RawMultibodyJointSet};
use crate::geometry::RawColliderSet;
use crate::math::{RawRotation, RawVector};
use crate::utils::{self, FlatHandle};
use rapier::dynamics::{MassProperties, RigidBody, RigidBodyBuilder, RigidBodySet, RigidBodyType};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub enum RawRigidBodyType {
Dynamic,
Fixed,
KinematicPositionBased,
KinematicVelocityBased,
}
impl Into<RigidBodyType> for RawRigidBodyType {
fn into(self) -> RigidBodyType {
match self {
RawRigidBodyType::Dynamic => RigidBodyType::Dynamic,
RawRigidBodyType::Fixed => RigidBodyType::Fixed,
RawRigidBodyType::KinematicPositionBased => RigidBodyType::KinematicPositionBased,
RawRigidBodyType::KinematicVelocityBased => RigidBodyType::KinematicVelocityBased,
}
}
}
impl Into<RawRigidBodyType> for RigidBodyType {
fn into(self) -> RawRigidBodyType {
match self {
RigidBodyType::Dynamic => RawRigidBodyType::Dynamic,
RigidBodyType::Fixed => RawRigidBodyType::Fixed,
RigidBodyType::KinematicPositionBased => RawRigidBodyType::KinematicPositionBased,
RigidBodyType::KinematicVelocityBased => RawRigidBodyType::KinematicVelocityBased,
}
}
}
#[wasm_bindgen]
pub struct RawRigidBodySet(pub(crate) RigidBodySet);
impl RawRigidBodySet {
pub(crate) fn map<T>(&self, handle: FlatHandle, f: impl FnOnce(&RigidBody) -> T) -> T {
let body = self.0.get(utils::body_handle(handle)).expect(
"Invalid RigidBody reference. It may have been removed from the physics World.",
);
f(body)
}
pub(crate) fn map_mut<T>(
&mut self,
handle: FlatHandle,
f: impl FnOnce(&mut RigidBody) -> T,
) -> T {
let body = self.0.get_mut(utils::body_handle(handle)).expect(
"Invalid RigidBody reference. It may have been removed from the physics World.",
);
f(body)
}
}
#[wasm_bindgen]
impl RawRigidBodySet {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawRigidBodySet(RigidBodySet::new())
}
#[cfg(feature = "dim3")]
pub fn createRigidBody(
&mut self,
enabled: bool,
translation: &RawVector,
rotation: &RawRotation,
gravityScale: f32,
mass: f32,
massOnly: bool,
centerOfMass: &RawVector,
linvel: &RawVector,
angvel: &RawVector,
principalAngularInertia: &RawVector,
angularInertiaFrame: &RawRotation,
translationEnabledX: bool,
translationEnabledY: bool,
translationEnabledZ: bool,
rotationEnabledX: bool,
rotationEnabledY: bool,
rotationEnabledZ: bool,
linearDamping: f32,
angularDamping: f32,
rb_type: RawRigidBodyType,
canSleep: bool,
sleeping: bool,
softCcdPrediction: f32,
ccdEnabled: bool,
dominanceGroup: i8,
additional_solver_iterations: usize,
) -> FlatHandle {
let pos = na::Isometry3::from_parts(translation.0.into(), rotation.0);
let mut rigid_body = RigidBodyBuilder::new(rb_type.into())
.enabled(enabled)
.pose(pos)
.gravity_scale(gravityScale)
.enabled_translations(
translationEnabledX,
translationEnabledY,
translationEnabledZ,
)
.enabled_rotations(rotationEnabledX, rotationEnabledY, rotationEnabledZ)
.linvel(linvel.0)
.angvel(angvel.0)
.linear_damping(linearDamping)
.angular_damping(angularDamping)
.can_sleep(canSleep)
.sleeping(sleeping)
.ccd_enabled(ccdEnabled)
.dominance_group(dominanceGroup)
.additional_solver_iterations(additional_solver_iterations)
.soft_ccd_prediction(softCcdPrediction);
rigid_body = if massOnly {
rigid_body.additional_mass(mass)
} else {
let props = MassProperties::with_principal_inertia_frame(
centerOfMass.0.into(),
mass,
principalAngularInertia.0,
angularInertiaFrame.0,
);
rigid_body.additional_mass_properties(props)
};
utils::flat_handle(self.0.insert(rigid_body.build()).0)
}
#[cfg(feature = "dim2")]
pub fn createRigidBody(
&mut self,
enabled: bool,
translation: &RawVector,
rotation: &RawRotation,
gravityScale: f32,
mass: f32,
massOnly: bool,
centerOfMass: &RawVector,
linvel: &RawVector,
angvel: f32,
principalAngularInertia: f32,
translationEnabledX: bool,
translationEnabledY: bool,
rotationsEnabled: bool,
linearDamping: f32,
angularDamping: f32,
rb_type: RawRigidBodyType,
canSleep: bool,
sleeping: bool,
softCcdPrediciton: f32,
ccdEnabled: bool,
dominanceGroup: i8,
additional_solver_iterations: usize,
) -> FlatHandle {
let pos = na::Isometry2::from_parts(translation.0.into(), rotation.0);
let mut rigid_body = RigidBodyBuilder::new(rb_type.into())
.enabled(enabled)
.pose(pos)
.gravity_scale(gravityScale)
.enabled_translations(translationEnabledX, translationEnabledY)
.linvel(linvel.0)
.angvel(angvel)
.linear_damping(linearDamping)
.angular_damping(angularDamping)
.can_sleep(canSleep)
.sleeping(sleeping)
.ccd_enabled(ccdEnabled)
.dominance_group(dominanceGroup)
.additional_solver_iterations(additional_solver_iterations)
.soft_ccd_prediction(softCcdPrediciton);
rigid_body = if massOnly {
rigid_body.additional_mass(mass)
} else {
let props = MassProperties::new(centerOfMass.0.into(), mass, principalAngularInertia);
rigid_body.additional_mass_properties(props)
};
if !rotationsEnabled {
rigid_body = rigid_body.lock_rotations();
}
utils::flat_handle(self.0.insert(rigid_body.build()).0)
}
pub fn remove(
&mut self,
handle: FlatHandle,
islands: &mut RawIslandManager,
colliders: &mut RawColliderSet,
joints: &mut RawImpulseJointSet,
articulations: &mut RawMultibodyJointSet,
) {
let handle = utils::body_handle(handle);
self.0.remove(
handle,
&mut islands.0,
&mut colliders.0,
&mut joints.0,
&mut articulations.0,
true,
);
}
/// The number of rigid-bodies on this set.
pub fn len(&self) -> usize {
self.0.len()
}
/// Checks if a rigid-body with the given integer handle exists.
pub fn contains(&self, handle: FlatHandle) -> bool {
self.0.get(utils::body_handle(handle)).is_some()
}
/// Applies the given JavaScript function to the integer handle of each rigid-body managed by this set.
///
/// # Parameters
/// - `f(handle)`: the function to apply to the integer handle of each rigid-body managed by this set. Called as `f(collider)`.
pub fn forEachRigidBodyHandle(&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)));
}
}
pub fn propagateModifiedBodyPositionsToColliders(&mut self, colliders: &mut RawColliderSet) {
self.0
.propagate_modified_body_positions_to_colliders(&mut colliders.0);
}
}

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()
}
}

32
thirdparty/rapier.js/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
//! # Rapier
//! Fast and deterministic WASM physics engine.
#![allow(non_snake_case)] // JS uses camelCase, so we will follow its convention for the generated bindings.
// #![deny(missing_docs)]
extern crate nalgebra as na;
#[cfg(feature = "dim2")]
extern crate rapier2d as rapier;
#[cfg(feature = "dim3")]
extern crate rapier3d as rapier;
#[macro_use]
extern crate serde;
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn reserve_memory(extra_bytes_count: u32) {
let mut unused: Vec<u8> = vec![];
unused.reserve(extra_bytes_count as usize);
std::hint::black_box(&unused);
}
pub mod control;
pub mod dynamics;
pub mod geometry;
pub mod math;
pub mod pipeline;
pub mod utils;

266
thirdparty/rapier.js/src/math.rs vendored Normal file
View File

@@ -0,0 +1,266 @@
//! Linear algebra primitives.
#[cfg(feature = "dim3")]
use js_sys::Float32Array;
#[cfg(feature = "dim3")]
use na::{Quaternion, Unit};
#[cfg(feature = "dim3")]
use rapier::math::Real;
use rapier::math::{Point, Rotation, Vector};
#[cfg(feature = "dim3")]
use rapier::parry::utils::SdpMatrix3;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[repr(transparent)]
#[derive(Copy, Clone)]
/// A rotation quaternion.
pub struct RawRotation(pub(crate) Rotation<f32>);
impl From<Rotation<f32>> for RawRotation {
fn from(v: Rotation<f32>) -> Self {
RawRotation(v)
}
}
#[wasm_bindgen]
#[cfg(feature = "dim2")]
/// A unit complex number describing the orientation of a Rapier entity.
impl RawRotation {
/// The identity rotation.
pub fn identity() -> Self {
Self(Rotation::identity())
}
/// The rotation with thegiven angle.
pub fn fromAngle(angle: f32) -> Self {
Self(Rotation::new(angle))
}
/// The imaginary part of this complex number.
#[wasm_bindgen(getter)]
pub fn im(&self) -> f32 {
self.0.im
}
/// The real part of this complex number.
#[wasm_bindgen(getter)]
pub fn re(&self) -> f32 {
self.0.re
}
/// The rotation angle in radians.
#[wasm_bindgen(getter)]
pub fn angle(&self) -> f32 {
self.0.angle()
}
}
#[wasm_bindgen]
#[cfg(feature = "dim3")]
/// A unit quaternion describing the orientation of a Rapier entity.
impl RawRotation {
#[wasm_bindgen(constructor)]
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
RawRotation(Unit::new_unchecked(Quaternion::new(w, x, y, z)))
}
/// The identity quaternion.
pub fn identity() -> Self {
Self(Rotation::identity())
}
/// The `x` component of this quaternion.
#[wasm_bindgen(getter)]
pub fn x(&self) -> f32 {
self.0.i
}
/// The `y` component of this quaternion.
#[wasm_bindgen(getter)]
pub fn y(&self) -> f32 {
self.0.j
}
/// The `z` component of this quaternion.
#[wasm_bindgen(getter)]
pub fn z(&self) -> f32 {
self.0.k
}
/// The `w` component of this quaternion.
#[wasm_bindgen(getter)]
pub fn w(&self) -> f32 {
self.0.w
}
}
#[wasm_bindgen]
#[repr(transparent)]
#[derive(Copy, Clone)]
/// A vector.
pub struct RawVector(pub(crate) Vector<f32>);
impl From<Vector<f32>> for RawVector {
fn from(v: Vector<f32>) -> Self {
RawVector(v)
}
}
impl From<Point<f32>> for RawVector {
fn from(pt: Point<f32>) -> Self {
pt.coords.into()
}
}
#[wasm_bindgen]
impl RawVector {
/// Creates a new vector filled with zeros.
pub fn zero() -> Self {
Self(Vector::zeros())
}
/// Creates a new 2D vector from its two components.
///
/// # Parameters
/// - `x`: the `x` component of this 2D vector.
/// - `y`: the `y` component of this 2D vector.
#[cfg(feature = "dim2")]
#[wasm_bindgen(constructor)]
pub fn new(x: f32, y: f32) -> Self {
Self(Vector::new(x, y))
}
/// Creates a new 3D vector from its two components.
///
/// # Parameters
/// - `x`: the `x` component of this 3D vector.
/// - `y`: the `y` component of this 3D vector.
/// - `z`: the `z` component of this 3D vector.
#[cfg(feature = "dim3")]
#[wasm_bindgen(constructor)]
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self(Vector::new(x, y, z))
}
/// The `x` component of this vector.
#[wasm_bindgen(getter)]
pub fn x(&self) -> f32 {
self.0.x
}
/// Sets the `x` component of this vector.
#[wasm_bindgen(setter)]
pub fn set_x(&mut self, x: f32) {
self.0.x = x
}
/// The `y` component of this vector.
#[wasm_bindgen(getter)]
pub fn y(&self) -> f32 {
self.0.y
}
/// Sets the `y` component of this vector.
#[wasm_bindgen(setter)]
pub fn set_y(&mut self, y: f32) {
self.0.y = y
}
/// The `z` component of this vector.
#[cfg(feature = "dim3")]
#[wasm_bindgen(getter)]
pub fn z(&self) -> f32 {
self.0.z
}
/// Sets the `z` component of this vector.
#[cfg(feature = "dim3")]
#[wasm_bindgen(setter)]
pub fn set_z(&mut self, z: f32) {
self.0.z = z
}
/// Create a new 2D vector from this vector with its components rearranged as `{x, y}`.
#[cfg(feature = "dim2")]
pub fn xy(&self) -> Self {
Self(self.0.xy())
}
/// Create a new 2D vector from this vector with its components rearranged as `{y, x}`.
#[cfg(feature = "dim2")]
pub fn yx(&self) -> Self {
Self(self.0.yx())
}
/// Create a new 2D vector from this vector with its components rearranged as `{z, y}`.
#[cfg(feature = "dim2")]
#[cfg(feature = "dim3")]
pub fn zy(&self) -> Self {
Self(self.0.zy())
}
/// Create a new 3D vector from this vector with its components rearranged as `{x, y, z}`.
///
/// This will effectively return a copy of `this`. This method exist for completeness with the
/// other swizzling functions.
#[cfg(feature = "dim3")]
pub fn xyz(&self) -> Self {
Self(self.0.xyz())
}
/// Create a new 3D vector from this vector with its components rearranged as `{y, x, z}`.
#[cfg(feature = "dim3")]
pub fn yxz(&self) -> Self {
Self(self.0.yxz())
}
/// Create a new 3D vector from this vector with its components rearranged as `{z, x, y}`.
#[cfg(feature = "dim3")]
pub fn zxy(&self) -> Self {
Self(self.0.zxy())
}
/// Create a new 3D vector from this vector with its components rearranged as `{x, z, y}`.
#[cfg(feature = "dim3")]
pub fn xzy(&self) -> Self {
Self(self.0.xzy())
}
/// Create a new 3D vector from this vector with its components rearranged as `{y, z, x}`.
#[cfg(feature = "dim3")]
pub fn yzx(&self) -> Self {
Self(self.0.yzx())
}
/// Create a new 3D vector from this vector with its components rearranged as `{z, y, x}`.
#[cfg(feature = "dim3")]
pub fn zyx(&self) -> Self {
Self(self.0.zyx())
}
}
#[wasm_bindgen]
#[repr(transparent)]
#[derive(Copy, Clone)]
#[cfg(feature = "dim3")]
pub struct RawSdpMatrix3(pub(crate) SdpMatrix3<Real>);
#[cfg(feature = "dim3")]
impl From<SdpMatrix3<Real>> for RawSdpMatrix3 {
fn from(v: SdpMatrix3<Real>) -> Self {
RawSdpMatrix3(v)
}
}
#[wasm_bindgen]
#[cfg(feature = "dim3")]
impl RawSdpMatrix3 {
/// Row major list of the upper-triangular part of the symmetric matrix.
pub fn elements(&self) -> Float32Array {
let m = self.0;
let output = Float32Array::new_with_length(6);
output.copy_from(&[m.m11, m.m12, m.m13, m.m22, m.m23, m.m33]);
output
}
}

View File

@@ -0,0 +1,152 @@
use crate::dynamics::{RawImpulseJointSet, RawMultibodyJointSet, RawRigidBodySet};
use crate::geometry::{RawColliderSet, RawNarrowPhase};
use js_sys::Float32Array;
use palette::convert::IntoColorUnclamped;
use palette::rgb::Rgba;
use palette::Hsla;
use rapier::dynamics::{RigidBody, RigidBodySet};
use rapier::geometry::ColliderSet;
use rapier::math::{Point, Real};
use rapier::pipeline::{DebugRenderBackend, DebugRenderObject, DebugRenderPipeline};
use rapier::prelude::{QueryFilter, QueryFilterFlags};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawDebugRenderPipeline {
pub(crate) raw: DebugRenderPipeline,
vertices: Vec<f32>,
colors: Vec<f32>,
}
#[wasm_bindgen]
impl RawDebugRenderPipeline {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawDebugRenderPipeline {
raw: DebugRenderPipeline::default(),
vertices: vec![],
colors: vec![],
}
}
pub fn vertices(&self) -> Float32Array {
let output = Float32Array::new_with_length(self.vertices.len() as u32);
output.copy_from(&self.vertices);
output
}
pub fn colors(&self) -> Float32Array {
let output = Float32Array::new_with_length(self.colors.len() as u32);
output.copy_from(&self.colors);
output
}
pub fn render(
&mut self,
bodies: &RawRigidBodySet,
colliders: &RawColliderSet,
impulse_joints: &RawImpulseJointSet,
multibody_joints: &RawMultibodyJointSet,
narrow_phase: &RawNarrowPhase,
filter_flags: u32,
filter_predicate: &js_sys::Function,
) {
self.vertices.clear();
self.colors.clear();
crate::utils::with_filter(filter_predicate, |predicate| {
let mut backend = CopyToBuffersBackend {
filter: QueryFilter {
flags: QueryFilterFlags::from_bits(filter_flags)
.unwrap_or(QueryFilterFlags::empty()),
groups: None,
exclude_collider: None,
exclude_rigid_body: None,
predicate,
},
bodies: &bodies.0,
colliders: &colliders.0,
vertices: &mut self.vertices,
colors: &mut self.colors,
};
self.raw.render(
&mut backend,
&bodies.0,
&colliders.0,
&impulse_joints.0,
&multibody_joints.0,
&narrow_phase.0,
)
})
}
}
struct CopyToBuffersBackend<'a> {
filter: QueryFilter<'a>,
bodies: &'a RigidBodySet,
colliders: &'a ColliderSet,
vertices: &'a mut Vec<f32>,
colors: &'a mut Vec<f32>,
}
impl<'a> DebugRenderBackend for CopyToBuffersBackend<'a> {
fn filter_object(&self, object: DebugRenderObject) -> bool {
let test_rigid_body = |rb: &RigidBody| {
rb.colliders().iter().all(|handle| {
let Some(co) = self.colliders.get(*handle) else {
return false;
};
self.filter.test(self.bodies, *handle, co)
})
};
match object {
DebugRenderObject::Collider(handle, co)
| DebugRenderObject::ColliderAabb(handle, co, _) => {
self.filter.test(self.bodies, handle, co)
}
DebugRenderObject::ContactPair(pair, co1, co2) => {
self.filter.test(self.bodies, pair.collider1, co1)
&& self.filter.test(self.bodies, pair.collider2, co2)
}
DebugRenderObject::ImpulseJoint(_, joint) => {
let Some(rb1) = self.bodies.get(joint.body1) else {
return false;
};
let Some(rb2) = self.bodies.get(joint.body2) else {
return false;
};
test_rigid_body(rb1) && test_rigid_body(rb2)
}
DebugRenderObject::MultibodyJoint(_, _, link) => {
let Some(rb) = self.bodies.get(link.rigid_body_handle()) else {
return false;
};
test_rigid_body(rb)
}
DebugRenderObject::RigidBody(_, rb) => test_rigid_body(rb),
}
}
/// Draws a colored line.
///
/// Note that this method can be called multiple time for the same `object`.
fn draw_line(
&mut self,
_object: DebugRenderObject,
a: Point<Real>,
b: Point<Real>,
color: [f32; 4],
) {
self.vertices.extend_from_slice(a.coords.as_slice());
self.vertices.extend_from_slice(b.coords.as_slice());
// Convert to RGB which will be easier to handle in JS.
let hsl = Hsla::new(color[0], color[1], color[2], color[3]);
let rgb: Rgba = hsl.into_color_unclamped();
self.colors.extend_from_slice(&[
rgb.red, rgb.green, rgb.blue, rgb.alpha, rgb.red, rgb.green, rgb.blue, rgb.alpha,
]);
}
}

View File

@@ -0,0 +1,140 @@
use crate::math::RawVector;
use crate::utils;
use crate::utils::FlatHandle;
use rapier::geometry::{CollisionEvent, ContactForceEvent};
use rapier::pipeline::ChannelEventCollector;
use std::sync::mpsc::Receiver;
use wasm_bindgen::prelude::*;
/// A structure responsible for collecting events generated
/// by the physics engine.
#[wasm_bindgen]
pub struct RawEventQueue {
pub(crate) collector: ChannelEventCollector,
collision_events: Receiver<CollisionEvent>,
contact_force_events: Receiver<ContactForceEvent>,
pub(crate) auto_drain: bool,
}
#[wasm_bindgen]
pub struct RawContactForceEvent(ContactForceEvent);
#[wasm_bindgen]
impl RawContactForceEvent {
/// The first collider involved in the contact.
pub fn collider1(&self) -> FlatHandle {
crate::utils::flat_handle(self.0.collider1.0)
}
/// The second collider involved in the contact.
pub fn collider2(&self) -> FlatHandle {
crate::utils::flat_handle(self.0.collider2.0)
}
/// The sum of all the forces between the two colliders.
pub fn total_force(&self) -> RawVector {
RawVector(self.0.total_force)
}
/// The sum of the magnitudes of each force between the two colliders.
///
/// Note that this is **not** the same as the magnitude of `self.total_force`.
/// Here we are summing the magnitude of all the forces, instead of taking
/// the magnitude of their sum.
pub fn total_force_magnitude(&self) -> f32 {
self.0.total_force_magnitude
}
/// The world-space (unit) direction of the force with strongest magnitude.
pub fn max_force_direction(&self) -> RawVector {
RawVector(self.0.max_force_direction)
}
/// The magnitude of the largest force at a contact point of this contact pair.
pub fn max_force_magnitude(&self) -> f32 {
self.0.max_force_magnitude
}
}
// #[wasm_bindgen]
// /// The proximity state of a sensor collider and another collider.
// pub enum RawIntersection {
// /// The sensor is intersecting the other collider.
// Intersecting = 0,
// /// The sensor is within tolerance margin of the other collider.
// WithinMargin = 1,
// /// The sensor is disjoint from the other collider.
// Disjoint = 2,
// }
#[wasm_bindgen]
impl RawEventQueue {
/// Creates a new event collector.
///
/// # Parameters
/// - `autoDrain`: setting this to `true` is strongly recommended. If true, the collector will
/// be automatically drained before each `world.step(collector)`. If false, the collector will
/// keep all events in memory unless it is manually drained/cleared; this may lead to unbounded use of
/// RAM if no drain is performed.
#[wasm_bindgen(constructor)]
pub fn new(autoDrain: bool) -> Self {
let collision_channel = std::sync::mpsc::channel();
let contact_force_channel = std::sync::mpsc::channel();
let collector = ChannelEventCollector::new(collision_channel.0, contact_force_channel.0);
Self {
collector,
collision_events: collision_channel.1,
contact_force_events: contact_force_channel.1,
auto_drain: autoDrain,
}
}
/// Applies the given javascript closure on each collision event of this collector, then clear
/// the internal collision event buffer.
///
/// # Parameters
/// - `f(handle1, handle2, started)`: JavaScript closure applied to each collision event. The
/// closure should take three arguments: two integers representing the handles of the colliders
/// involved in the collision, and a boolean indicating if the collision started (true) or stopped
/// (false).
pub fn drainCollisionEvents(&mut self, f: &js_sys::Function) {
let this = JsValue::null();
while let Ok(event) = self.collision_events.try_recv() {
match event {
CollisionEvent::Started(co1, co2, _) => {
let h1 = utils::flat_handle(co1.0);
let h2 = utils::flat_handle(co2.0);
let _ = f.call3(
&this,
&JsValue::from(h1),
&JsValue::from(h2),
&JsValue::from_bool(true),
);
}
CollisionEvent::Stopped(co1, co2, _) => {
let h1 = utils::flat_handle(co1.0);
let h2 = utils::flat_handle(co2.0);
let _ = f.call3(
&this,
&JsValue::from(h1),
&JsValue::from(h2),
&JsValue::from_bool(false),
);
}
}
}
}
pub fn drainContactForceEvents(&mut self, f: &js_sys::Function) {
let this = JsValue::null();
while let Ok(event) = self.contact_force_events.try_recv() {
let _ = f.call1(&this, &JsValue::from(RawContactForceEvent(event)));
}
}
/// Removes all events contained by this collector.
pub fn clear(&self) {
while let Ok(_) = self.collision_events.try_recv() {}
}
}

View File

@@ -0,0 +1,11 @@
pub use self::debug_render_pipeline::*;
pub use self::event_queue::*;
pub use self::physics_hooks::*;
pub use self::physics_pipeline::*;
pub use self::serialization_pipeline::*;
mod debug_render_pipeline;
mod event_queue;
mod physics_hooks;
mod physics_pipeline;
mod serialization_pipeline;

View File

@@ -0,0 +1,217 @@
use crate::utils;
use rapier::geometry::SolverFlags;
use rapier::pipeline::{ContactModificationContext, PairFilterContext, PhysicsHooks};
use wasm_bindgen::prelude::*;
pub struct RawPhysicsHooks {
pub this: js_sys::Object,
pub filter_contact_pair: js_sys::Function,
pub filter_intersection_pair: js_sys::Function,
// pub modify_solver_contacts: &'a js_sys::Function,
}
// HACK: the RawPhysicsHooks is no longer Send+Sync because the JS objects are
// no longer Send+Sync since https://github.com/rustwasm/wasm-bindgen/pull/955
// As far as this is confined to the bindings this should be fine since we
// never use threading in wasm.
unsafe impl Send for RawPhysicsHooks {}
unsafe impl Sync for RawPhysicsHooks {}
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
impl PhysicsHooks for RawPhysicsHooks {
fn filter_contact_pair(&self, ctxt: &PairFilterContext) -> Option<SolverFlags> {
let rb1 = ctxt
.rigid_body1
.map(|rb| JsValue::from(utils::flat_handle(rb.0)))
.unwrap_or(JsValue::NULL);
let rb2 = ctxt
.rigid_body2
.map(|rb| JsValue::from(utils::flat_handle(rb.0)))
.unwrap_or(JsValue::NULL);
let result = self
.filter_contact_pair
.bind2(
&self.this,
&JsValue::from(utils::flat_handle(ctxt.collider1.0)),
&JsValue::from(utils::flat_handle(ctxt.collider2.0)),
)
.call2(&self.this, &rb1, &rb2)
.ok()?;
let flags = result.as_f64()?;
// TODO: not sure exactly why we have to do `flags as u32` instead
// of `flags.to_bits() as u32`.
SolverFlags::from_bits(flags as u32)
}
fn filter_intersection_pair(&self, ctxt: &PairFilterContext) -> bool {
let rb1 = ctxt
.rigid_body1
.map(|rb| JsValue::from(utils::flat_handle(rb.0)))
.unwrap_or(JsValue::NULL);
let rb2 = ctxt
.rigid_body2
.map(|rb| JsValue::from(utils::flat_handle(rb.0)))
.unwrap_or(JsValue::NULL);
self.filter_intersection_pair
.bind2(
&self.this,
&JsValue::from(utils::flat_handle(ctxt.collider1.0)),
&JsValue::from(utils::flat_handle(ctxt.collider2.0)),
)
.call2(&self.this, &rb1, &rb2)
.ok()
.and_then(|res| res.as_bool())
.unwrap_or(false)
}
fn modify_solver_contacts(&self, _ctxt: &mut ContactModificationContext) {}
}
/* NOTE: the following is an attempt to make contact modification work.
*
#[wasm_bindgen]
#[derive(Copy, Clone, Debug)]
pub struct RawContactManifold(*const ContactManifold);
pub struct RawSolverContact(*const SolverContact);
#[wasm_bindgen]
pub struct RawContactModificationContext {
pub collider1: u32,
pub collider2: u32,
pub rigid_body1: Option<u32>,
pub rigid_body2: Option<u32>,
pub manifold: *const ContactManifold,
pub solver_contacts: *mut Vec<SolverContact>,
normal: *mut Vector<Real>,
user_data: *mut u32,
}
#[wasm_bindgen]
impl RawContactModificationContext {
pub fn collider1(&self) -> u32 {
self.collider1
}
pub fn collider2(&self) -> u32 {
self.collider2
}
#[wasm_bindgen(getter)]
pub fn normal(&self) -> RawVector {
unsafe { RawVector(*self.normal) }
}
#[wasm_bindgen(setter)]
pub fn set_normal(&mut self, normal: RawVector) {
unsafe {
*self.normal = normal.0;
}
}
#[wasm_bindgen(getter)]
pub fn user_data(&self) -> u32 {
unsafe { *self.user_data }
}
#[wasm_bindgen(setter)]
pub fn set_user_data(&mut self, user_data: u32) {
unsafe {
*self.user_data = user_data;
}
}
pub fn num_solver_contacts(&self) -> usize {
unsafe { (*self.solver_contacts).len() }
}
pub fn clear_solver_contacts(&mut self) {
unsafe { (*self.solver_contacts).clear() }
}
pub fn remove_solver_contact(&mut self, i: usize) {
unsafe {
if i < self.num_solver_contacts() {
(*self.solver_contacts).swap_remove(i);
}
}
}
pub fn solver_contact_point(&self, i: usize) -> Option<RawVector> {
unsafe {
(*self.solver_contacts)
.get(i)
.map(|c| c.point.coords.into())
}
}
pub fn set_solver_contact_point(&mut self, i: usize, pt: &RawVector) {
unsafe {
if let Some(c) = (*self.solver_contacts).get_mut(i) {
c.point = pt.0.into()
}
}
}
pub fn solver_contact_dist(&self, i: usize) -> Real {
unsafe {
(*self.solver_contacts)
.get(i)
.map(|c| c.dist)
.unwrap_or(0.0)
}
}
pub fn set_solver_contact_dist(&mut self, i: usize, dist: Real) {
unsafe {
if let Some(c) = (*self.solver_contacts).get_mut(i) {
c.dist = dist
}
}
}
pub fn solver_contact_friction(&self, i: usize) -> Real {
unsafe { (*self.solver_contacts)[i].friction }
}
pub fn set_solver_contact_friction(&mut self, i: usize, friction: Real) {
unsafe {
if let Some(c) = (*self.solver_contacts).get_mut(i) {
c.friction = friction
}
}
}
pub fn solver_contact_restitution(&self, i: usize) -> Real {
unsafe { (*self.solver_contacts)[i].restitution }
}
pub fn set_solver_contact_restitution(&mut self, i: usize, restitution: Real) {
unsafe {
if let Some(c) = (*self.solver_contacts).get_mut(i) {
c.restitution = restitution
}
}
}
pub fn solver_contact_tangent_velocity(&self, i: usize) -> RawVector {
unsafe { (*self.solver_contacts)[i].tangent_velocity.into() }
}
pub fn set_solver_contact_tangent_velocity(&mut self, i: usize, vel: &RawVector) {
unsafe {
if let Some(c) = (*self.solver_contacts).get_mut(i) {
c.tangent_velocity = vel.0.into()
}
}
}
}
*/

View File

@@ -0,0 +1,170 @@
use crate::dynamics::{
RawCCDSolver, RawImpulseJointSet, RawIntegrationParameters, RawIslandManager,
RawMultibodyJointSet, RawRigidBodySet,
};
use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase};
use crate::math::RawVector;
use crate::pipeline::{RawEventQueue, RawPhysicsHooks};
use crate::rapier::pipeline::PhysicsPipeline;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RawPhysicsPipeline(pub(crate) PhysicsPipeline);
#[wasm_bindgen]
impl RawPhysicsPipeline {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
let mut pipeline = PhysicsPipeline::new();
pipeline.counters.disable(); // Disable perf counters by default.
RawPhysicsPipeline(pipeline)
}
pub fn set_profiler_enabled(&mut self, enabled: bool) {
if enabled {
self.0.counters.enable();
} else {
self.0.counters.disable();
}
}
pub fn is_profiler_enabled(&self) -> bool {
self.0.counters.enabled()
}
pub fn timing_step(&self) -> f64 {
self.0.counters.step_time_ms()
}
pub fn timing_collision_detection(&self) -> f64 {
self.0.counters.collision_detection_time_ms()
}
pub fn timing_broad_phase(&self) -> f64 {
self.0.counters.broad_phase_time_ms()
}
pub fn timing_narrow_phase(&self) -> f64 {
self.0.counters.narrow_phase_time_ms()
}
pub fn timing_solver(&self) -> f64 {
self.0.counters.solver_time_ms()
}
pub fn timing_velocity_assembly(&self) -> f64 {
self.0.counters.solver.velocity_assembly_time.time_ms()
}
pub fn timing_velocity_resolution(&self) -> f64 {
self.0.counters.velocity_resolution_time_ms()
}
pub fn timing_velocity_update(&self) -> f64 {
self.0.counters.velocity_update_time_ms()
}
pub fn timing_velocity_writeback(&self) -> f64 {
self.0.counters.solver.velocity_writeback_time.time_ms()
}
pub fn timing_ccd(&self) -> f64 {
self.0.counters.ccd_time_ms()
}
pub fn timing_ccd_toi_computation(&self) -> f64 {
self.0.counters.ccd.toi_computation_time.time_ms()
}
pub fn timing_ccd_broad_phase(&self) -> f64 {
self.0.counters.ccd.broad_phase_time.time_ms()
}
pub fn timing_ccd_narrow_phase(&self) -> f64 {
self.0.counters.ccd.narrow_phase_time.time_ms()
}
pub fn timing_ccd_solver(&self) -> f64 {
self.0.counters.ccd.solver_time.time_ms()
}
pub fn timing_island_construction(&self) -> f64 {
self.0.counters.island_construction_time_ms()
}
pub fn timing_user_changes(&self) -> f64 {
self.0.counters.stages.user_changes.time_ms()
}
pub fn step(
&mut self,
gravity: &RawVector,
integrationParameters: &RawIntegrationParameters,
islands: &mut RawIslandManager,
broadPhase: &mut RawBroadPhase,
narrowPhase: &mut RawNarrowPhase,
bodies: &mut RawRigidBodySet,
colliders: &mut RawColliderSet,
joints: &mut RawImpulseJointSet,
articulations: &mut RawMultibodyJointSet,
ccd_solver: &mut RawCCDSolver,
) {
self.0.step(
&gravity.0,
&integrationParameters.0,
&mut islands.0,
&mut broadPhase.0,
&mut narrowPhase.0,
&mut bodies.0,
&mut colliders.0,
&mut joints.0,
&mut articulations.0,
&mut ccd_solver.0,
&(),
&(),
);
}
pub fn stepWithEvents(
&mut self,
gravity: &RawVector,
integrationParameters: &RawIntegrationParameters,
islands: &mut RawIslandManager,
broadPhase: &mut RawBroadPhase,
narrowPhase: &mut RawNarrowPhase,
bodies: &mut RawRigidBodySet,
colliders: &mut RawColliderSet,
joints: &mut RawImpulseJointSet,
articulations: &mut RawMultibodyJointSet,
ccd_solver: &mut RawCCDSolver,
eventQueue: &mut RawEventQueue,
hookObject: js_sys::Object,
hookFilterContactPair: js_sys::Function,
hookFilterIntersectionPair: js_sys::Function,
) {
if eventQueue.auto_drain {
eventQueue.clear();
}
let hooks = RawPhysicsHooks {
this: hookObject,
filter_contact_pair: hookFilterContactPair,
filter_intersection_pair: hookFilterIntersectionPair,
};
self.0.step(
&gravity.0,
&integrationParameters.0,
&mut islands.0,
&mut broadPhase.0,
&mut narrowPhase.0,
&mut bodies.0,
&mut colliders.0,
&mut joints.0,
&mut articulations.0,
&mut ccd_solver.0,
&hooks,
&eventQueue.collector,
);
}
}

View File

@@ -0,0 +1,145 @@
use crate::dynamics::{
RawImpulseJointSet, RawIntegrationParameters, RawIslandManager, RawMultibodyJointSet,
RawRigidBodySet,
};
use crate::geometry::{RawBroadPhase, RawColliderSet, RawNarrowPhase};
use crate::math::RawVector;
use js_sys::Uint8Array;
use rapier::dynamics::{
ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet,
};
use rapier::geometry::{ColliderSet, DefaultBroadPhase, NarrowPhase};
use rapier::math::Vector;
use wasm_bindgen::prelude::*;
#[derive(Serialize)]
struct SerializableWorld<'a> {
gravity: &'a Vector<f32>,
integration_parameters: &'a IntegrationParameters,
islands: &'a IslandManager,
broad_phase: &'a DefaultBroadPhase,
narrow_phase: &'a NarrowPhase,
bodies: &'a RigidBodySet,
colliders: &'a ColliderSet,
impulse_joints: &'a ImpulseJointSet,
multibody_joints: &'a MultibodyJointSet,
}
#[derive(Deserialize)]
struct DeserializableWorld {
gravity: Vector<f32>,
integration_parameters: IntegrationParameters,
islands: IslandManager,
broad_phase: DefaultBroadPhase,
narrow_phase: NarrowPhase,
bodies: RigidBodySet,
colliders: ColliderSet,
impulse_joints: ImpulseJointSet,
multibody_joints: MultibodyJointSet,
}
#[wasm_bindgen]
pub struct RawDeserializedWorld {
gravity: Option<RawVector>,
integrationParameters: Option<RawIntegrationParameters>,
islands: Option<RawIslandManager>,
broadPhase: Option<RawBroadPhase>,
narrowPhase: Option<RawNarrowPhase>,
bodies: Option<RawRigidBodySet>,
colliders: Option<RawColliderSet>,
impulse_joints: Option<RawImpulseJointSet>,
multibody_joints: Option<RawMultibodyJointSet>,
}
#[wasm_bindgen]
impl RawDeserializedWorld {
pub fn takeGravity(&mut self) -> Option<RawVector> {
self.gravity.take()
}
pub fn takeIntegrationParameters(&mut self) -> Option<RawIntegrationParameters> {
self.integrationParameters.take()
}
pub fn takeIslandManager(&mut self) -> Option<RawIslandManager> {
self.islands.take()
}
pub fn takeBroadPhase(&mut self) -> Option<RawBroadPhase> {
self.broadPhase.take()
}
pub fn takeNarrowPhase(&mut self) -> Option<RawNarrowPhase> {
self.narrowPhase.take()
}
pub fn takeBodies(&mut self) -> Option<RawRigidBodySet> {
self.bodies.take()
}
pub fn takeColliders(&mut self) -> Option<RawColliderSet> {
self.colliders.take()
}
pub fn takeImpulseJoints(&mut self) -> Option<RawImpulseJointSet> {
self.impulse_joints.take()
}
pub fn takeMultibodyJoints(&mut self) -> Option<RawMultibodyJointSet> {
self.multibody_joints.take()
}
}
#[wasm_bindgen]
pub struct RawSerializationPipeline;
#[wasm_bindgen]
impl RawSerializationPipeline {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
RawSerializationPipeline
}
pub fn serializeAll(
&self,
gravity: &RawVector,
integrationParameters: &RawIntegrationParameters,
islands: &RawIslandManager,
broadPhase: &RawBroadPhase,
narrowPhase: &RawNarrowPhase,
bodies: &RawRigidBodySet,
colliders: &RawColliderSet,
impulse_joints: &RawImpulseJointSet,
multibody_joints: &RawMultibodyJointSet,
) -> Option<Uint8Array> {
let to_serialize = SerializableWorld {
gravity: &gravity.0,
integration_parameters: &integrationParameters.0,
islands: &islands.0,
broad_phase: &broadPhase.0,
narrow_phase: &narrowPhase.0,
bodies: &bodies.0,
colliders: &colliders.0,
impulse_joints: &impulse_joints.0,
multibody_joints: &multibody_joints.0,
};
let snap = bincode::serialize(&to_serialize).ok()?;
Some(Uint8Array::from(&snap[..]))
}
pub fn deserializeAll(&self, data: Uint8Array) -> Option<RawDeserializedWorld> {
let data = data.to_vec();
let d: DeserializableWorld = bincode::deserialize(&data[..]).ok()?;
Some(RawDeserializedWorld {
gravity: Some(RawVector(d.gravity)),
integrationParameters: Some(RawIntegrationParameters(d.integration_parameters)),
islands: Some(RawIslandManager(d.islands)),
broadPhase: Some(RawBroadPhase(d.broad_phase)),
narrowPhase: Some(RawNarrowPhase(d.narrow_phase)),
bodies: Some(RawRigidBodySet(d.bodies)),
colliders: Some(RawColliderSet(d.colliders)),
impulse_joints: Some(RawImpulseJointSet(d.impulse_joints)),
multibody_joints: Some(RawMultibodyJointSet(d.multibody_joints)),
})
}
}

79
thirdparty/rapier.js/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,79 @@
use rapier::data::Index;
use rapier::dynamics::{ImpulseJointHandle, MultibodyJointHandle, RigidBodyHandle};
use rapier::geometry::{Collider, ColliderHandle};
use wasm_bindgen::JsValue;
pub type FlatHandle = f64;
#[inline(always)]
pub fn collider_handle(id: FlatHandle) -> ColliderHandle {
ColliderHandle::from_raw_parts(id.to_bits() as u32, (id.to_bits() >> 32) as u32)
}
#[inline(always)]
pub fn body_handle(id: FlatHandle) -> RigidBodyHandle {
RigidBodyHandle::from_raw_parts(id.to_bits() as u32, (id.to_bits() >> 32) as u32)
}
#[inline(always)]
pub fn impulse_joint_handle(id: FlatHandle) -> ImpulseJointHandle {
ImpulseJointHandle::from_raw_parts(id.to_bits() as u32, (id.to_bits() >> 32) as u32)
}
#[inline(always)]
pub fn multibody_joint_handle(id: FlatHandle) -> MultibodyJointHandle {
MultibodyJointHandle::from_raw_parts(id.to_bits() as u32, (id.to_bits() >> 32) as u32)
}
#[inline(always)]
pub fn flat_handle(id: Index) -> FlatHandle {
let (i, g) = id.into_raw_parts();
FlatHandle::from_bits(i as u64 | ((g as u64) << 32))
}
// pub type FlatHandle = u32;
//
// #[inline(always)]
// pub fn collider_handle(id: FlatHandle) -> ColliderHandle {
// ColliderHandle::from_raw_parts(id as u32, (id >> 16) as u32)
// }
//
// #[inline(always)]
// pub fn body_handle(id: FlatHandle) -> RigidBodyHandle {
// RigidBodyHandle::from_raw_parts(id as u32, (id >> 16) as u32)
// }
//
// #[inline(always)]
// pub fn impulse_joint_handle(id: FlatHandle) -> ImpulseJointHandle {
// ImpulseJointHandle::from_raw_parts(id as u32, (id >> 16) as u32)
// }
//
// #[inline(always)]
// pub fn multibody_joint_handle(id: FlatHandle) -> MultibodyJointHandle {
// MultibodyJointHandle::from_raw_parts(id as u32, (id >> 16) as u32)
// }
//
// #[inline(always)]
// pub fn flat_handle(id: Index) -> FlatHandle {
// let (i, g) = id.into_raw_parts();
// i as u32 | ((g as u32) << 16)
// }
#[inline(always)]
pub fn with_filter<T>(
filter: &js_sys::Function,
f: impl FnOnce(Option<&dyn Fn(ColliderHandle, &Collider) -> bool>) -> T,
) -> T {
if filter.is_function() {
let filtercb = move |handle: ColliderHandle, _: &Collider| match filter
.call1(&JsValue::null(), &JsValue::from(flat_handle(handle.0)))
{
Err(_) => true,
Ok(val) => val.as_bool().unwrap_or(true),
};
f(Some(&filtercb))
} else {
f(None)
}
}