use std::fmt;
use std::sync::Arc;
use oxijolt_sys::*;
use crate::owned::{JoltObject, Owned};
use crate::world::ensure_initialized;
use crate::CollisionGroupError;
impl JoltObject for JPH_GroupFilterTable {
unsafe fn destroy(ptr: *mut Self) {
unsafe { JPH_GroupFilter_Destroy(ptr.cast()) };
}
}
pub struct GroupFilterTableBuilder {
table: Owned<JPH_GroupFilterTable>,
sub_groups: u32,
}
impl GroupFilterTableBuilder {
pub fn new(sub_groups: u32) -> Result<Self, CollisionGroupError> {
if sub_groups == 0 {
return Err(CollisionGroupError::NoSubGroups);
}
if sub_groups > GroupFilterTable::MAX_SUB_GROUPS {
return Err(CollisionGroupError::TooManySubGroups(sub_groups));
}
if !ensure_initialized() {
return Err(CollisionGroupError::InitFailed);
}
let table = unsafe { Owned::from_raw(JPH_GroupFilterTable_Create(sub_groups)) }
.unwrap_or_else(|| unreachable!("joltc `new`s the table"));
Ok(Self { table, sub_groups })
}
pub fn disable_collision(&mut self, a: u32, b: u32) -> Result<(), CollisionGroupError> {
self.check_pair(a, b)?;
unsafe { JPH_GroupFilterTable_DisableCollision(self.table.as_ptr(), a, b) };
Ok(())
}
pub fn enable_collision(&mut self, a: u32, b: u32) -> Result<(), CollisionGroupError> {
self.check_pair(a, b)?;
unsafe { JPH_GroupFilterTable_EnableCollision(self.table.as_ptr(), a, b) };
Ok(())
}
pub fn build(self) -> GroupFilterTable {
GroupFilterTable(Arc::new(TableInner {
table: self.table,
sub_groups: self.sub_groups,
}))
}
fn check_pair(&self, a: u32, b: u32) -> Result<(), CollisionGroupError> {
check_pair(self.sub_groups, a, b)
}
}
impl fmt::Debug for GroupFilterTableBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GroupFilterTableBuilder")
.field("sub_groups", &self.sub_groups)
.finish_non_exhaustive()
}
}
fn check_pair(sub_groups: u32, a: u32, b: u32) -> Result<(), CollisionGroupError> {
check_sub_group(sub_groups, a)?;
check_sub_group(sub_groups, b)?;
if a == b {
return Err(CollisionGroupError::SameSubGroup(a));
}
Ok(())
}
fn check_sub_group(sub_groups: u32, id: u32) -> Result<(), CollisionGroupError> {
if id >= sub_groups {
return Err(CollisionGroupError::SubGroupOutOfRange(id));
}
Ok(())
}
unsafe impl Send for GroupFilterTableBuilder {}
#[derive(Clone)]
pub struct GroupFilterTable(Arc<TableInner>);
struct TableInner {
table: Owned<JPH_GroupFilterTable>,
sub_groups: u32,
}
unsafe impl Send for TableInner {}
unsafe impl Sync for TableInner {}
impl GroupFilterTable {
pub const MAX_SUB_GROUPS: u32 = 4096;
pub fn sub_groups(&self) -> u32 {
self.0.sub_groups
}
pub fn is_collision_enabled(&self, a: u32, b: u32) -> Result<bool, CollisionGroupError> {
check_sub_group(self.0.sub_groups, a)?;
check_sub_group(self.0.sub_groups, b)?;
if a == b {
return Ok(false);
}
Ok(unsafe { JPH_GroupFilterTable_IsCollisionEnabled(self.as_ptr(), a, b) })
}
fn as_ptr(&self) -> *mut JPH_GroupFilterTable {
self.0.table.as_ptr()
}
}
impl PartialEq for GroupFilterTable {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for GroupFilterTable {}
impl fmt::Debug for GroupFilterTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GroupFilterTable")
.field("sub_groups", &self.0.sub_groups)
.finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CollisionGroup {
table: GroupFilterTable,
group_id: u32,
sub_group_id: u32,
}
impl CollisionGroup {
pub const MAX_GROUP_ID: u32 = (1 << 31) - 1;
pub fn new(
table: &GroupFilterTable,
group_id: u32,
sub_group_id: u32,
) -> Result<Self, CollisionGroupError> {
if group_id > Self::MAX_GROUP_ID {
return Err(CollisionGroupError::GroupIdOutOfRange(group_id));
}
check_sub_group(table.sub_groups(), sub_group_id)?;
Ok(Self {
table: table.clone(),
group_id,
sub_group_id,
})
}
pub fn table(&self) -> &GroupFilterTable {
&self.table
}
pub fn group_id(&self) -> u32 {
self.group_id
}
pub fn sub_group_id(&self) -> u32 {
self.sub_group_id
}
pub fn can_collide(&self, other: &CollisionGroup) -> bool {
let (a, b) = (self.to_jph(), other.to_jph());
unsafe { JPH_GroupFilter_CanCollide(self.table.as_ptr().cast(), &a, &b) }
}
pub(crate) fn to_jph(&self) -> JPH_CollisionGroup {
JPH_CollisionGroup {
groupFilter: self.table.as_ptr().cast_const().cast(),
groupID: self.group_id,
subGroupID: self.sub_group_id,
}
}
}