wde_renderer/assets/meshes/
plane.rs

1use bevy::math::Vec3;
2use wde_wgpu::vertex::Vertex;
3
4use crate::assets::{Mesh, MeshBbox};
5
6pub struct PlaneMesh;
7impl PlaneMesh {
8    /// Create a new plane mesh.
9    ///
10    /// # Arguments
11    ///
12    /// * `label` - The label for this mesh asset.
13    /// * `size` - The size in the u and v direction.
14    ///   The plane will be centered at the origin.
15    /// * `subdivisions` - Number of subdivisions per axis (minimum 1).
16    /// * `normal` - The normal direction of the plane.
17    ///
18    /// # Returns
19    ///
20    /// The plane mesh.
21    pub fn from(label: &str, subdivisions: u32, normal: Vec3) -> Mesh {
22        let subdivisions = subdivisions.max(1);
23        let half_size = [0.5, 0.5];
24
25        // Normalize the normal vector
26        let normal = normal.normalize();
27
28        // Compute tangent and bitangent vectors to form a coordinate system
29        let (tangent, bitangent) = compute_tangent_bitangent(normal);
30
31        // Generate vertices
32        let mut vertices = Vec::new();
33        let mut min_bounds = Vec3::new(f32::MAX, f32::MAX, f32::MAX);
34        let mut max_bounds = Vec3::new(f32::MIN, f32::MIN, f32::MIN);
35
36        for i in 0..=subdivisions {
37            for j in 0..=subdivisions {
38                let u = (i as f32) / (subdivisions as f32);
39                let v = (j as f32) / (subdivisions as f32);
40
41                // Position in local plane space [-half_size to +half_size]
42                let local_x = -half_size[0] + u;
43                let local_y = -half_size[1] + v;
44
45                // Transform to world space using tangent/bitangent basis
46                let position = tangent * local_x + bitangent * local_y;
47
48                // Update bounding box
49                min_bounds = min_bounds.min(position);
50                max_bounds = max_bounds.max(position);
51
52                vertices.push(Vertex {
53                    position: [position.x, position.y, position.z],
54                    normal: [normal.x, normal.y, normal.z],
55                    uv: [u, v],
56                    tangent: [tangent.x, tangent.y, tangent.z, 1.0]
57                });
58            }
59        }
60
61        // Generate indices (two triangles per quad)
62        let mut indices = Vec::new();
63        for i in 0..subdivisions {
64            for j in 0..subdivisions {
65                let row = subdivisions + 1;
66                let v0 = i * row + j;
67                let v1 = v0 + 1;
68                let v2 = v0 + row;
69                let v3 = v2 + 1;
70
71                // First triangle
72                indices.push(v0);
73                indices.push(v2);
74                indices.push(v1);
75
76                // Second triangle
77                indices.push(v1);
78                indices.push(v2);
79                indices.push(v3);
80            }
81        }
82
83        // Create bounding box
84        let bounding_box = MeshBbox {
85            min: min_bounds,
86            max: max_bounds
87        };
88
89        Mesh {
90            label: label.to_string(),
91            vertices,
92            indices,
93            bbox: bounding_box,
94            use_ssbo: false
95        }
96    }
97}
98
99/// Compute tangent and bitangent vectors for a given normal.
100/// This creates an orthonormal basis for the plane.
101fn compute_tangent_bitangent(normal: Vec3) -> (Vec3, Vec3) {
102    // Choose an arbitrary vector that's not parallel to normal
103    let up = if normal.y.abs() > 0.9 {
104        Vec3::new(1.0, 0.0, 0.0)
105    } else {
106        Vec3::new(0.0, 1.0, 0.0)
107    };
108
109    let tangent = normal.cross(up).normalize();
110    let bitangent = normal.cross(tangent).normalize();
111
112    (tangent, bitangent)
113}