use crate::rapier::na::{vector, Vector4};
use rapier::geometry::{Shape, ShapeType, TypedShape};
use crate::math::{Point, Vector};
pub enum GpuShapeType {
Ball = 0,
Cuboid = 1,
Capsule = 2,
#[cfg(feature = "dim3")]
Cone = 3,
#[cfg(feature = "dim3")]
Cylinder = 4,
Polyline = 5,
TriMesh = 6,
}
#[derive(Default, Clone, Debug)]
pub struct ShapeBuffers {
pub vertices: Vec<Point<f32>>,
pub collision_vertices: Vec<Point<f32>>,
pub collision_indices: Vec<u32>,
}
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct GpuShape {
a: Vector4<f32>,
b: Vector4<f32>,
}
impl GpuShape {
pub fn ball(radius: f32) -> Self {
let tag = f32::from_bits(GpuShapeType::Ball as u32);
Self {
a: vector![radius, 0.0, 0.0, tag],
b: vector![0.0, 0.0, 0.0, 0.0],
}
}
pub fn cuboid(half_extents: Vector<f32>) -> Self {
let tag = f32::from_bits(GpuShapeType::Cuboid as u32);
Self {
#[cfg(feature = "dim2")]
a: vector![half_extents.x, half_extents.y, 0.0, tag],
#[cfg(feature = "dim3")]
a: vector![half_extents.x, half_extents.y, half_extents.z, tag],
b: vector![0.0, 0.0, 0.0, 0.0],
}
}
pub fn capsule(a: Point<f32>, b: Point<f32>, radius: f32) -> Self {
let tag = f32::from_bits(GpuShapeType::Capsule as u32);
#[cfg(feature = "dim2")]
return Self {
a: vector![a.x, a.y, 0.0, tag],
b: vector![b.x, b.y, 0.0, radius],
};
#[cfg(feature = "dim3")]
return Self {
a: vector![a.x, a.y, a.z, tag],
b: vector![b.x, b.y, b.z, radius],
};
}
pub fn polyline(vertex_range: [u32; 2]) -> Self {
let tag = f32::from_bits(GpuShapeType::Polyline as u32);
let rng0 = f32::from_bits(vertex_range[0]);
let rng1 = f32::from_bits(vertex_range[1]);
Self {
a: vector![rng0, rng1, 0.0, tag],
b: vector![0.0, 0.0, 0.0, 0.0],
}
}
pub fn trimesh(trimesh: &crate::trimesh::GpuTriMesh, vertex_base_id: u32) -> Self {
let tag = f32::from_bits(GpuShapeType::TriMesh as u32);
Self {
a: vector![
f32::from_bits(trimesh.bvh_vtx_root_id),
f32::from_bits(trimesh.bvh_idx_root_id),
f32::from_bits(trimesh.bvh_node_len),
tag
],
b: vector![
f32::from_bits(trimesh.num_triangles),
f32::from_bits(trimesh.num_vertices),
f32::from_bits(vertex_base_id),
0.0
],
}
}
#[cfg(feature = "dim3")]
pub fn cone(half_height: f32, radius: f32) -> Self {
let tag = f32::from_bits(GpuShapeType::Cone as u32);
Self {
a: vector![half_height, radius, 0.0, tag],
b: vector![0.0, 0.0, 0.0, 0.0],
}
}
#[cfg(feature = "dim3")]
pub fn cylinder(half_height: f32, radius: f32) -> Self {
let tag = f32::from_bits(GpuShapeType::Cylinder as u32);
Self {
a: vector![half_height, radius, 0.0, tag],
b: vector![0.0, 0.0, 0.0, 0.0],
}
}
pub fn from_parry(shape: &(impl Shape + ?Sized), buffers: &mut ShapeBuffers) -> Option<Self> {
match shape.as_typed_shape() {
TypedShape::Ball(shape) => Some(Self::ball(shape.radius)),
TypedShape::Cuboid(shape) => Some(Self::cuboid(shape.half_extents)),
TypedShape::Capsule(shape) => Some(Self::capsule(
shape.segment.a,
shape.segment.b,
shape.radius,
)),
TypedShape::Polyline(shape) => {
let base_id = buffers.vertices.len();
buffers.vertices.extend_from_slice(shape.vertices());
Some(Self::polyline([
base_id as u32,
buffers.vertices.len() as u32,
]))
}
TypedShape::TriMesh(shape) => {
let base_id = buffers.vertices.len() as u32;
buffers.vertices.extend_from_slice(shape.vertices());
let gpu_trimesh = crate::trimesh::convert_trimesh_to_gpu(
shape,
&mut buffers.collision_vertices,
&mut buffers.collision_indices,
);
Some(Self::trimesh(&gpu_trimesh, base_id))
}
#[cfg(feature = "dim2")]
TypedShape::HeightField(shape) => {
let base_id = buffers.vertices.len();
let (vtx, _) = shape.to_polyline();
buffers.vertices.extend_from_slice(&vtx);
Some(Self::polyline([
base_id as u32,
buffers.vertices.len() as u32,
]))
}
#[cfg(feature = "dim3")]
TypedShape::HeightField(shape) => {
let (vtx, idx) = shape.to_trimesh();
let base_id = buffers.vertices.len() as u32;
buffers.vertices.extend_from_slice(&vtx);
let trimesh = rapier::geometry::TriMesh::with_flags(
vtx,
idx,
rapier::geometry::TriMeshFlags::ORIENTED
| rapier::geometry::TriMeshFlags::MERGE_DUPLICATE_VERTICES,
)
.expect("Failed to build trimesh from heightfield");
let gpu_trimesh = crate::trimesh::convert_trimesh_to_gpu(
&trimesh,
&mut buffers.collision_vertices,
&mut buffers.collision_indices,
);
Some(Self::trimesh(&gpu_trimesh, base_id))
}
#[cfg(feature = "dim3")]
TypedShape::Cone(shape) => Some(Self::cone(shape.half_height, shape.radius)),
#[cfg(feature = "dim3")]
TypedShape::Cylinder(shape) => Some(Self::cylinder(shape.half_height, shape.radius)),
_ => None,
}
}
pub fn shape_type(&self) -> ShapeType {
let tag = self.a.w.to_bits();
match tag {
0 => ShapeType::Ball,
1 => ShapeType::Cuboid,
2 => ShapeType::Capsule,
#[cfg(feature = "dim3")]
3 => ShapeType::Cone,
#[cfg(feature = "dim3")]
4 => ShapeType::Cylinder,
5 => ShapeType::Polyline,
6 => ShapeType::TriMesh,
_ => panic!("Unknown shape type: {}", tag),
}
}
pub fn polyline_rngs(&self) -> [u32; 2] {
assert!(self.shape_type() == ShapeType::Polyline);
[self.a.x.to_bits(), self.a.y.to_bits()]
}
pub fn trimesh_meta(&self) -> crate::trimesh::GpuTriMesh {
assert!(self.shape_type() == ShapeType::TriMesh);
crate::trimesh::GpuTriMesh {
bvh_vtx_root_id: self.a.x.to_bits(),
bvh_idx_root_id: self.a.y.to_bits(),
bvh_node_len: self.a.z.to_bits(),
num_triangles: self.b.x.to_bits(),
num_vertices: self.b.y.to_bits(),
}
}
pub fn trimesh_vertex_base_id(&self) -> u32 {
assert!(self.shape_type() == ShapeType::TriMesh);
self.b.z.to_bits()
}
}