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() });