Skip to main content

bevy_mesh/primitives/dim3/
plane.rs

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