Skip to content

Commit 1196186

Browse files
authored
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 <[email protected]>
1 parent 1f61c26 commit 1196186

File tree

1 file changed

+92
-54
lines changed

1 file changed

+92
-54
lines changed

crates/bevy_render/src/mesh/primitives/extrusion.rs

+92-54
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ where
9191
ExtrusionBuilder {
9292
base_builder: self.base_shape.mesh(),
9393
half_depth: self.half_depth,
94+
segments: 1,
9495
}
9596
}
9697
}
@@ -101,8 +102,9 @@ where
101102
P: Primitive2d + Meshable,
102103
P::Output: Extrudable,
103104
{
104-
base_builder: P::Output,
105-
half_depth: f32,
105+
pub base_builder: P::Output,
106+
pub half_depth: f32,
107+
pub segments: usize,
106108
}
107109

108110
impl<P> ExtrusionBuilder<P>
@@ -115,8 +117,16 @@ where
115117
Self {
116118
base_builder: base_shape.mesh(),
117119
half_depth: depth / 2.,
120+
segments: 1,
118121
}
119122
}
123+
124+
/// Sets the number of segments along the depth of the extrusion.
125+
/// Must be greater than `0` for the geometry of the mantel to be generated.
126+
pub fn segments(mut self, segments: usize) -> Self {
127+
self.segments = segments;
128+
self
129+
}
120130
}
121131

122132
impl ExtrusionBuilder<Circle> {
@@ -218,14 +228,19 @@ where
218228
panic!("The base mesh did not have vertex positions");
219229
};
220230

231+
debug_assert!(self.segments > 0);
232+
233+
let layers = self.segments + 1;
234+
let layer_depth_delta = self.half_depth * 2.0 / self.segments as f32;
235+
221236
let perimeter = self.base_builder.perimeter();
222237
let (vert_count, index_count) =
223238
perimeter
224239
.iter()
225240
.fold((0, 0), |(verts, indices), perimeter| {
226241
(
227-
verts + 2 * perimeter.vertices_per_layer(),
228-
indices + perimeter.indices_per_segment(),
242+
verts + layers * perimeter.vertices_per_layer(),
243+
indices + self.segments * perimeter.indices_per_segment(),
229244
)
230245
});
231246
let mut positions = Vec::with_capacity(vert_count);
@@ -253,36 +268,37 @@ where
253268
// Get the index of the next vertex added to the mantel.
254269
let index = positions.len() as u32;
255270

256-
// Push the positions of the two indices and their equivalent points on the back face.
257-
// This works, since the front face has already been moved to the correct depth.
258-
positions.push(a);
259-
positions.push(b);
260-
positions.push([a[0], a[1], -a[2]]);
261-
positions.push([b[0], b[1], -b[2]]);
262-
263-
// UVs for the mantel are between (0, 0.5) and (1, 1).
264-
uvs.extend_from_slice(&[
265-
[uv_x, 0.5],
266-
[uv_x + uv_delta, 0.5],
267-
[uv_x, 1.],
268-
[uv_x + uv_delta, 1.],
269-
]);
271+
// Push the positions of the two indices and their equivalent points on each layer.
272+
for i in 0..layers {
273+
let i = i as f32;
274+
let z = a[2] - layer_depth_delta * i;
275+
positions.push([a[0], a[1], z]);
276+
positions.push([b[0], b[1], z]);
277+
278+
// UVs for the mantel are between (0, 0.5) and (1, 1).
279+
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
280+
uvs.push([uv_x, uv_y]);
281+
uvs.push([uv_x + uv_delta, uv_y]);
282+
}
270283

271284
// The normal is calculated to be the normal of the line segment connecting a and b.
272285
let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.])
273286
.normalize_or_zero()
274287
.to_array();
275-
normals.extend_from_slice(&[n; 4]);
288+
normals.extend_from_slice(&vec![n; 2 * layers]);
276289

277290
// Add the indices for the vertices created above to the mesh.
278-
indices.extend_from_slice(&[
279-
index,
280-
index + 2,
281-
index + 1,
282-
index + 1,
283-
index + 2,
284-
index + 3,
285-
]);
291+
for i in 0..self.segments as u32 {
292+
let base_index = index + 2 * i;
293+
indices.extend_from_slice(&[
294+
base_index,
295+
base_index + 2,
296+
base_index + 1,
297+
base_index + 1,
298+
base_index + 2,
299+
base_index + 3,
300+
]);
301+
}
286302
}
287303
}
288304
PerimeterSegment::Smooth {
@@ -296,14 +312,22 @@ where
296312
// we need to store the index of the first vertex that is part of this segment.
297313
let base_index = positions.len() as u32;
298314

299-
// If there is a first vertex, we need to add it and its counterpart on the back face.
315+
// If there is a first vertex, we need to add it and its counterparts on each layer.
300316
// The normal is provided by `segment.first_normal`.
301317
if let Some(i) = segment_indices.first() {
302318
let p = cap_verts[*i as usize];
303-
positions.push(p);
304-
positions.push([p[0], p[1], -p[2]]);
305-
uvs.extend_from_slice(&[[uv_start, 0.5], [uv_start, 1.]]);
306-
normals.extend_from_slice(&[first_normal.extend(0.).to_array(); 2]);
319+
for i in 0..layers {
320+
let i = i as f32;
321+
let z = p[2] - layer_depth_delta * i;
322+
positions.push([p[0], p[1], z]);
323+
324+
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
325+
uvs.push([uv_start, uv_y]);
326+
}
327+
normals.extend_from_slice(&vec![
328+
first_normal.extend(0.).to_array();
329+
layers
330+
]);
307331
}
308332

309333
// For all points inbetween the first and last vertices, we can automatically compute the normals.
@@ -315,11 +339,15 @@ where
315339
let b = cap_verts[segment_indices[i] as usize];
316340
let c = cap_verts[segment_indices[i + 1] as usize];
317341

318-
// Add the current vertex and its counterpart on the backface
319-
positions.push(b);
320-
positions.push([b[0], b[1], -b[2]]);
342+
// Add the current vertex and its counterparts on each layer.
343+
for i in 0..layers {
344+
let i = i as f32;
345+
let z = b[2] - layer_depth_delta * i;
346+
positions.push([b[0], b[1], z]);
321347

322-
uvs.extend_from_slice(&[[uv_x, 0.5], [uv_x, 1.]]);
348+
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
349+
uvs.push([uv_x, uv_y]);
350+
}
323351

324352
// The normal for the current vertices can be calculated based on the two neighbouring vertices.
325353
// The normal is interpolated between the normals of the two line segments connecting the current vertex with its neighbours.
@@ -333,32 +361,42 @@ where
333361
.extend(0.)
334362
.to_array()
335363
};
336-
normals.extend_from_slice(&[n; 2]);
364+
normals.extend_from_slice(&vec![n; layers]);
337365
}
338366

339-
// If there is a last vertex, we need to add it and its counterpart on the back face.
367+
// If there is a last vertex, we need to add it and its counterparts on each layer.
340368
// The normal is provided by `segment.last_normal`.
341369
if let Some(i) = segment_indices.last() {
342370
let p = cap_verts[*i as usize];
343-
positions.push(p);
344-
positions.push([p[0], p[1], -p[2]]);
345-
uvs.extend_from_slice(&[
346-
[uv_start + uv_segment_delta, 0.5],
347-
[uv_start + uv_segment_delta, 1.],
371+
for i in 0..layers {
372+
let i = i as f32;
373+
let z = p[2] - layer_depth_delta * i;
374+
positions.push([p[0], p[1], z]);
375+
376+
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
377+
uvs.push([uv_start + uv_segment_delta, uv_y]);
378+
}
379+
normals.extend_from_slice(&vec![
380+
last_normal.extend(0.).to_array();
381+
layers
348382
]);
349-
normals.extend_from_slice(&[last_normal.extend(0.).to_array(); 2]);
350383
}
351384

352-
for i in 0..(segment_indices.len() as u32 - 1) {
353-
let index = base_index + 2 * i;
354-
indices.extend_from_slice(&[
355-
index,
356-
index + 1,
357-
index + 2,
358-
index + 2,
359-
index + 1,
360-
index + 3,
361-
]);
385+
let columns = segment_indices.len() as u32;
386+
let segments = self.segments as u32;
387+
let layers = segments + 1;
388+
for s in 0..segments {
389+
for column in 0..(columns - 1) {
390+
let index = base_index + s + column * layers;
391+
indices.extend_from_slice(&[
392+
index,
393+
index + 1,
394+
index + layers,
395+
index + layers,
396+
index + 1,
397+
index + layers + 1,
398+
]);
399+
}
362400
}
363401
}
364402
}

0 commit comments

Comments
 (0)