use crate::core::{units, validation};
use crate::error::Result;
use crate::types::{MotionLocks, Pos, Quat, Vec3};
use boxddd_sys::ffi;
use std::ffi::CString;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BodyType {
Static,
Kinematic,
Dynamic,
}
impl BodyType {
#[inline]
pub const fn into_raw(self) -> ffi::b3BodyType {
match self {
Self::Static => ffi::b3BodyType_b3_staticBody,
Self::Kinematic => ffi::b3BodyType_b3_kinematicBody,
Self::Dynamic => ffi::b3BodyType_b3_dynamicBody,
}
}
#[inline]
pub const fn from_raw(raw: ffi::b3BodyType) -> Option<Self> {
match raw {
ffi::b3BodyType_b3_staticBody => Some(Self::Static),
ffi::b3BodyType_b3_kinematicBody => Some(Self::Kinematic),
ffi::b3BodyType_b3_dynamicBody => Some(Self::Dynamic),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct BodyDef {
pub body_type: BodyType,
pub position: Pos,
pub rotation: Quat,
pub linear_velocity: Vec3,
pub angular_velocity: Vec3,
pub linear_damping: f32,
pub angular_damping: f32,
pub gravity_scale: f32,
pub sleep_threshold: f32,
pub name: Option<String>,
pub motion_locks: MotionLocks,
pub enable_sleep: bool,
pub awake: bool,
pub enabled: bool,
pub bullet: bool,
pub allow_fast_rotation: bool,
pub enable_contact_recycling: bool,
}
impl Default for BodyDef {
fn default() -> Self {
let length_units = units::current_length_units();
Self {
body_type: BodyType::Static,
position: Pos::ZERO,
rotation: Quat::IDENTITY,
linear_velocity: Vec3::ZERO,
angular_velocity: Vec3::ZERO,
linear_damping: 0.0,
angular_damping: 0.0,
gravity_scale: 1.0,
sleep_threshold: 0.05 * length_units,
name: None,
motion_locks: MotionLocks::default(),
enable_sleep: true,
awake: true,
enabled: true,
bullet: false,
allow_fast_rotation: false,
enable_contact_recycling: true,
}
}
}
impl BodyDef {
#[inline]
pub fn builder() -> BodyDefBuilder {
BodyDefBuilder::new()
}
pub fn validate(&self) -> Result<()> {
validation::position("body.position", self.position)?;
validation::quaternion("body.rotation", self.rotation)?;
validation::vec3("body.linear_velocity", self.linear_velocity)?;
validation::vec3("body.angular_velocity", self.angular_velocity)?;
validation::nonnegative("body.linear_damping", self.linear_damping)?;
validation::nonnegative("body.angular_damping", self.angular_damping)?;
validation::finite("body.gravity_scale", self.gravity_scale)?;
validation::nonnegative("body.sleep_threshold", self.sleep_threshold)?;
if let Some(name) = self.name.as_deref() {
validation::c_string_value("body.name", name)?;
}
Ok(())
}
pub(crate) fn prepare(&self) -> Result<PreparedBodyDef<'_>> {
self.validate()?;
let name = validation::optional_c_string("body.name", self.name.as_deref())?;
Ok(PreparedBodyDef { def: self, name })
}
}
pub(crate) struct PreparedBodyDef<'a> {
def: &'a BodyDef,
name: Option<CString>,
}
impl PreparedBodyDef<'_> {
pub(crate) fn lower_locked(&self) -> ffi::b3BodyDef {
let mut raw = unsafe { ffi::b3DefaultBodyDef() };
raw.type_ = self.def.body_type.into_raw();
raw.position = self.def.position.into_raw();
raw.rotation = self.def.rotation.into_raw();
raw.linearVelocity = self.def.linear_velocity.into_raw();
raw.angularVelocity = self.def.angular_velocity.into_raw();
raw.linearDamping = self.def.linear_damping;
raw.angularDamping = self.def.angular_damping;
raw.gravityScale = self.def.gravity_scale;
raw.sleepThreshold = self.def.sleep_threshold;
raw.name = self
.name
.as_ref()
.map_or(std::ptr::null(), |name| name.as_ptr());
raw.userData = std::ptr::null_mut();
raw.motionLocks = self.def.motion_locks.into_raw();
raw.enableSleep = self.def.enable_sleep;
raw.isAwake = self.def.awake;
raw.isEnabled = self.def.enabled;
raw.isBullet = self.def.bullet;
raw.allowFastRotation = self.def.allow_fast_rotation;
raw.enableContactRecycling = self.def.enable_contact_recycling;
raw
}
}
#[derive(Clone, Debug)]
pub struct BodyDefBuilder {
def: BodyDef,
}
impl BodyDefBuilder {
#[inline]
pub fn new() -> Self {
Self {
def: BodyDef::default(),
}
}
#[inline]
pub fn body_type(mut self, body_type: BodyType) -> Self {
self.def.body_type = body_type;
self
}
#[inline]
pub fn position(mut self, position: impl Into<Pos>) -> Self {
self.def.position = position.into();
self
}
#[inline]
pub fn rotation(mut self, rotation: Quat) -> Self {
self.def.rotation = rotation;
self
}
#[inline]
pub fn linear_velocity(mut self, velocity: impl Into<Vec3>) -> Self {
self.def.linear_velocity = velocity.into();
self
}
#[inline]
pub fn angular_velocity(mut self, velocity: impl Into<Vec3>) -> Self {
self.def.angular_velocity = velocity.into();
self
}
#[inline]
pub fn gravity_scale(mut self, gravity_scale: f32) -> Self {
self.def.gravity_scale = gravity_scale;
self
}
#[inline]
pub fn bullet(mut self, is_bullet: bool) -> Self {
self.def.bullet = is_bullet;
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.def.name = Some(name.into());
self
}
pub fn linear_damping(mut self, damping: f32) -> Self {
self.def.linear_damping = damping;
self
}
pub fn angular_damping(mut self, damping: f32) -> Self {
self.def.angular_damping = damping;
self
}
pub fn sleep_threshold(mut self, threshold: f32) -> Self {
self.def.sleep_threshold = threshold;
self
}
pub fn motion_locks(mut self, locks: MotionLocks) -> Self {
self.def.motion_locks = locks;
self
}
pub fn enable_sleep(mut self, enabled: bool) -> Self {
self.def.enable_sleep = enabled;
self
}
pub fn awake(mut self, awake: bool) -> Self {
self.def.awake = awake;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.def.enabled = enabled;
self
}
pub fn allow_fast_rotation(mut self, allowed: bool) -> Self {
self.def.allow_fast_rotation = allowed;
self
}
pub fn enable_contact_recycling(mut self, enabled: bool) -> Self {
self.def.enable_contact_recycling = enabled;
self
}
#[inline]
pub fn build(self) -> Result<BodyDef> {
self.def.validate()?;
Ok(self.def)
}
}
impl Default for BodyDefBuilder {
fn default() -> Self {
Self::new()
}
}