Skip to content

Commit 596f9ea

Browse files
committed
wip parry_to_bevy_mesh
1 parent 3d0e4c4 commit 596f9ea

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

bevy_rapier2d/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ serde-serialize = ["rapier2d/serde-serialize", "bevy/serialize", "serde"]
5050
enhanced-determinism = ["rapier2d/enhanced-determinism"]
5151
headless = []
5252
async-collider = ["bevy/bevy_asset", "bevy/bevy_scene", "bevy/bevy_render"]
53+
collider-to-bevy-mesh = ["bevy/bevy_render"]
5354

5455
[dependencies]
5556
bevy = { version = "0.15", default-features = false }

bevy_rapier3d/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ serde-serialize = ["rapier3d/serde-serialize", "bevy/serialize", "serde"]
5151
enhanced-determinism = ["rapier3d/enhanced-determinism"]
5252
headless = []
5353
async-collider = ["bevy/bevy_asset", "bevy/bevy_scene", "bevy/bevy_render"]
54+
collider-to-bevy-mesh = ["bevy/bevy_render"]
5455

5556
[dependencies]
5657
bevy = { version = "0.15", default-features = false }

src/geometry/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ mod collider;
1212
mod collider_impl;
1313
/// Wrappers around Rapier shapes to access their properties.
1414
pub mod shape_views;
15+
#[cfg(feature = "collider-to-bevy-mesh")]
16+
pub mod to_bevy_mesh;
1517

1618
/// Result of the projection of a point on a shape.
1719
#[derive(Copy, Clone, Debug, PartialEq)]

src/geometry/to_bevy_mesh.rs

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
use super::Collider;
2+
use bevy::{
3+
asset::RenderAssetUsages,
4+
prelude::{Mesh, MeshBuilder, Segment3d},
5+
render::mesh::Indices,
6+
};
7+
use rapier::prelude::{Shape, TriMesh, TypedShape};
8+
9+
pub fn typed_shape_to_mesh(typed_shape: TypedShape) -> Mesh {
10+
match typed_shape {
11+
rapier::prelude::TypedShape::Ball(ball) => {
12+
let radius = ball.radius;
13+
let mesh = bevy::render::mesh::SphereMeshBuilder::new(
14+
radius,
15+
bevy::render::mesh::SphereKind::Ico { subdivisions: 10 },
16+
);
17+
mesh.build()
18+
}
19+
rapier::prelude::TypedShape::Cuboid(cuboid) => {
20+
let half_extents = cuboid.half_extents;
21+
#[cfg(feature = "dim2")]
22+
let mesh = bevy::prelude::Rectangle::new(half_extents.x * 2.0, half_extents.y * 2.0);
23+
#[cfg(feature = "dim3")]
24+
let mesh = bevy::prelude::Cuboid::new(
25+
half_extents.x * 2.0,
26+
half_extents.y * 2.0,
27+
half_extents.z * 2.0,
28+
);
29+
Mesh::from(mesh)
30+
}
31+
rapier::prelude::TypedShape::Capsule(capsule) => {
32+
let radius = capsule.radius;
33+
let half_height = capsule.half_height();
34+
#[cfg(feature = "dim2")]
35+
let mesh = bevy::render::mesh::Capsule2dMeshBuilder::new(radius, half_height, 10);
36+
#[cfg(feature = "dim3")]
37+
let mesh = bevy::render::mesh::Capsule3dMeshBuilder::new(radius, half_height, 10, 10);
38+
mesh.build()
39+
}
40+
rapier::prelude::TypedShape::Segment(segment) => {
41+
todo!("Segment shape not implemented yet, how to represent it ? A LineStrip?");
42+
}
43+
rapier::prelude::TypedShape::Triangle(triangle) => {
44+
let a = triangle.a.coords;
45+
let b = triangle.b.coords;
46+
let c = triangle.c.coords;
47+
let mesh = bevy::prelude::Triangle3d::new(
48+
bevy::prelude::Vec3::new(a.x, a.y, 0.0),
49+
bevy::prelude::Vec3::new(b.x, b.y, 0.0),
50+
bevy::prelude::Vec3::new(c.x, c.y, 0.0),
51+
);
52+
mesh.into()
53+
}
54+
rapier::prelude::TypedShape::TriMesh(tri_mesh) => {
55+
let vertices = tri_mesh.vertices();
56+
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, 0.0]).collect();
57+
let indices = tri_mesh.indices();
58+
let mesh = Mesh::new(
59+
bevy::render::mesh::PrimitiveTopology::TriangleList,
60+
RenderAssetUsages::default(),
61+
)
62+
.with_inserted_indices(Indices::U32(indices.iter().cloned().flatten().collect()));
63+
let mesh = mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
64+
65+
mesh.into()
66+
}
67+
rapier::prelude::TypedShape::Polyline(polyline) => {
68+
todo!("Polyline shape not implemented yet, how to represent it ? BoxedPolyline3d is only a primitive.");
69+
}
70+
rapier::prelude::TypedShape::HalfSpace(half_space) => {
71+
todo!("HalfSpace shape not implemented yet, how to represent it ? its infinite property makes it difficult.");
72+
}
73+
rapier::prelude::TypedShape::HeightField(height_field) => {
74+
#[cfg(feature = "dim2")]
75+
todo!("HeightField for 2d not implemented yet, how to represent it ? its effectively a line.");
76+
#[cfg(feature = "dim3")]
77+
{
78+
// FIXME: we could use TriMesh::From(height_field), but that would clone, we should fix that in parry.
79+
let (vtx, idx) = height_field.to_trimesh();
80+
let tri_mesh = TriMesh::new(vtx, idx);
81+
82+
// From Trimesh:
83+
let vertices = tri_mesh.vertices();
84+
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, 0.0]).collect();
85+
let indices = tri_mesh.indices();
86+
let mesh = Mesh::new(
87+
bevy::render::mesh::PrimitiveTopology::TriangleList,
88+
RenderAssetUsages::default(),
89+
)
90+
.with_inserted_indices(Indices::U32(indices.iter().cloned().flatten().collect()));
91+
let mesh = mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
92+
93+
mesh.into()
94+
}
95+
}
96+
rapier::prelude::TypedShape::Compound(compound) => {
97+
let mut vertices = Vec::new();
98+
let mut indices = Vec::new();
99+
for shape in compound.shapes() {
100+
let typed_shape = shape.1.as_typed_shape();
101+
let mesh = typed_shape_to_mesh(typed_shape);
102+
103+
assert!(mesh.primitive_topology() == bevy::render::mesh::PrimitiveTopology::TriangleList,
104+
"Compound shape mesh conversion does not support shapes not converting to PrimitiveTopology::TriangleList.");
105+
106+
vertices.append(
107+
&mut mesh
108+
.attribute(Mesh::ATTRIBUTE_POSITION.id)
109+
.unwrap()
110+
.as_float3()
111+
.unwrap()
112+
.iter()
113+
.cloned()
114+
.flatten()
115+
.collect::<Vec<_>>(),
116+
);
117+
indices.append(&mut mesh.indices().unwrap().iter().collect::<Vec<_>>());
118+
}
119+
let mesh = Mesh::new(
120+
bevy::render::mesh::PrimitiveTopology::TriangleList,
121+
RenderAssetUsages::default(),
122+
)
123+
.with_inserted_indices(Indices::U32(indices.iter().map(|i| *i as u32).collect()));
124+
let mesh = mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
125+
mesh.into()
126+
}
127+
#[cfg(feature = "dim2")]
128+
rapier::prelude::TypedShape::ConvexPolygon(convex_polygon) => {
129+
let vertices = convex_polygon.points();
130+
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, 0.0]).collect();
131+
132+
let indices = (1..vertices.len() as u32 - 1)
133+
.flat_map(|i| vec![0, i, i + 1])
134+
.collect::<Vec<u32>>();
135+
let mesh = Mesh::new(
136+
bevy::render::mesh::PrimitiveTopology::TriangleList,
137+
RenderAssetUsages::default(),
138+
)
139+
.with_inserted_indices(Indices::U32(indices));
140+
let mesh = mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
141+
142+
mesh.into()
143+
}
144+
#[cfg(feature = "dim3")]
145+
rapier::prelude::TypedShape::ConvexPolyhedron(convex_polyhedron) => {
146+
let vertices = convex_polyhedron.points();
147+
let vertices: Vec<_> = vertices.iter().map(|pos| [pos.x, pos.y, pos.z]).collect();
148+
149+
let indices = (1..vertices.len() as u32 - 1)
150+
.flat_map(|i| vec![0, i, i + 1])
151+
.collect::<Vec<u32>>();
152+
let mesh = Mesh::new(
153+
bevy::render::mesh::PrimitiveTopology::TriangleList,
154+
RenderAssetUsages::default(),
155+
)
156+
.with_inserted_indices(Indices::U32(indices));
157+
let mesh = mesh.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
158+
159+
mesh.into()
160+
}
161+
#[cfg(feature = "dim3")]
162+
rapier::prelude::TypedShape::Cone(cone) => {
163+
let radius = cone.radius;
164+
let half_height = cone.half_height;
165+
// TODO: implement Meshable for all TypedShape variants, that probably will have to be wrapped in a new type.
166+
let mesh = bevy::render::mesh::ConeMeshBuilder::new(radius, half_height, 10);
167+
mesh.build()
168+
}
169+
#[cfg(feature = "dim3")]
170+
rapier::prelude::TypedShape::Cylinder(cylinder) => {
171+
let radius = cylinder.radius;
172+
let half_height = cylinder.half_height;
173+
let mesh = bevy::render::mesh::CylinderMeshBuilder::new(radius, half_height, 10);
174+
mesh.build()
175+
}
176+
#[cfg(feature = "dim3")]
177+
rapier::prelude::TypedShape::RoundCone(round_cone) => {
178+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
179+
}
180+
#[cfg(feature = "dim3")]
181+
rapier::prelude::TypedShape::RoundCylinder(round_cylinder) => {
182+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
183+
}
184+
#[cfg(feature = "dim2")]
185+
rapier::prelude::TypedShape::RoundConvexPolygon(round_shape) => {
186+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
187+
}
188+
#[cfg(feature = "dim3")]
189+
rapier::prelude::TypedShape::RoundConvexPolyhedron(round_shape) => {
190+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
191+
}
192+
rapier::prelude::TypedShape::RoundCuboid(round_shape) => {
193+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
194+
}
195+
rapier::prelude::TypedShape::RoundTriangle(round_shape) => {
196+
todo!("parry doesn't have easy to use functions to convert RoundShapes to a mesh.");
197+
}
198+
rapier::prelude::TypedShape::Custom(shape) => {
199+
todo!("I'm not sure how to convert a custom shape to a mesh.");
200+
}
201+
}
202+
}
203+
204+
impl From<Collider> for Mesh {
205+
fn from(shape: Collider) -> Self {
206+
let typed_shape = shape.raw.as_typed_shape();
207+
typed_shape_to_mesh(typed_shape)
208+
}
209+
}

0 commit comments

Comments
 (0)