From 1bd390806fe552b5444533ad51c50ef315fc9de6 Mon Sep 17 00:00:00 2001 From: woodroww Date: Mon, 13 Feb 2023 18:20:20 +0000 Subject: [PATCH] added subdivisions to shape::Plane (#7546) # Objective There was issue #191 requesting subdivisions on the shape::Plane. I also could have used this recently. I then write the solution. Fixes #191 ## Solution I changed the shape::Plane to include subdivisions field and the code to create the subdivisions. I don't know how people are counting subdivisions so as I put in the doc comments 0 subdivisions results in the original geometry of the Plane. Greater then 0 results in the number of lines dividing the plane. I didn't know if it would be better to create a new struct that implemented this feature, say SubdivisionPlane or change Plane. I decided on changing Plane as that was what the original issue was. It would be trivial to alter this to use another struct instead of altering Plane. The issues of migration, although small, would be eliminated if a new struct was implemented. ## Changelog ### Added Added subdivisions field to shape::Plane ## Migration Guide All the examples needed to be updated to initalize the subdivisions field. Also there were two tests in tests/window that need to be updated. A user would have to update all their uses of shape::Plane to initalize the subdivisions field. --- crates/bevy_render/src/mesh/shape/mod.rs | 73 +++++++++++++++---- examples/3d/3d_scene.rs | 2 +- examples/3d/3d_shapes.rs | 2 +- examples/3d/blend_modes.rs | 3 +- examples/3d/fxaa.rs | 2 +- examples/3d/lighting.rs | 2 +- examples/3d/orthographic.rs | 2 +- examples/3d/shadow_biases.rs | 4 +- examples/3d/shadow_caster_receiver.rs | 4 +- examples/3d/spherical_area_lights.rs | 2 +- examples/3d/split_screen.rs | 2 +- examples/3d/spotlight.rs | 2 +- examples/3d/transparency_3d.rs | 2 +- examples/3d/two_passes.rs | 2 +- examples/3d/vertex_colors.rs | 2 +- examples/3d/wireframe.rs | 2 +- examples/animation/animated_fox.rs | 2 +- examples/mobile/src/lib.rs | 2 +- .../shader_material_screenspace_texture.rs | 2 +- examples/shader/shader_prepass.rs | 2 +- examples/stress_tests/many_foxes.rs | 2 +- tests/window/minimising.rs | 5 +- tests/window/resizing.rs | 5 +- 23 files changed, 88 insertions(+), 40 deletions(-) diff --git a/crates/bevy_render/src/mesh/shape/mod.rs b/crates/bevy_render/src/mesh/shape/mod.rs index fd8f1873edae1..beba19c687e25 100644 --- a/crates/bevy_render/src/mesh/shape/mod.rs +++ b/crates/bevy_render/src/mesh/shape/mod.rs @@ -187,33 +187,76 @@ impl From for Mesh { pub struct Plane { /// The total side length of the square. pub size: f32, + /// The number of subdivisions in the mesh. + /// + /// 0 - is the original plane geometry, the 4 points in the XZ plane. + /// + /// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis, resulting in a plane with 4 quads / 8 triangles. + /// + /// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3 equal sections along each axis, resulting in a plane with 9 quads / 18 triangles. + /// + /// and so on... + pub subdivisions: u32, } impl Default for Plane { fn default() -> Self { - Plane { size: 1.0 } + Plane { + size: 1.0, + subdivisions: 0, + } + } +} + +impl Plane { + /// Creates a new plane centered at the origin with the supplied side length and zero subdivisions. + pub fn from_size(size: f32) -> Self { + Self { + size, + subdivisions: 0, + } } } impl From for Mesh { fn from(plane: Plane) -> Self { - let extent = plane.size / 2.0; - - let vertices = [ - ([extent, 0.0, -extent], [0.0, 1.0, 0.0], [1.0, 1.0]), - ([extent, 0.0, extent], [0.0, 1.0, 0.0], [1.0, 0.0]), - ([-extent, 0.0, extent], [0.0, 1.0, 0.0], [0.0, 0.0]), - ([-extent, 0.0, -extent], [0.0, 1.0, 0.0], [0.0, 1.0]), - ]; - - let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]); + // here this is split in the z and x directions if one ever needs asymetrical subdivision + // two Plane struct fields would need to be added instead of the single subdivisions field + let z_vertex_count = plane.subdivisions + 2; + let x_vertex_count = plane.subdivisions + 2; + let num_vertices = (z_vertex_count * x_vertex_count) as usize; + let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize; + let up = Vec3::Y.to_array(); + + let mut positions: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); + let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); + let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices); + let mut indices: Vec = Vec::with_capacity(num_indices); + + for y in 0..z_vertex_count { + for x in 0..x_vertex_count { + let tx = x as f32 / (x_vertex_count - 1) as f32; + let ty = y as f32 / (z_vertex_count - 1) as f32; + positions.push([(-0.5 + tx) * plane.size, 0.0, (-0.5 + ty) * plane.size]); + normals.push(up); + uvs.push([tx, 1.0 - ty]); + } + } - let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); - let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); - let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); + for y in 0..z_vertex_count - 1 { + for x in 0..x_vertex_count - 1 { + let quad = y * x_vertex_count + x; + indices.push(quad + x_vertex_count + 1); + indices.push(quad + 1); + indices.push(quad + x_vertex_count); + indices.push(quad); + indices.push(quad + x_vertex_count); + indices.push(quad + 1); + } + } let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); + mesh.set_indices(Some(Indices::U32(indices))); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index b2db6e3a43c30..50b094c53c499 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -17,7 +17,7 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index d942505b56fad..5358ca4ed321b 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -75,7 +75,7 @@ fn setup( // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane { size: 50. }.into()), + mesh: meshes.add(shape::Plane::from_size(50.0).into()), material: materials.add(Color::SILVER.into()), ..default() }); diff --git a/examples/3d/blend_modes.rs b/examples/3d/blend_modes.rs index 30076743e5fb1..10b779d9226ff 100644 --- a/examples/3d/blend_modes.rs +++ b/examples/3d/blend_modes.rs @@ -148,7 +148,8 @@ fn setup( // Chessboard Plane let black_material = materials.add(Color::BLACK.into()); let white_material = materials.add(Color::WHITE.into()); - let plane_mesh = meshes.add(shape::Plane { size: 2.0 }.into()); + + let plane_mesh = meshes.add(shape::Plane::from_size(2.0).into()); for x in -3..4 { for z in -3..4 { diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 279f4c18a583b..8873987385ee5 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -44,7 +44,7 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index ef382ddb32b58..73856c01a6e29 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -25,7 +25,7 @@ fn setup( ) { // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 10.0 })), + mesh: meshes.add(shape::Plane::from_size(10.0).into()), material: materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, diff --git a/examples/3d/orthographic.rs b/examples/3d/orthographic.rs index 920706674cdb3..4ecb8b284444a 100644 --- a/examples/3d/orthographic.rs +++ b/examples/3d/orthographic.rs @@ -29,7 +29,7 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/shadow_biases.rs b/examples/3d/shadow_biases.rs index 9297900216a3e..81b9fbe024b96 100644 --- a/examples/3d/shadow_biases.rs +++ b/examples/3d/shadow_biases.rs @@ -104,9 +104,7 @@ fn setup( // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { - size: 2.0 * spawn_plane_depth, - })), + mesh: meshes.add(shape::Plane::from_size(2.0 * spawn_plane_depth).into()), material: white_handle, ..default() }); diff --git a/examples/3d/shadow_caster_receiver.rs b/examples/3d/shadow_caster_receiver.rs index 0216acb01638a..f8eed512d738b 100644 --- a/examples/3d/shadow_caster_receiver.rs +++ b/examples/3d/shadow_caster_receiver.rs @@ -67,7 +67,7 @@ fn setup( // floating plane - initially not a shadow receiver and not a caster commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 20.0 })), + mesh: meshes.add(shape::Plane::from_size(20.0).into()), material: materials.add(Color::GREEN.into()), transform: Transform::from_xyz(0.0, 1.0, -10.0), ..default() @@ -78,7 +78,7 @@ fn setup( // lower ground plane - initially a shadow receiver commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 20.0 })), + mesh: meshes.add(shape::Plane::from_size(20.0).into()), material: white_handle, ..default() }); diff --git a/examples/3d/spherical_area_lights.rs b/examples/3d/spherical_area_lights.rs index b28e1d824c795..c9316c4fd66b2 100644 --- a/examples/3d/spherical_area_lights.rs +++ b/examples/3d/spherical_area_lights.rs @@ -23,7 +23,7 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 100.0 })), + mesh: meshes.add(shape::Plane::from_size(100.0).into()), material: materials.add(StandardMaterial { base_color: Color::rgb(0.2, 0.2, 0.2), perceptual_roughness: 0.08, diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 9a32318ada676..bb1e67d5a8fd4 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -24,7 +24,7 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 100.0 })), + mesh: meshes.add(shape::Plane::from_size(100.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index 61b01b61ce824..9969d5614ce8b 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -29,7 +29,7 @@ fn setup( ) { // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 100.0 })), + mesh: meshes.add(shape::Plane::from_size(100.0).into()), material: materials.add(StandardMaterial { base_color: Color::GREEN, perceptual_roughness: 1.0, diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index 4ac093c82dbff..4371ba1784dc6 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -20,7 +20,7 @@ fn setup( ) { // opaque plane, uses `alpha_mode: Opaque` by default commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 6.0 })), + mesh: meshes.add(shape::Plane::from_size(6.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/two_passes.rs b/examples/3d/two_passes.rs index bb576d5c8e063..5ad8da9a8d0a3 100644 --- a/examples/3d/two_passes.rs +++ b/examples/3d/two_passes.rs @@ -17,7 +17,7 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/vertex_colors.rs b/examples/3d/vertex_colors.rs index 2585d5419f927..ef10af376ceed 100644 --- a/examples/3d/vertex_colors.rs +++ b/examples/3d/vertex_colors.rs @@ -17,7 +17,7 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index ef7599eda57c5..203c06d35ce15 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -30,7 +30,7 @@ fn setup( wireframe_config.global = false; // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 7b564c12e06da..1cc50849e606c 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -44,7 +44,7 @@ fn setup( // Plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 500000.0 })), + mesh: meshes.add(shape::Plane::from_size(500000.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index 76651e24e1767..398ea2969fae1 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -55,7 +55,7 @@ fn setup_scene( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: materials.add(Color::rgb(0.1, 0.2, 0.1).into()), ..default() }); diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index 9fd63f8588d5a..f0fec91b198e3 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -26,7 +26,7 @@ fn setup( mut standard_materials: ResMut>, ) { commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: standard_materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index 6e0f9a934e9ec..0f9697bcadf6b 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -58,7 +58,7 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane { size: 5.0 }.into()), + mesh: meshes.add(shape::Plane::from_size(5.0).into()), material: std_materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index bf58aa522de8c..5980bb3587c22 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -161,7 +161,7 @@ fn setup( // Plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5000.0 })), + mesh: meshes.add(shape::Plane::from_size(5000.0).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/tests/window/minimising.rs b/tests/window/minimising.rs index 32eaa25023f0f..217228dbd8fb0 100644 --- a/tests/window/minimising.rs +++ b/tests/window/minimising.rs @@ -36,7 +36,10 @@ fn setup_3d( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(Mesh::from(shape::Plane { + size: 5.0, + subdivisions: 0, + })), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index a6dc392b0bd49..20ba42133a63b 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -114,7 +114,10 @@ fn setup_3d( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(Mesh::from(shape::Plane { + size: 5.0, + subdivisions: 0, + })), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() });