use super::Collider;
use crate::rapier::prelude::Ball;
#[cfg(feature = "dim3")]
use crate::rapier::prelude::{Cone, Cylinder};
#[cfg(feature = "dim2")]
use bevy::render::mesh::{Capsule2dMeshBuilder, CircleMeshBuilder};
#[cfg(feature = "dim3")]
use bevy::render::mesh::{
Capsule3dMeshBuilder, ConeMeshBuilder, CylinderMeshBuilder, PlaneMeshBuilder, SphereMeshBuilder,
};
use bevy::{
asset::RenderAssetUsages,
prelude::{Mesh, MeshBuilder},
render::mesh::Indices,
};
use rapier::prelude::{Capsule, TypedShape};
pub fn typed_shape_to_mesh(typed_shape: &TypedShape) -> Option<Mesh> {
Some(match typed_shape {
rapier::prelude::TypedShape::Ball(ball) => ball.mesh_builder().build(),
rapier::prelude::TypedShape::Cuboid(cuboid) => {
let half_extents = cuboid.half_extents;
#[cfg(feature = "dim2")]
let mesh = bevy::prelude::Rectangle::new(half_extents.x * 2.0, half_extents.y * 2.0);
#[cfg(feature = "dim3")]
let mesh = bevy::prelude::Cuboid::new(
half_extents.x * 2.0,
half_extents.y * 2.0,
half_extents.z * 2.0,
);
Mesh::from(mesh)
}
rapier::prelude::TypedShape::Capsule(capsule) => capsule.mesh_builder().build(),
rapier::prelude::TypedShape::Segment(_segment) => {
log::warn!("Segment not implemented yet");
return None;
}
rapier::prelude::TypedShape::Triangle(triangle) => {
let a = triangle.a.coords;
let b = triangle.b.coords;
let c = triangle.c.coords;
#[cfg(feature = "dim2")]
let mesh = bevy::prelude::Triangle3d::new(
bevy::prelude::Vec3::new(a.x, a.y, 0.0),
bevy::prelude::Vec3::new(b.x, b.y, 0.0),
bevy::prelude::Vec3::new(c.x, c.y, 0.0),
);
#[cfg(feature = "dim3")]
let mesh = bevy::prelude::Triangle3d::new(a.into(), b.into(), c.into());
mesh.into()
}
rapier::prelude::TypedShape::TriMesh(tri_mesh) => {
let vertices = tri_mesh.vertices();
#[cfg(feature = "dim2")]
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, 0.0]).collect();
#[cfg(feature = "dim3")]
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, pos.z]).collect();
let indices = tri_mesh.indices();
let mesh = Mesh::new(
bevy::render::mesh::PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices.iter().cloned().flatten().collect()));
mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
}
rapier::prelude::TypedShape::Polyline(_polyline) => {
log::warn!("Polyline not implemented yet");
return None;
}
rapier::prelude::TypedShape::HalfSpace(_half_space) => {
log::warn!(
"HalfSpace cannot be converted automatically to a mesh, use [`ToMeshBuilder`] instead"
);
return None;
}
rapier::prelude::TypedShape::HeightField(_height_field) => {
#[cfg(feature = "dim2")]
{
log::warn!("HeightField for 2d not implemented yet");
return None;
}
#[cfg(feature = "dim3")]
{
let (vtx, idx) = _height_field.to_trimesh();
let mesh = Mesh::new(
bevy::render::mesh::PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(idx.into_iter().flatten().collect()));
mesh.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
vtx.iter()
.map(|pos| [pos.x, pos.y, pos.z])
.collect::<Vec<_>>(),
)
}
}
rapier::prelude::TypedShape::Compound(compound) => {
let mut final_mesh: Option<Mesh> = Option::None;
for shape in compound.shapes() {
let typed_shape = shape.1.as_typed_shape();
let mesh = typed_shape_to_mesh(&typed_shape)?;
if let Some(ref mut final_mesh) = final_mesh {
final_mesh.merge(&mesh);
} else {
final_mesh = Some(mesh);
}
}
final_mesh?
}
#[cfg(feature = "dim2")]
rapier::prelude::TypedShape::ConvexPolygon(convex_polygon) => {
let vertices = convex_polygon.points();
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, 0.0]).collect();
let indices = (1..vertices.len() as u32 - 1)
.flat_map(|i| vec![0, i, i + 1])
.collect::<Vec<u32>>();
let mesh = Mesh::new(
bevy::render::mesh::PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices));
mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
}
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::ConvexPolyhedron(convex_polyhedron) => {
let vertices = convex_polyhedron.points();
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, pos.z]).collect();
let indices = (1..vertices.len() as u32 - 1)
.flat_map(|i| vec![0, i, i + 1])
.collect::<Vec<u32>>();
let mesh = Mesh::new(
bevy::render::mesh::PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices));
mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
}
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::Cone(cone) => cone.mesh_builder().build(),
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::Cylinder(cylinder) => cylinder.mesh_builder().build(),
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::RoundCone(_round_cone) => {
log::warn!("RoundCone not implemented yet");
return None;
}
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::RoundCylinder(_round_cylinder) => {
log::warn!("RoundCylinder not implemented yet");
return None;
}
#[cfg(feature = "dim2")]
rapier::prelude::TypedShape::RoundConvexPolygon(_round_shape) => {
log::warn!("RoundConvexPolygon not implemented yet");
return None;
}
#[cfg(feature = "dim3")]
rapier::prelude::TypedShape::RoundConvexPolyhedron(_round_shape) => {
log::warn!("RoundConvexPolyhedron not implemented yet");
return None;
}
rapier::prelude::TypedShape::RoundCuboid(_round_shape) => {
log::warn!("RoundCuboid not implemented yet");
return None;
}
rapier::prelude::TypedShape::RoundTriangle(_round_shape) => {
log::warn!("RoundTriangle not implemented yet");
return None;
}
rapier::prelude::TypedShape::Custom(_shape) => {
log::warn!("Custom shape not implemented yet");
return None;
}
})
}
impl TryFrom<&Collider> for Mesh {
type Error = ();
fn try_from(collider: &Collider) -> Result<Self, Self::Error> {
let typed_shape = collider.raw.as_typed_shape();
typed_shape_to_mesh(&typed_shape).ok_or(())
}
}
pub trait ToMeshBuilder {
type MeshBuilder: bevy::render::prelude::MeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder;
}
#[cfg(feature = "dim2")]
impl ToMeshBuilder for &Ball {
type MeshBuilder = CircleMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
CircleMeshBuilder::new(self.radius, 16)
}
}
#[cfg(feature = "dim3")]
impl ToMeshBuilder for &Ball {
type MeshBuilder = SphereMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
SphereMeshBuilder::new(
self.radius,
bevy::render::mesh::SphereKind::Ico { subdivisions: 1 },
)
}
}
#[cfg(feature = "dim3")]
impl ToMeshBuilder for &rapier3d::prelude::HalfSpace {
type MeshBuilder = PlaneMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
PlaneMeshBuilder::new(
bevy::prelude::Dir3::new(self.normal.into()).unwrap_or(bevy::prelude::Dir3::Y),
bevy::prelude::Vec2::ONE,
)
}
}
#[cfg(feature = "dim2")]
impl ToMeshBuilder for &Capsule {
type MeshBuilder = Capsule2dMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
bevy::render::mesh::Capsule2dMeshBuilder::new(self.radius, self.height(), 10)
}
}
#[cfg(feature = "dim3")]
impl ToMeshBuilder for &Capsule {
type MeshBuilder = Capsule3dMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
bevy::render::mesh::Capsule3dMeshBuilder::new(self.radius, self.height(), 10, 10)
}
}
#[cfg(feature = "dim3")]
impl ToMeshBuilder for &Cone {
type MeshBuilder = ConeMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
bevy::render::mesh::ConeMeshBuilder::new(self.radius, self.half_height * 2.0, 16)
}
}
#[cfg(feature = "dim3")]
impl ToMeshBuilder for &Cylinder {
type MeshBuilder = CylinderMeshBuilder;
fn mesh_builder(&self) -> Self::MeshBuilder {
bevy::render::mesh::CylinderMeshBuilder::new(self.radius, self.half_height * 2.0, 16)
}
}