feat: 添加跨平台运行时、资产系统和UI适配功能 (#256)

* feat(platform-common): 添加WASM加载器和环境检测API

* feat(rapier2d): 新增Rapier2D WASM绑定包

* feat(physics-rapier2d): 添加跨平台WASM加载器

* feat(asset-system): 添加运行时资产目录和bundle格式

* feat(asset-system-editor): 新增编辑器资产管理包

* feat(editor-core): 添加构建系统和模块管理

* feat(editor-app): 重构浏览器预览使用import maps

* feat(platform-web): 添加BrowserRuntime和资产读取

* feat(engine): 添加材质系统和着色器管理

* feat(material): 新增材质系统和着色器编辑器

* feat(tilemap): 增强tilemap编辑器和动画系统

* feat(modules): 添加module.json配置

* feat(core): 添加module.json和类型定义更新

* chore: 更新依赖和构建配置

* refactor(plugins): 更新插件模板使用ModuleManifest

* chore: 添加第三方依赖库

* chore: 移除BehaviourTree-ai和ecs-astar子模块

* docs: 更新README和文档主题样式

* fix: 修复Rust文档测试和添加rapier2d WASM绑定

* fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题

* feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea)

* fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖

* fix: 添加缺失的包依赖修复CI构建

* fix: 修复CodeQL检测到的代码问题

* fix: 修复构建错误和缺失依赖

* fix: 修复类型检查错误

* fix(material-system): 修复tsconfig配置支持TypeScript项目引用

* fix(editor-core): 修复Rollup构建配置添加tauri external

* fix: 修复CodeQL检测到的代码问题

* fix: 修复CodeQL检测到的代码问题
This commit is contained in:
YHH
2025-12-03 22:15:22 +08:00
committed by GitHub
parent caf7622aa0
commit 63f006ab62
496 changed files with 77601 additions and 4067 deletions

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