use crate::{
core::{
algebra::{Vector2, Vector3, Vector4},
math::TriangleDefinition,
},
graphics::ElementRange,
scene::mesh::{
buffer::{TriangleBuffer, VertexBuffer},
surface::{SurfaceData, SurfaceResource},
vertex::StaticVertex,
},
};
use fyrox_core::Uuid;
use fyrox_resource::untyped::ResourceKind;
#[derive(Default, Debug, Clone)]
pub struct TerrainGeometry {
pub data: SurfaceResource,
pub quadrants: [ElementRange; 4],
}
impl TerrainGeometry {
pub fn new(mesh_size: Vector2<u32>) -> Self {
let mut surface_data = SurfaceData::new(
VertexBuffer::new::<StaticVertex>(0, vec![]).unwrap(),
TriangleBuffer::default(),
);
let mut vertex_buffer_mut = surface_data.vertex_buffer.modify();
for iy in 0..mesh_size.y {
let kz = iy as f32 / ((mesh_size.y - 1) as f32);
for x in 0..mesh_size.x {
let kx = x as f32 / ((mesh_size.x - 1) as f32);
vertex_buffer_mut
.push_vertex(&StaticVertex {
position: Vector3::new(kx, 0.0, kz),
tex_coord: Vector2::new(kx, kz),
normal: Vector3::new(0.0, 1.0, 0.0),
tangent: Vector4::new(1.0, 0.0, 0.0, -1.0),
})
.unwrap();
}
}
drop(vertex_buffer_mut);
let mut geometry_buffer_mut = surface_data.geometry_buffer.modify();
let half_size = Vector2::new(mesh_size.x - 1, mesh_size.y - 1) / 2;
let mut quadrants = [ElementRange::Full; 4];
for ((x_range, y_range), quadrant) in [
(0..half_size.x, 0..half_size.y),
(half_size.x..mesh_size.x - 1, 0..half_size.y),
(half_size.x..mesh_size.x - 1, half_size.y..mesh_size.y - 1),
(0..half_size.x, half_size.y..mesh_size.y - 1),
]
.into_iter()
.zip(&mut quadrants)
{
let offset = geometry_buffer_mut.len();
for iy in y_range {
let iy_next = iy + 1;
for x in x_range.clone() {
let x_next = x + 1;
let i0 = iy * mesh_size.x + x;
let i1 = iy_next * mesh_size.x + x;
let i2 = iy_next * mesh_size.x + x_next;
let i3 = iy * mesh_size.x + x_next;
geometry_buffer_mut.push(TriangleDefinition([i0, i1, i2]));
geometry_buffer_mut.push(TriangleDefinition([i2, i3, i0]));
}
}
*quadrant = ElementRange::Specific {
offset,
count: geometry_buffer_mut.len() - offset,
};
}
drop(geometry_buffer_mut);
Self {
data: SurfaceResource::new_ok(Uuid::new_v4(), ResourceKind::Embedded, surface_data),
quadrants,
}
}
}
#[cfg(test)]
mod tests {
use crate::scene::mesh::buffer::VertexAttributeUsage;
use super::*;
#[test]
fn geometry_3x3() {
let g = TerrainGeometry::new(Vector2::new(3, 3));
for (i, q) in g.quadrants.iter().copied().enumerate() {
let ElementRange::Specific { count, .. } = q else {
panic!("Quadrant is full.")
};
assert_eq!(count, 2, "Quadrant: {i}");
}
}
#[test]
fn geometry_4x4() {
let g = TerrainGeometry::new(Vector2::new(4, 4));
let counts = [2, 4, 8, 4];
for (i, (q, c)) in g.quadrants.iter().copied().zip(counts).enumerate() {
let ElementRange::Specific { count, .. } = q else {
panic!("Quadrant is full.")
};
assert_eq!(count, c, "Quadrant: {i}");
}
}
#[test]
fn geometry_5x5() {
let g = TerrainGeometry::new(Vector2::new(5, 5));
for (i, q) in g.quadrants.iter().copied().enumerate() {
let ElementRange::Specific { count, .. } = q else {
panic!("Quadrant is full.")
};
assert_eq!(count, 8, "Quadrant: {i}");
}
}
#[test]
fn normals() {
let g = TerrainGeometry::new(Vector2::new(3, 3));
let vertices = &g.data.data_ref().vertex_buffer;
let attr_view = vertices
.attribute_view::<Vector3<f32>>(VertexAttributeUsage::Normal)
.unwrap();
for i in 0..vertices.vertex_count() as usize {
assert_eq!(
attr_view.get(i).unwrap().clone(),
Vector3::<f32>::new(0.0, 1.0, 0.0)
);
}
}
#[test]
fn tangents() {
let g = TerrainGeometry::new(Vector2::new(3, 3));
let vertices = &g.data.data_ref().vertex_buffer;
let attr_view = vertices
.attribute_view::<Vector4<f32>>(VertexAttributeUsage::Tangent)
.unwrap();
for i in 0..vertices.vertex_count() as usize {
assert_eq!(
attr_view.get(i).unwrap().clone(),
Vector4::<f32>::new(1.0, 0.0, 0.0, -1.0)
);
}
}
}