bevy_render/mesh/primitives/dim3/
plane.rs

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