From 1196186257e785e268bb61f2759c88e9d1be3664 Mon Sep 17 00:00:00 2001 From: Lynn <62256001+lynn-lumen@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:50:29 +0200 Subject: [PATCH] Add segments to `ExtrusionBuilder` (#13719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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> --- .../src/mesh/primitives/extrusion.rs | 146 +++++++++++------- 1 file changed, 92 insertions(+), 54 deletions(-) diff --git a/crates/bevy_render/src/mesh/primitives/extrusion.rs b/crates/bevy_render/src/mesh/primitives/extrusion.rs index 1a4fcc662695a..96cfbba9fb9b3 100644 --- a/crates/bevy_render/src/mesh/primitives/extrusion.rs +++ b/crates/bevy_render/src/mesh/primitives/extrusion.rs @@ -91,6 +91,7 @@ where ExtrusionBuilder { base_builder: self.base_shape.mesh(), half_depth: self.half_depth, + segments: 1, } } } @@ -101,8 +102,9 @@ where P: Primitive2d + Meshable, P::Output: Extrudable, { - base_builder: P::Output, - half_depth: f32, + pub base_builder: P::Output, + pub half_depth: f32, + pub segments: usize, } impl

ExtrusionBuilder

@@ -115,8 +117,16 @@ where Self { base_builder: base_shape.mesh(), 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 { @@ -218,14 +228,19 @@ where 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 (vert_count, index_count) = perimeter .iter() .fold((0, 0), |(verts, indices), perimeter| { ( - verts + 2 * perimeter.vertices_per_layer(), - indices + perimeter.indices_per_segment(), + verts + layers * perimeter.vertices_per_layer(), + indices + self.segments * perimeter.indices_per_segment(), ) }); let mut positions = Vec::with_capacity(vert_count); @@ -253,36 +268,37 @@ where // Get the index of the next vertex added to the mantel. let index = positions.len() as u32; - // Push the positions of the two indices and their equivalent points on the back face. - // This works, since the front face has already been moved to the correct depth. - positions.push(a); - positions.push(b); - positions.push([a[0], a[1], -a[2]]); - positions.push([b[0], b[1], -b[2]]); - - // UVs for the mantel are between (0, 0.5) and (1, 1). - uvs.extend_from_slice(&[ - [uv_x, 0.5], - [uv_x + uv_delta, 0.5], - [uv_x, 1.], - [uv_x + uv_delta, 1.], - ]); + // Push the positions of the two indices and their equivalent points on each layer. + for i in 0..layers { + let i = i as f32; + let z = a[2] - layer_depth_delta * i; + positions.push([a[0], a[1], z]); + positions.push([b[0], b[1], z]); + + // UVs for the mantel are between (0, 0.5) and (1, 1). + let uv_y = 0.5 + 0.5 * i / self.segments as f32; + uvs.push([uv_x, uv_y]); + uvs.push([uv_x + uv_delta, uv_y]); + } // 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.]) .normalize_or_zero() .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. - indices.extend_from_slice(&[ - index, - index + 2, - index + 1, - index + 1, - index + 2, - index + 3, - ]); + for i in 0..self.segments as u32 { + let base_index = index + 2 * i; + indices.extend_from_slice(&[ + base_index, + base_index + 2, + base_index + 1, + base_index + 1, + base_index + 2, + base_index + 3, + ]); + } } } PerimeterSegment::Smooth { @@ -296,14 +312,22 @@ where // we need to store the index of the first vertex that is part of this segment. 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`. if let Some(i) = segment_indices.first() { let p = cap_verts[*i as usize]; - positions.push(p); - positions.push([p[0], p[1], -p[2]]); - uvs.extend_from_slice(&[[uv_start, 0.5], [uv_start, 1.]]); - normals.extend_from_slice(&[first_normal.extend(0.).to_array(); 2]); + for i in 0..layers { + let i = i as f32; + let z = p[2] - layer_depth_delta * i; + 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. @@ -315,11 +339,15 @@ where let b = cap_verts[segment_indices[i] as usize]; let c = cap_verts[segment_indices[i + 1] as usize]; - // Add the current vertex and its counterpart on the backface - positions.push(b); - positions.push([b[0], b[1], -b[2]]); + // Add the current vertex and its counterparts on each layer. + for i in 0..layers { + 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 is interpolated between the normals of the two line segments connecting the current vertex with its neighbours. @@ -333,32 +361,42 @@ where .extend(0.) .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`. if let Some(i) = segment_indices.last() { let p = cap_verts[*i as usize]; - positions.push(p); - positions.push([p[0], p[1], -p[2]]); - uvs.extend_from_slice(&[ - [uv_start + uv_segment_delta, 0.5], - [uv_start + uv_segment_delta, 1.], + for i in 0..layers { + let i = i as f32; + let z = p[2] - layer_depth_delta * i; + positions.push([p[0], p[1], z]); + + 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 index = base_index + 2 * i; - indices.extend_from_slice(&[ - index, - index + 1, - index + 2, - index + 2, - index + 1, - index + 3, - ]); + let columns = segment_indices.len() as u32; + let segments = self.segments as u32; + let layers = segments + 1; + for s in 0..segments { + for column in 0..(columns - 1) { + let index = base_index + s + column * layers; + indices.extend_from_slice(&[ + index, + index + 1, + index + layers, + index + layers, + index + 1, + index + layers + 1, + ]); + } } } }