#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PlaneConfig {
pub width: f32,
pub depth: f32,
pub subdivisions_x: usize,
pub subdivisions_z: usize,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PlaneMesh {
pub positions: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
pub uvs: Vec<[f32; 2]>,
pub indices: Vec<u32>,
}
#[allow(dead_code)]
pub fn default_plane_config() -> PlaneConfig {
PlaneConfig {
width: 1.0,
depth: 1.0,
subdivisions_x: 1,
subdivisions_z: 1,
}
}
#[allow(dead_code)]
pub fn generate_plane(config: &PlaneConfig) -> PlaneMesh {
let nx = config.subdivisions_x.max(1);
let nz = config.subdivisions_z.max(1);
let w = config.width.abs().max(f32::EPSILON);
let d = config.depth.abs().max(f32::EPSILON);
let mut positions: Vec<[f32; 3]> = Vec::new();
let mut normals: Vec<[f32; 3]> = Vec::new();
let mut uvs: Vec<[f32; 2]> = Vec::new();
for iz in 0..=nz {
let z = -d * 0.5 + d * (iz as f32) / (nz as f32);
let v = (iz as f32) / (nz as f32);
for ix in 0..=nx {
let x = -w * 0.5 + w * (ix as f32) / (nx as f32);
let u = (ix as f32) / (nx as f32);
positions.push([x, 0.0, z]);
normals.push([0.0, 1.0, 0.0]);
uvs.push([u, v]);
}
}
let stride = (nx + 1) as u32;
let mut indices: Vec<u32> = Vec::new();
for iz in 0..nz as u32 {
for ix in 0..nx as u32 {
let a = iz * stride + ix;
let b = a + 1;
let c = a + stride;
let d = c + 1;
indices.extend_from_slice(&[a, b, c, b, d, c]);
}
}
PlaneMesh {
positions,
normals,
uvs,
indices,
}
}
#[allow(dead_code)]
pub fn plane_vertex_count(subdivisions_x: usize, subdivisions_z: usize) -> usize {
(subdivisions_x + 1) * (subdivisions_z + 1)
}
#[allow(dead_code)]
pub fn plane_index_count(subdivisions_x: usize, subdivisions_z: usize) -> usize {
subdivisions_x * subdivisions_z * 6
}
#[allow(dead_code)]
pub fn plane_area(config: &PlaneConfig) -> f32 {
config.width * config.depth
}
#[allow(dead_code)]
pub fn plane_indices_valid(plane: &PlaneMesh) -> bool {
let n = plane.positions.len() as u32;
plane.indices.iter().all(|&i| i < n)
}
#[allow(dead_code)]
pub fn plane_uvs_in_range(plane: &PlaneMesh) -> bool {
plane
.uvs
.iter()
.all(|uv| (0.0..=1.0).contains(&uv[0]) && (0.0..=1.0).contains(&uv[1]))
}
#[allow(dead_code)]
pub fn plane_to_json(plane: &PlaneMesh) -> String {
format!(
"{{\"vertices\":{},\"indices\":{}}}",
plane.positions.len(),
plane.indices.len()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let cfg = default_plane_config();
assert_eq!(cfg.width, 1.0);
assert_eq!(cfg.depth, 1.0);
}
#[test]
fn test_generate_plane_vertices() {
let cfg = default_plane_config();
let plane = generate_plane(&cfg);
assert!(!plane.positions.is_empty());
}
#[test]
fn test_vertex_count_matches() {
let cfg = PlaneConfig {
width: 2.0,
depth: 2.0,
subdivisions_x: 4,
subdivisions_z: 4,
};
let plane = generate_plane(&cfg);
assert_eq!(plane.positions.len(), plane_vertex_count(4, 4));
}
#[test]
fn test_index_count_matches() {
let cfg = PlaneConfig {
width: 2.0,
depth: 2.0,
subdivisions_x: 4,
subdivisions_z: 4,
};
let plane = generate_plane(&cfg);
assert_eq!(plane.indices.len(), plane_index_count(4, 4));
}
#[test]
fn test_normals_all_up() {
let cfg = default_plane_config();
let plane = generate_plane(&cfg);
for n in &plane.normals {
assert!((n[1] - 1.0).abs() < 1e-5);
}
}
#[test]
fn test_indices_valid() {
let cfg = PlaneConfig {
width: 1.0,
depth: 1.0,
subdivisions_x: 3,
subdivisions_z: 3,
};
let plane = generate_plane(&cfg);
assert!(plane_indices_valid(&plane));
}
#[test]
fn test_uvs_in_range() {
let cfg = default_plane_config();
let plane = generate_plane(&cfg);
assert!(plane_uvs_in_range(&plane));
}
#[test]
fn test_area() {
let cfg = PlaneConfig {
width: 3.0,
depth: 4.0,
subdivisions_x: 1,
subdivisions_z: 1,
};
assert!((plane_area(&cfg) - 12.0).abs() < 1e-5);
}
#[test]
fn test_to_json() {
let cfg = default_plane_config();
let plane = generate_plane(&cfg);
let json = plane_to_json(&plane);
assert!(json.contains("vertices"));
}
#[test]
fn test_normals_count_matches() {
let cfg = default_plane_config();
let plane = generate_plane(&cfg);
assert_eq!(plane.normals.len(), plane.positions.len());
}
}