Skip to main content

qualia_core_db/domains/geospatial/
dem.rs

1#[derive(Debug, Clone)]
2pub struct TerrainMesh {
3    pub vertices: Vec<[f32; 3]>,
4    pub indices: Vec<u32>,
5}
6
7/// Generates a triangulated terrain mesh from a 2D array of height values.
8/// The `heightfield` is expected to be in row-major order.
9/// `cell_size` is the spatial distance between adjacent height samples.
10pub fn generate_terrain_mesh(
11    heightfield: &[f32],
12    width: usize,
13    height: usize,
14    cell_size: f64,
15) -> TerrainMesh {
16    assert_eq!(
17        heightfield.len(),
18        width * height,
19        "Heightfield length must match width * height"
20    );
21
22    let mut vertices = Vec::with_capacity(width * height);
23    let mut indices = Vec::with_capacity((width - 1) * (height - 1) * 6);
24
25    // offset so the center of the grid is at (0, 0)
26    let offset_x = (width as f64 * cell_size) / 2.0;
27    let offset_y = (height as f64 * cell_size) / 2.0;
28
29    for y in 0..height {
30        for x in 0..width {
31            let px = (x as f64 * cell_size) - offset_x;
32            let py = (y as f64 * cell_size) - offset_y;
33            let pz = heightfield[y * width + x] as f64;
34            vertices.push([px as f32, py as f32, pz as f32]);
35        }
36    }
37
38    for y in 0..(height - 1) {
39        for x in 0..(width - 1) {
40            let i0 = (y * width + x) as u32;
41            let i1 = i0 + 1;
42            let i2 = ((y + 1) * width + x) as u32;
43            let i3 = i2 + 1;
44
45            // First triangle (bottom-left)
46            indices.push(i0);
47            indices.push(i2);
48            indices.push(i1);
49
50            // Second triangle (top-right)
51            indices.push(i1);
52            indices.push(i2);
53            indices.push(i3);
54        }
55    }
56
57    TerrainMesh { vertices, indices }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_generate_terrain_mesh() {
66        let width = 3;
67        let height = 3;
68        let heights = vec![0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0];
69
70        let mesh = generate_terrain_mesh(&heights, width, height, 10.0);
71
72        assert_eq!(mesh.vertices.len(), 9);
73        // (width - 1) * (height - 1) * 2 triangles = 4 * 2 = 8 triangles = 24 indices
74        assert_eq!(mesh.indices.len(), 24);
75
76        // Center vertex should have height 2.0
77        assert_eq!(mesh.vertices[4][2], 2.0);
78    }
79}