use crate::core::callback_state;
use crate::core::provenance::ContactEpoch;
use crate::error::{Error, Result};
use crate::types::{BodyId, ContactData, ContactId, JointId, Pos, ShapeId, Vec3, WorldTransform};
use crate::world::World;
use boxddd_sys::ffi;
use std::cell::{RefCell, RefMut};
use std::ffi::c_void;
#[derive(Copy, Clone)]
pub struct BodyMove<'a> {
raw: &'a ffi::b3BodyMoveEvent,
world: &'a World,
}
impl BodyMove<'_> {
pub fn body_id(&self) -> Result<BodyId> {
self.world
.state()
.ledger
.resolve_observed_body(self.raw.bodyId)
}
pub fn transform(&self) -> WorldTransform {
WorldTransform::from_raw(self.raw.transform)
}
pub fn fell_asleep(&self) -> bool {
self.raw.fellAsleep
}
pub fn raw_user_data(&self) -> *mut c_void {
self.raw.userData
}
}
pub struct BodyMoveIter<'a> {
inner: std::slice::Iter<'a, ffi::b3BodyMoveEvent>,
world: &'a World,
}
impl<'a> Iterator for BodyMoveIter<'a> {
type Item = BodyMove<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| BodyMove {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BodyMoveEvent {
pub body_id: BodyId,
pub transform: WorldTransform,
pub fell_asleep: bool,
pub raw_user_data: *mut c_void,
}
#[derive(Copy, Clone)]
pub struct SensorBeginTouch<'a> {
raw: &'a ffi::b3SensorBeginTouchEvent,
world: &'a World,
}
impl SensorBeginTouch<'_> {
pub fn sensor_shape(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.sensorShapeId)
}
pub fn visitor_shape(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.visitorShapeId)
}
}
#[derive(Copy, Clone)]
pub struct SensorEndTouch<'a> {
raw: &'a ffi::b3SensorEndTouchEvent,
world: &'a World,
}
impl SensorEndTouch<'_> {
pub fn sensor_shape(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.sensorShapeId)
}
pub fn visitor_shape(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.visitorShapeId)
}
}
pub struct SensorBeginIter<'a> {
inner: std::slice::Iter<'a, ffi::b3SensorBeginTouchEvent>,
world: &'a World,
}
impl<'a> Iterator for SensorBeginIter<'a> {
type Item = SensorBeginTouch<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| SensorBeginTouch {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct SensorEndIter<'a> {
inner: std::slice::Iter<'a, ffi::b3SensorEndTouchEvent>,
world: &'a World,
}
impl<'a> Iterator for SensorEndIter<'a> {
type Item = SensorEndTouch<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| SensorEndTouch {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SensorBeginTouchEvent {
pub sensor_shape: ShapeId,
pub visitor_shape: ShapeId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SensorEndTouchEvent {
pub sensor_shape: ShapeId,
pub visitor_shape: ShapeId,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SensorEvents {
pub begin: Vec<SensorBeginTouchEvent>,
pub end: Vec<SensorEndTouchEvent>,
}
impl SensorEvents {
fn clear_buffers(&mut self) {
self.begin.clear();
self.end.clear();
}
fn commit_buffers(out: &mut Self, scratch: &mut Self) {
crate::core::ffi_vec::commit_scratch(&mut out.begin, &mut scratch.begin);
crate::core::ffi_vec::commit_scratch(&mut out.end, &mut scratch.end);
}
}
#[derive(Copy, Clone)]
pub struct ContactBeginTouch<'a> {
raw: &'a ffi::b3ContactBeginTouchEvent,
world: &'a World,
}
impl ContactBeginTouch<'_> {
pub fn shape_a(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdA)
}
pub fn shape_b(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdB)
}
pub fn contact_id(&self) -> Result<ContactId> {
self.world
.state()
.ledger
.resolve_contact(self.raw.contactId)
}
}
#[derive(Copy, Clone)]
pub struct ContactEndTouch<'a> {
raw: &'a ffi::b3ContactEndTouchEvent,
world: &'a World,
}
impl ContactEndTouch<'_> {
pub fn shape_a(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdA)
}
pub fn shape_b(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdB)
}
pub fn contact_id(&self) -> Result<ContactId> {
self.world
.state()
.ledger
.resolve_contact_end(self.raw.contactId)
}
}
#[derive(Copy, Clone)]
pub struct ContactHit<'a> {
raw: &'a ffi::b3ContactHitEvent,
world: &'a World,
}
impl ContactHit<'_> {
pub fn shape_a(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdA)
}
pub fn shape_b(&self) -> Result<ShapeId> {
self.world
.state()
.ledger
.resolve_observed_shape(self.raw.shapeIdB)
}
pub fn contact_id(&self) -> Result<ContactId> {
self.world
.state()
.ledger
.resolve_contact(self.raw.contactId)
}
pub fn point(&self) -> Pos {
Pos::from_raw(self.raw.point)
}
pub fn normal(&self) -> Vec3 {
Vec3::from_raw(self.raw.normal)
}
pub fn approach_speed(&self) -> f32 {
self.raw.approachSpeed
}
pub fn user_material_id_a(&self) -> u64 {
self.raw.userMaterialIdA
}
pub fn user_material_id_b(&self) -> u64 {
self.raw.userMaterialIdB
}
}
pub struct ContactBeginIter<'a> {
inner: std::slice::Iter<'a, ffi::b3ContactBeginTouchEvent>,
world: &'a World,
}
impl<'a> Iterator for ContactBeginIter<'a> {
type Item = ContactBeginTouch<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| ContactBeginTouch {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct ContactEndIter<'a> {
inner: std::slice::Iter<'a, ffi::b3ContactEndTouchEvent>,
world: &'a World,
}
impl<'a> Iterator for ContactEndIter<'a> {
type Item = ContactEndTouch<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| ContactEndTouch {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct ContactHitIter<'a> {
inner: std::slice::Iter<'a, ffi::b3ContactHitEvent>,
world: &'a World,
}
impl<'a> Iterator for ContactHitIter<'a> {
type Item = ContactHit<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| ContactHit {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ContactBeginTouchEvent {
pub shape_a: ShapeId,
pub shape_b: ShapeId,
pub contact_id: ContactId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ContactEndTouchEvent {
pub shape_a: ShapeId,
pub shape_b: ShapeId,
pub contact_id: ContactId,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ContactHitEvent {
pub shape_a: ShapeId,
pub shape_b: ShapeId,
pub contact_id: ContactId,
pub point: Pos,
pub normal: Vec3,
pub approach_speed: f32,
pub user_material_id_a: u64,
pub user_material_id_b: u64,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ContactEvents {
pub begin: Vec<ContactBeginTouchEvent>,
pub end: Vec<ContactEndTouchEvent>,
pub hit: Vec<ContactHitEvent>,
}
impl ContactEvents {
fn clear_buffers(&mut self) {
self.begin.clear();
self.end.clear();
self.hit.clear();
}
fn commit_buffers(out: &mut Self, scratch: &mut Self) {
crate::core::ffi_vec::commit_scratch(&mut out.begin, &mut scratch.begin);
crate::core::ffi_vec::commit_scratch(&mut out.end, &mut scratch.end);
crate::core::ffi_vec::commit_scratch(&mut out.hit, &mut scratch.hit);
}
}
#[derive(Copy, Clone)]
pub struct JointEventView<'a> {
raw: &'a ffi::b3JointEvent,
world: &'a World,
}
impl JointEventView<'_> {
pub fn joint_id(&self) -> Result<JointId> {
self.world
.state()
.ledger
.resolve_observed_joint(self.raw.jointId)
}
pub fn raw_user_data(&self) -> *mut c_void {
self.raw.userData
}
}
pub struct JointEventIter<'a> {
inner: std::slice::Iter<'a, ffi::b3JointEvent>,
world: &'a World,
}
impl<'a> Iterator for JointEventIter<'a> {
type Item = JointEventView<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|raw| JointEventView {
raw,
world: self.world,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct JointEvent {
pub joint_id: JointId,
pub raw_user_data: *mut c_void,
}
#[derive(Debug, Default)]
pub(crate) struct EventScratch {
body: RefCell<Vec<BodyMoveEvent>>,
sensor: RefCell<SensorEvents>,
contact: RefCell<ContactEvents>,
joint: RefCell<Vec<JointEvent>>,
}
fn borrow_scratch<T>(scratch: &RefCell<T>) -> Result<RefMut<'_, T>> {
scratch.try_borrow_mut().map_err(|_| Error::NativeFailure)
}
fn raw_event_slice<'a, T>(ptr: *const T, count: i32) -> &'a [T] {
if count > 0 && !ptr.is_null() {
unsafe { std::slice::from_raw_parts(ptr, count as usize) }
} else {
&[]
}
}
fn body_event_slice<'a>(raw: ffi::b3BodyEvents) -> &'a [ffi::b3BodyMoveEvent] {
raw_event_slice(raw.moveEvents, raw.moveCount)
}
fn sensor_begin_slice<'a>(raw: ffi::b3SensorEvents) -> &'a [ffi::b3SensorBeginTouchEvent] {
raw_event_slice(raw.beginEvents, raw.beginCount)
}
fn sensor_end_slice<'a>(raw: ffi::b3SensorEvents) -> &'a [ffi::b3SensorEndTouchEvent] {
raw_event_slice(raw.endEvents, raw.endCount)
}
fn contact_begin_slice<'a>(raw: ffi::b3ContactEvents) -> &'a [ffi::b3ContactBeginTouchEvent] {
raw_event_slice(raw.beginEvents, raw.beginCount)
}
fn contact_end_slice<'a>(raw: ffi::b3ContactEvents) -> &'a [ffi::b3ContactEndTouchEvent] {
raw_event_slice(raw.endEvents, raw.endCount)
}
fn contact_hit_slice<'a>(raw: ffi::b3ContactEvents) -> &'a [ffi::b3ContactHitEvent] {
raw_event_slice(raw.hitEvents, raw.hitCount)
}
fn joint_event_slice<'a>(raw: ffi::b3JointEvents) -> &'a [ffi::b3JointEvent] {
raw_event_slice(raw.jointEvents, raw.count)
}
impl World {
fn body_event_snapshot(&self, event: &ffi::b3BodyMoveEvent) -> Result<BodyMoveEvent> {
Ok(BodyMoveEvent {
body_id: self.state().ledger.resolve_observed_body(event.bodyId)?,
transform: WorldTransform::from_raw(event.transform),
fell_asleep: event.fellAsleep,
raw_user_data: event.userData,
})
}
fn fill_body_events(
&self,
out: &mut Vec<BodyMoveEvent>,
events: &[ffi::b3BodyMoveEvent],
) -> Result<()> {
crate::core::ffi_vec::map_into_scratch(out, events, |event| self.body_event_snapshot(event))
}
fn fill_sensor_events(
&self,
out: &mut SensorEvents,
begin: &[ffi::b3SensorBeginTouchEvent],
end: &[ffi::b3SensorEndTouchEvent],
) -> Result<()> {
crate::core::ffi_vec::map_into_scratch(&mut out.begin, begin, |event| {
Ok(SensorBeginTouchEvent {
sensor_shape: self
.state()
.ledger
.resolve_observed_shape(event.sensorShapeId)?,
visitor_shape: self
.state()
.ledger
.resolve_observed_shape(event.visitorShapeId)?,
})
})?;
crate::core::ffi_vec::map_into_scratch(&mut out.end, end, |event| {
Ok(SensorEndTouchEvent {
sensor_shape: self
.state()
.ledger
.resolve_observed_shape(event.sensorShapeId)?,
visitor_shape: self
.state()
.ledger
.resolve_observed_shape(event.visitorShapeId)?,
})
})
}
fn fill_contact_events(
&self,
out: &mut ContactEvents,
begin: &[ffi::b3ContactBeginTouchEvent],
end: &[ffi::b3ContactEndTouchEvent],
hit: &[ffi::b3ContactHitEvent],
) -> Result<()> {
crate::core::ffi_vec::map_into_scratch(&mut out.begin, begin, |event| {
Ok(ContactBeginTouchEvent {
shape_a: self.state().ledger.resolve_observed_shape(event.shapeIdA)?,
shape_b: self.state().ledger.resolve_observed_shape(event.shapeIdB)?,
contact_id: self.state().ledger.resolve_contact(event.contactId)?,
})
})?;
crate::core::ffi_vec::map_into_scratch(&mut out.end, end, |event| {
Ok(ContactEndTouchEvent {
shape_a: self.state().ledger.resolve_observed_shape(event.shapeIdA)?,
shape_b: self.state().ledger.resolve_observed_shape(event.shapeIdB)?,
contact_id: self.state().ledger.resolve_contact_end(event.contactId)?,
})
})?;
crate::core::ffi_vec::map_into_scratch(&mut out.hit, hit, |event| {
Ok(ContactHitEvent {
shape_a: self.state().ledger.resolve_observed_shape(event.shapeIdA)?,
shape_b: self.state().ledger.resolve_observed_shape(event.shapeIdB)?,
contact_id: self.state().ledger.resolve_contact(event.contactId)?,
point: Pos::from_raw(event.point),
normal: Vec3::from_raw(event.normal),
approach_speed: event.approachSpeed,
user_material_id_a: event.userMaterialIdA,
user_material_id_b: event.userMaterialIdB,
})
})
}
fn fill_joint_events(
&self,
out: &mut Vec<JointEvent>,
events: &[ffi::b3JointEvent],
) -> Result<()> {
crate::core::ffi_vec::map_into_scratch(out, events, |event| {
self.joint_event_snapshot(event)
})
}
fn joint_event_snapshot(&self, event: &ffi::b3JointEvent) -> Result<JointEvent> {
Ok(JointEvent {
joint_id: self.state().ledger.resolve_observed_joint(event.jointId)?,
raw_user_data: event.userData,
})
}
pub(crate) fn finish_step_provenance(&mut self, next_epoch: ContactEpoch) {
self.state_mut().ledger.finish_step(next_epoch);
}
pub(crate) fn finish_contact_turnover(
&mut self,
next_epoch: crate::core::provenance::ContactEpoch,
) {
self.state_mut().ledger.finish_contact_turnover(next_epoch);
}
pub fn body_events(&self) -> Result<Vec<BodyMoveEvent>> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetBodyEvents(self.raw()) };
let mut out = Vec::new();
self.fill_body_events(&mut out, body_event_slice(raw))?;
Ok(out)
}
pub fn body_events_into(&self, out: &mut Vec<BodyMoveEvent>) -> Result<()> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetBodyEvents(self.raw()) };
let events = body_event_slice(raw);
let mut scratch = borrow_scratch(&self.state().event_scratch.body)?;
crate::core::ffi_vec::map_into_scratch_transactional(out, &mut scratch, events, |event| {
self.body_event_snapshot(event)
})
}
pub fn with_body_events_view<T>(&self, f: impl FnOnce(BodyMoveIter<'_>) -> T) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetBodyEvents(self.raw()) };
let events = body_event_slice(raw);
drop(_call);
Ok(f(BodyMoveIter {
inner: events.iter(),
world: self,
}))
}
pub unsafe fn with_body_events_raw<T>(
&self,
f: impl FnOnce(&[ffi::b3BodyMoveEvent]) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetBodyEvents(self.raw()) };
let events = body_event_slice(raw);
drop(_call);
Ok(f(events))
}
pub fn sensor_events(&self) -> Result<SensorEvents> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetSensorEvents(self.raw()) };
let mut out = SensorEvents::default();
self.fill_sensor_events(&mut out, sensor_begin_slice(raw), sensor_end_slice(raw))?;
Ok(out)
}
pub fn sensor_events_into(&self, out: &mut SensorEvents) -> Result<()> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetSensorEvents(self.raw()) };
let begin = sensor_begin_slice(raw);
let end = sensor_end_slice(raw);
let mut scratch = borrow_scratch(&self.state().event_scratch.sensor)?;
crate::core::ffi_vec::replace_from_scratch_transactional(
out,
&mut scratch,
SensorEvents::clear_buffers,
|scratch| self.fill_sensor_events(scratch, begin, end),
SensorEvents::commit_buffers,
)
}
pub fn with_sensor_events_view<T>(
&self,
f: impl FnOnce(SensorBeginIter<'_>, SensorEndIter<'_>) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetSensorEvents(self.raw()) };
let begin = sensor_begin_slice(raw);
let end = sensor_end_slice(raw);
drop(_call);
Ok(f(
SensorBeginIter {
inner: begin.iter(),
world: self,
},
SensorEndIter {
inner: end.iter(),
world: self,
},
))
}
pub unsafe fn with_sensor_events_raw<T>(
&self,
f: impl FnOnce(&[ffi::b3SensorBeginTouchEvent], &[ffi::b3SensorEndTouchEvent]) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetSensorEvents(self.raw()) };
let begin = sensor_begin_slice(raw);
let end = sensor_end_slice(raw);
drop(_call);
Ok(f(begin, end))
}
pub fn contact_events(&self) -> Result<ContactEvents> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetContactEvents(self.raw()) };
let mut out = ContactEvents::default();
self.fill_contact_events(
&mut out,
contact_begin_slice(raw),
contact_end_slice(raw),
contact_hit_slice(raw),
)?;
Ok(out)
}
pub fn contact_data(&self, contact_id: ContactId) -> Result<ContactData> {
callback_state::check_not_in_callback()?;
let raw_contact = self.state().ledger.authorize_contact(contact_id)?;
let _call = self.enter_world_call()?;
crate::core::debug_checks::check_contact_valid_raw(raw_contact)?;
let raw = unsafe { ffi::b3Contact_GetData(contact_id.into_raw()) };
let shape_id_a = self.state().ledger.resolve_shape(raw.shapeIdA)?;
let shape_id_b = self.state().ledger.resolve_shape(raw.shapeIdB)?;
Ok(unsafe { ContactData::from_raw_parts(raw, contact_id, shape_id_a, shape_id_b) })
}
pub fn contact_events_into(&self, out: &mut ContactEvents) -> Result<()> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetContactEvents(self.raw()) };
let begin = contact_begin_slice(raw);
let end = contact_end_slice(raw);
let hit = contact_hit_slice(raw);
let mut scratch = borrow_scratch(&self.state().event_scratch.contact)?;
crate::core::ffi_vec::replace_from_scratch_transactional(
out,
&mut scratch,
ContactEvents::clear_buffers,
|scratch| self.fill_contact_events(scratch, begin, end, hit),
ContactEvents::commit_buffers,
)
}
pub fn with_contact_events_view<T>(
&self,
f: impl FnOnce(ContactBeginIter<'_>, ContactEndIter<'_>, ContactHitIter<'_>) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetContactEvents(self.raw()) };
let begin = contact_begin_slice(raw);
let end = contact_end_slice(raw);
let hit = contact_hit_slice(raw);
drop(_call);
Ok(f(
ContactBeginIter {
inner: begin.iter(),
world: self,
},
ContactEndIter {
inner: end.iter(),
world: self,
},
ContactHitIter {
inner: hit.iter(),
world: self,
},
))
}
pub unsafe fn with_contact_events_raw<T>(
&self,
f: impl FnOnce(
&[ffi::b3ContactBeginTouchEvent],
&[ffi::b3ContactEndTouchEvent],
&[ffi::b3ContactHitEvent],
) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetContactEvents(self.raw()) };
let begin = contact_begin_slice(raw);
let end = contact_end_slice(raw);
let hit = contact_hit_slice(raw);
drop(_call);
Ok(f(begin, end, hit))
}
pub fn joint_events(&self) -> Result<Vec<JointEvent>> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetJointEvents(self.raw()) };
let mut out = Vec::new();
self.fill_joint_events(&mut out, joint_event_slice(raw))?;
Ok(out)
}
pub fn joint_events_into(&self, out: &mut Vec<JointEvent>) -> Result<()> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetJointEvents(self.raw()) };
let events = joint_event_slice(raw);
let mut scratch = borrow_scratch(&self.state().event_scratch.joint)?;
crate::core::ffi_vec::map_into_scratch_transactional(out, &mut scratch, events, |event| {
self.joint_event_snapshot(event)
})
}
pub fn with_joint_events_view<T>(&self, f: impl FnOnce(JointEventIter<'_>) -> T) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetJointEvents(self.raw()) };
let events = joint_event_slice(raw);
drop(_call);
Ok(f(JointEventIter {
inner: events.iter(),
world: self,
}))
}
pub unsafe fn with_joint_events_raw<T>(
&self,
f: impl FnOnce(&[ffi::b3JointEvent]) -> T,
) -> Result<T> {
let _call = self.enter_world_call()?;
let raw = unsafe { ffi::b3World_GetJointEvents(self.raw()) };
let events = joint_event_slice(raw);
drop(_call);
Ok(f(events))
}
}