use std::ptr::{null, null_mut};
use oxijolt_sys::*;
use super::geometry::{diagonal, mul, mul_vec, rotation, v3, M3, V3};
use super::mesh::{convex_extent_of, is_collidable};
use super::{initialize, Shape, ShapeSettings};
use crate::{limits, ShapeError, ThinTrianglesError, Vec3};
impl Shape {
pub fn new_scaled(shape: &Shape, scale: Vec3) -> Result<Self, ShapeError> {
if !scale.is_finite() {
return Err(ShapeError::InvalidValue("scale must be finite"));
}
initialize()?;
let jolt_scale = scale.to_jph();
if !unsafe { JPH_Shape_IsValidScale(shape.as_ptr(), &jolt_scale) } {
return Err(ShapeError::InvalidValue(
"scale is not valid for this shape",
));
}
check_scaled_triangles(shape, scale)?;
let settings = unsafe {
ShapeSettings::from_raw(
JPH_ScaledShapeSettings_Create2(shape.as_ptr(), &jolt_scale).cast(),
)
}?;
let scaled = settings.create()?.within_extent_bounds()?;
if !limits::is_local_offset(scaled.center_of_mass()) {
return Err(ShapeError::InvalidValue(
"scaled centre of mass must lie within limits::MAX_SHAPE_EXTENT",
));
}
Ok(scaled)
}
}
type Placement = M3;
fn check_scaled_triangles(shape: &Shape, scale: Vec3) -> Result<(), ShapeError> {
let Some(leaves) = triangle_leaves(shape, scale) else {
return Err(ShapeError::InvalidValue(
"scale cannot be checked for this shape",
));
};
for (leaf, placement) in leaves {
let (max_convex_extent, triangles) =
unsafe { (convex_extent_of(leaf), placed_triangles(leaf, &placement)) };
if !triangles
.into_iter()
.all(|corners| is_collidable(corners, [0.0; 3], max_convex_extent))
{
return Err(ShapeError::ThinTriangles(ThinTrianglesError {
scale,
max_convex_extent,
}));
}
}
Ok(())
}
fn triangle_leaves(shape: &Shape, scale: Vec3) -> Option<Vec<(*const JPH_Shape, Placement)>> {
let mut leaves = Vec::new();
let mut pending = vec![(shape.as_ptr(), diagonal(v3(scale)))];
while let Some((part, placement)) = pending.pop() {
let (static_only, sub_type) =
unsafe { (JPH_Shape_MustBeStatic(part), JPH_Shape_GetSubType(part)) };
if !static_only {
continue;
}
if sub_type == JPH_ShapeSubType_Mesh || sub_type == JPH_ShapeSubType_HeightField {
leaves.push((part, placement));
} else {
let known = unsafe { push_parts(part, sub_type, &placement, &mut pending) };
if !known {
return None;
}
}
}
Some(leaves)
}
unsafe fn push_parts(
shape: *const JPH_Shape,
sub_type: JPH_ShapeSubType,
placement: &Placement,
pending: &mut Vec<(*const JPH_Shape, Placement)>,
) -> bool {
let turned = |q: JPH_Quat| mul(placement, &rotation([q.x, q.y, q.z, q.w].map(f64::from)));
let mut turn = JPH_Quat {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
};
if sub_type == JPH_ShapeSubType_StaticCompound || sub_type == JPH_ShapeSubType_MutableCompound {
let compound: *const JPH_CompoundShape = shape.cast();
let count = unsafe { JPH_CompoundShape_GetNumSubShapes(compound) };
for index in 0..count {
let mut child = null();
unsafe {
JPH_CompoundShape_GetSubShape(
compound,
index,
&mut child,
null_mut(),
&mut turn,
null_mut(),
);
}
pending.push((child, turned(turn)));
}
return true;
}
let decorators = [
JPH_ShapeSubType_Scaled,
JPH_ShapeSubType_RotatedTranslated,
JPH_ShapeSubType_OffsetCenterOfMass,
];
if !decorators.contains(&sub_type) {
return false;
}
let decorated: *const JPH_DecoratedShape = shape.cast();
let inner = unsafe { JPH_DecoratedShape_GetInnerShape(decorated) };
let inner_placement = if sub_type == JPH_ShapeSubType_Scaled {
let mut inner_scale = Vec3::ZERO.to_jph();
unsafe { JPH_ScaledShape_GetScale(shape.cast(), &mut inner_scale) };
mul(placement, &diagonal(v3(Vec3::from_jph(inner_scale))))
} else if sub_type == JPH_ShapeSubType_RotatedTranslated {
unsafe { JPH_RotatedTranslatedShape_GetRotation(shape.cast(), &mut turn) };
turned(turn)
} else {
*placement
};
pending.push((inner, inner_placement));
true
}
unsafe fn placed_triangles(shape: *const JPH_Shape, placement: &Placement) -> Vec<[V3; 3]> {
let count = unsafe { JPH_Shape_GetTriangles(shape, null_mut(), 0) };
let mut vertices = vec![Vec3::ZERO.to_jph(); 3 * count as usize];
unsafe { JPH_Shape_GetTriangles(shape, vertices.as_mut_ptr(), count) };
let placed: Vec<V3> = vertices
.iter()
.map(|&vertex| mul_vec(placement, v3(Vec3::from_jph(vertex))))
.collect();
placed.as_chunks::<3>().0.to_vec()
}
#[cfg(test)]
mod tests;