75 lines
2.4 KiB
Rust
75 lines
2.4 KiB
Rust
//! Plane primitive — horizontal plane in XZ with subdivisions.
|
||
|
||
use crate::core::geometry::Geometry;
|
||
|
||
/// Generates a horizontal plane in the XZ plane (normal +Y), centered at (0, 0, 0), with
|
||
/// `width` × `depth` dimensions, subdivided into `seg_x` × `seg_z` cells.
|
||
pub fn plane(width: f32, depth: f32, seg_x: u32, seg_z: u32) -> Geometry {
|
||
let sx = seg_x.max(1);
|
||
let sz = seg_z.max(1);
|
||
let (mut positions, mut normals, mut uvs, mut indices) = (
|
||
Vec::<[f32; 3]>::new(),
|
||
Vec::<[f32; 3]>::new(),
|
||
Vec::<[f32; 2]>::new(),
|
||
Vec::<u16>::new(),
|
||
);
|
||
for z in 0..=sz {
|
||
let vz = z as f32 / sz as f32;
|
||
for x in 0..=sx {
|
||
let vx = x as f32 / sx as f32;
|
||
positions.push([(vx - 0.5) * width, 0.0, (vz - 0.5) * depth]);
|
||
normals.push([0.0, 1.0, 0.0]);
|
||
uvs.push([vx, vz]);
|
||
}
|
||
}
|
||
for z in 0..sz {
|
||
for x in 0..sx {
|
||
let a = z * (sx + 1) + x;
|
||
let b = a + 1;
|
||
let c = (z + 1) * (sx + 1) + x;
|
||
let d = c + 1;
|
||
indices
|
||
.extend_from_slice(&[a as u16, c as u16, b as u16, b as u16, c as u16, d as u16]);
|
||
}
|
||
}
|
||
Geometry::new(positions)
|
||
.with_normals(normals)
|
||
.with_uvs(uvs)
|
||
.with_indices(indices)
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use glam::Vec3;
|
||
|
||
fn assert_valid(geo: &Geometry) {
|
||
geo.validate().expect("generated geometry must validate");
|
||
let positions = &geo.positions;
|
||
let normals = geo.normals.as_ref().expect("normals present");
|
||
let uvs = geo.uvs.as_ref().expect("uvs present");
|
||
let indices = geo.indices.as_ref().expect("indices present");
|
||
assert_eq!(normals.len(), positions.len());
|
||
assert_eq!(uvs.len(), positions.len());
|
||
for n in normals {
|
||
let len = Vec3::from_array(*n).length();
|
||
assert!((len - 1.0).abs() < 1e-3);
|
||
}
|
||
for &i in indices {
|
||
assert!((i as usize) < positions.len());
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn plane_counts() {
|
||
let g = plane(2.0, 3.0, 1, 1);
|
||
assert_eq!(g.positions.len(), 4);
|
||
assert_eq!(g.indices.as_ref().unwrap().len(), 6);
|
||
assert_valid(&g);
|
||
assert!(g.positions.iter().all(|p| p[1] == 0.0));
|
||
let g2 = plane(2.0, 3.0, 4, 5);
|
||
assert_eq!(g2.positions.len(), (4 + 1) * (5 + 1));
|
||
assert_valid(&g2);
|
||
}
|
||
}
|