Add segments to ExtrusionBuilder (#13719)

# Objective

- Add support for `segments` for extrusion-meshes, akin to what is
possible with cylinders

## Solution

- Added a `.segments(segments: usize)` function to `ExtrusionBuilder`.
- Implemented support for segments in the meshing algorithm.
- If you set `.segments(0)`, the meshing will fail, just like it does
with cylinders.

## Additional information

Here is a wireframe of some extrusions with 1, 2, 3, etc. segments:

![image_2024-06-06_233205114](https://github.com/bevyengine/bevy/assets/62256001/358081e2-172d-407b-8bdb-9cda88eb4664)

---------

Co-authored-by: Lynn Büttgenbach <62256001+solis-lumine-vorago@users.noreply.github.com>
This commit is contained in:
Lynn 2024-06-10 14:50:29 +02:00 committed by François
parent be65fbb691
commit a2c5b0d415
No known key found for this signature in database

View File

@ -91,6 +91,7 @@ where
ExtrusionBuilder { ExtrusionBuilder {
base_builder: self.base_shape.mesh(), base_builder: self.base_shape.mesh(),
half_depth: self.half_depth, half_depth: self.half_depth,
segments: 1,
} }
} }
} }
@ -101,8 +102,9 @@ where
P: Primitive2d + Meshable, P: Primitive2d + Meshable,
P::Output: Extrudable, P::Output: Extrudable,
{ {
base_builder: P::Output, pub base_builder: P::Output,
half_depth: f32, pub half_depth: f32,
pub segments: usize,
} }
impl<P> ExtrusionBuilder<P> impl<P> ExtrusionBuilder<P>
@ -115,8 +117,16 @@ where
Self { Self {
base_builder: base_shape.mesh(), base_builder: base_shape.mesh(),
half_depth: depth / 2., half_depth: depth / 2.,
segments: 1,
} }
} }
/// Sets the number of segments along the depth of the extrusion.
/// Must be greater than `0` for the geometry of the mantel to be generated.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self
}
} }
impl ExtrusionBuilder<Circle> { impl ExtrusionBuilder<Circle> {
@ -218,14 +228,19 @@ where
panic!("The base mesh did not have vertex positions"); panic!("The base mesh did not have vertex positions");
}; };
debug_assert!(self.segments > 0);
let layers = self.segments + 1;
let layer_depth_delta = self.half_depth * 2.0 / self.segments as f32;
let perimeter = self.base_builder.perimeter(); let perimeter = self.base_builder.perimeter();
let (vert_count, index_count) = let (vert_count, index_count) =
perimeter perimeter
.iter() .iter()
.fold((0, 0), |(verts, indices), perimeter| { .fold((0, 0), |(verts, indices), perimeter| {
( (
verts + 2 * perimeter.vertices_per_layer(), verts + layers * perimeter.vertices_per_layer(),
indices + perimeter.indices_per_segment(), indices + self.segments * perimeter.indices_per_segment(),
) )
}); });
let mut positions = Vec::with_capacity(vert_count); let mut positions = Vec::with_capacity(vert_count);
@ -253,36 +268,37 @@ where
// Get the index of the next vertex added to the mantel. // Get the index of the next vertex added to the mantel.
let index = positions.len() as u32; let index = positions.len() as u32;
// Push the positions of the two indices and their equivalent points on the back face. // Push the positions of the two indices and their equivalent points on each layer.
// This works, since the front face has already been moved to the correct depth. for i in 0..layers {
positions.push(a); let i = i as f32;
positions.push(b); let z = a[2] - layer_depth_delta * i;
positions.push([a[0], a[1], -a[2]]); positions.push([a[0], a[1], z]);
positions.push([b[0], b[1], -b[2]]); positions.push([b[0], b[1], z]);
// UVs for the mantel are between (0, 0.5) and (1, 1). // UVs for the mantel are between (0, 0.5) and (1, 1).
uvs.extend_from_slice(&[ let uv_y = 0.5 + 0.5 * i / self.segments as f32;
[uv_x, 0.5], uvs.push([uv_x, uv_y]);
[uv_x + uv_delta, 0.5], uvs.push([uv_x + uv_delta, uv_y]);
[uv_x, 1.], }
[uv_x + uv_delta, 1.],
]);
// The normal is calculated to be the normal of the line segment connecting a and b. // The normal is calculated to be the normal of the line segment connecting a and b.
let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.]) let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.])
.normalize_or_zero() .normalize_or_zero()
.to_array(); .to_array();
normals.extend_from_slice(&[n; 4]); normals.extend_from_slice(&vec![n; 2 * layers]);
// Add the indices for the vertices created above to the mesh. // Add the indices for the vertices created above to the mesh.
indices.extend_from_slice(&[ for i in 0..self.segments as u32 {
index, let base_index = index + 2 * i;
index + 2, indices.extend_from_slice(&[
index + 1, base_index,
index + 1, base_index + 2,
index + 2, base_index + 1,
index + 3, base_index + 1,
]); base_index + 2,
base_index + 3,
]);
}
} }
} }
PerimeterSegment::Smooth { PerimeterSegment::Smooth {
@ -296,14 +312,22 @@ where
// we need to store the index of the first vertex that is part of this segment. // we need to store the index of the first vertex that is part of this segment.
let base_index = positions.len() as u32; let base_index = positions.len() as u32;
// If there is a first vertex, we need to add it and its counterpart on the back face. // If there is a first vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.first_normal`. // The normal is provided by `segment.first_normal`.
if let Some(i) = segment_indices.first() { if let Some(i) = segment_indices.first() {
let p = cap_verts[*i as usize]; let p = cap_verts[*i as usize];
positions.push(p); for i in 0..layers {
positions.push([p[0], p[1], -p[2]]); let i = i as f32;
uvs.extend_from_slice(&[[uv_start, 0.5], [uv_start, 1.]]); let z = p[2] - layer_depth_delta * i;
normals.extend_from_slice(&[first_normal.extend(0.).to_array(); 2]); positions.push([p[0], p[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start, uv_y]);
}
normals.extend_from_slice(&vec![
first_normal.extend(0.).to_array();
layers
]);
} }
// For all points inbetween the first and last vertices, we can automatically compute the normals. // For all points inbetween the first and last vertices, we can automatically compute the normals.
@ -315,11 +339,15 @@ where
let b = cap_verts[segment_indices[i] as usize]; let b = cap_verts[segment_indices[i] as usize];
let c = cap_verts[segment_indices[i + 1] as usize]; let c = cap_verts[segment_indices[i + 1] as usize];
// Add the current vertex and its counterpart on the backface // Add the current vertex and its counterparts on each layer.
positions.push(b); for i in 0..layers {
positions.push([b[0], b[1], -b[2]]); let i = i as f32;
let z = b[2] - layer_depth_delta * i;
positions.push([b[0], b[1], z]);
uvs.extend_from_slice(&[[uv_x, 0.5], [uv_x, 1.]]); let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_x, uv_y]);
}
// The normal for the current vertices can be calculated based on the two neighbouring vertices. // The normal for the current vertices can be calculated based on the two neighbouring vertices.
// The normal is interpolated between the normals of the two line segments connecting the current vertex with its neighbours. // The normal is interpolated between the normals of the two line segments connecting the current vertex with its neighbours.
@ -333,32 +361,42 @@ where
.extend(0.) .extend(0.)
.to_array() .to_array()
}; };
normals.extend_from_slice(&[n; 2]); normals.extend_from_slice(&vec![n; layers]);
} }
// If there is a last vertex, we need to add it and its counterpart on the back face. // If there is a last vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.last_normal`. // The normal is provided by `segment.last_normal`.
if let Some(i) = segment_indices.last() { if let Some(i) = segment_indices.last() {
let p = cap_verts[*i as usize]; let p = cap_verts[*i as usize];
positions.push(p); for i in 0..layers {
positions.push([p[0], p[1], -p[2]]); let i = i as f32;
uvs.extend_from_slice(&[ let z = p[2] - layer_depth_delta * i;
[uv_start + uv_segment_delta, 0.5], positions.push([p[0], p[1], z]);
[uv_start + uv_segment_delta, 1.],
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start + uv_segment_delta, uv_y]);
}
normals.extend_from_slice(&vec![
last_normal.extend(0.).to_array();
layers
]); ]);
normals.extend_from_slice(&[last_normal.extend(0.).to_array(); 2]);
} }
for i in 0..(segment_indices.len() as u32 - 1) { let columns = segment_indices.len() as u32;
let index = base_index + 2 * i; let segments = self.segments as u32;
indices.extend_from_slice(&[ let layers = segments + 1;
index, for s in 0..segments {
index + 1, for column in 0..(columns - 1) {
index + 2, let index = base_index + s + column * layers;
index + 2, indices.extend_from_slice(&[
index + 1, index,
index + 3, index + 1,
]); index + layers,
index + layers,
index + 1,
index + layers + 1,
]);
}
} }
} }
} }