bevy_mesh/primitives/dim3/
plane.rs

1use crate::{Indices, Mesh, MeshBuilder, Meshable};
2use bevy_asset::RenderAssetUsages;
3use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3};
4use wgpu::PrimitiveTopology;
5
6/// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape.
7#[derive(Clone, Copy, Debug, Default)]
8pub struct PlaneMeshBuilder {
9    /// The [`Plane3d`] shape.
10    pub plane: Plane3d,
11    /// The number of subdivisions in the mesh.
12    ///
13    /// 0 - is the original plane geometry, the 4 points in the XZ plane.
14    ///
15    /// 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.
16    ///
17    /// 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.
18    ///
19    /// and so on...
20    pub subdivisions: u32,
21}
22
23impl PlaneMeshBuilder {
24    /// Creates a new [`PlaneMeshBuilder`] from a given normal and size.
25    #[inline]
26    pub fn new(normal: Dir3, size: Vec2) -> Self {
27        Self {
28            plane: Plane3d {
29                normal,
30                half_size: size / 2.0,
31            },
32            subdivisions: 0,
33        }
34    }
35
36    /// Creates a new [`PlaneMeshBuilder`] from the given size, with the normal pointing upwards.
37    #[inline]
38    pub fn from_size(size: Vec2) -> Self {
39        Self {
40            plane: Plane3d {
41                half_size: size / 2.0,
42                ..Default::default()
43            },
44            subdivisions: 0,
45        }
46    }
47
48    /// Creates a new [`PlaneMeshBuilder`] from the given length, with the normal pointing upwards,
49    /// and the resulting [`PlaneMeshBuilder`] being a square.
50    #[inline]
51    pub fn from_length(length: f32) -> Self {
52        Self {
53            plane: Plane3d {
54                half_size: Vec2::splat(length) / 2.0,
55                ..Default::default()
56            },
57            subdivisions: 0,
58        }
59    }
60
61    /// Sets the normal of the plane, aka the direction the plane is facing.
62    #[inline]
63    #[doc(alias = "facing")]
64    pub fn normal(mut self, normal: Dir3) -> Self {
65        self.plane = Plane3d {
66            normal,
67            ..self.plane
68        };
69        self
70    }
71
72    /// Sets the size of the plane mesh.
73    #[inline]
74    pub fn size(mut self, width: f32, height: f32) -> Self {
75        self.plane.half_size = Vec2::new(width, height) / 2.0;
76        self
77    }
78
79    /// Sets the subdivisions of the plane mesh.
80    ///
81    /// 0 - is the original plane geometry, the 4 points in the XZ plane.
82    ///
83    /// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis,
84    ///     resulting in a plane with 4 quads / 8 triangles.
85    ///
86    /// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3
87    ///     equal sections along each axis, resulting in a plane with 9 quads / 18 triangles.
88    #[inline]
89    pub fn subdivisions(mut self, subdivisions: u32) -> Self {
90        self.subdivisions = subdivisions;
91        self
92    }
93}
94
95impl MeshBuilder for PlaneMeshBuilder {
96    fn build(&self) -> Mesh {
97        let z_vertex_count = self.subdivisions + 2;
98        let x_vertex_count = self.subdivisions + 2;
99        let num_vertices = (z_vertex_count * x_vertex_count) as usize;
100        let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize;
101
102        let mut positions: Vec<Vec3> = Vec::with_capacity(num_vertices);
103        let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices);
104        let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices);
105        let mut indices: Vec<u32> = Vec::with_capacity(num_indices);
106
107        let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal);
108        let size = self.plane.half_size * 2.0;
109
110        for z in 0..z_vertex_count {
111            for x in 0..x_vertex_count {
112                let tx = x as f32 / (x_vertex_count - 1) as f32;
113                let tz = z as f32 / (z_vertex_count - 1) as f32;
114                let pos = rotation * Vec3::new((-0.5 + tx) * size.x, 0.0, (-0.5 + tz) * size.y);
115                positions.push(pos);
116                normals.push(self.plane.normal.to_array());
117                uvs.push([tx, tz]);
118            }
119        }
120
121        for z in 0..z_vertex_count - 1 {
122            for x in 0..x_vertex_count - 1 {
123                let quad = z * x_vertex_count + x;
124                indices.push(quad + x_vertex_count + 1);
125                indices.push(quad + 1);
126                indices.push(quad + x_vertex_count);
127                indices.push(quad);
128                indices.push(quad + x_vertex_count);
129                indices.push(quad + 1);
130            }
131        }
132
133        Mesh::new(
134            PrimitiveTopology::TriangleList,
135            RenderAssetUsages::default(),
136        )
137        .with_inserted_indices(Indices::U32(indices))
138        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
139        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
140        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
141    }
142}
143
144impl Meshable for Plane3d {
145    type Output = PlaneMeshBuilder;
146
147    fn mesh(&self) -> Self::Output {
148        PlaneMeshBuilder {
149            plane: *self,
150            subdivisions: 0,
151        }
152    }
153}
154
155impl From<Plane3d> for Mesh {
156    fn from(plane: Plane3d) -> Self {
157        plane.mesh().build()
158    }
159}