1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use ahash::AHashMap;
use bevy::{
    prelude::{Bundle, Component, Mesh, Transform},
    render::{mesh::Indices, render_asset::RenderAssetUsages, render_resource::PrimitiveTopology},
    utils::FloatOrd,
};
use de_map::size::MapBounds;
use de_types::projection::{ToAltitude, ToFlat};
use glam::{Vec2, Vec3};
use parry3d::{
    math::Isometry,
    na::{DMatrix, Vector3},
    query::{Ray, RayCast, RayIntersection},
    shape::HeightField,
};

use crate::shader::UV_SCALE;

#[derive(Bundle)]
pub struct TerrainBundle {
    transform: Transform,
    terrain: Terrain,
}

impl TerrainBundle {
    pub fn flat(bounds: MapBounds) -> Self {
        let transform = Transform::from_translation(Vec3::from(bounds.aabb().to_msl().center()));
        let size = bounds.size();
        let terrain = Terrain::new(HeightField::new(
            DMatrix::from_row_slice(2, 2, &[0., 0., 0., 0.]),
            Vector3::new(size.x, 1., size.y),
        ));

        Self { transform, terrain }
    }
}

#[derive(Component)]
pub struct Terrain {
    heightfield: HeightField,
}

impl Terrain {
    fn new(heightfield: HeightField) -> Self {
        Self { heightfield }
    }

    pub(crate) fn cast_ray(
        &self,
        m: &Isometry<f32>,
        ray: &Ray,
        max_toi: f32,
    ) -> Option<RayIntersection> {
        self.heightfield
            .cast_ray_and_get_normal(m, ray, max_toi, true)
    }

    pub(crate) fn generate_mesh(&self, translation: Vec3) -> Mesh {
        let translation = translation.to_flat();

        let mut point_to_index: AHashMap<[FloatOrd; 2], u32> = AHashMap::new();
        let mut indices: Vec<u32> = Vec::new();

        let mut positions = Vec::<[f32; 3]>::new();
        let mut normals = Vec::<[f32; 3]>::new();
        let mut uvs = Vec::<[f32; 2]>::new();

        for triangle in self.heightfield.triangles() {
            for point in [triangle.a, triangle.c, triangle.b] {
                let key = [FloatOrd(point.x), FloatOrd(point.z)];
                match point_to_index.get(&key) {
                    Some(&index) => indices.push(index),
                    None => {
                        let index = point_to_index.len() as u32;
                        point_to_index.insert(key, index);
                        indices.push(index);

                        let world = Vec2::new(point.x, point.z).to_msl();
                        positions.push([world.x, point.y, world.z]);
                        normals.push([0., 1., 0.]);
                        uvs.push([
                            (point.x + translation.x) / UV_SCALE,
                            (point.z + translation.y) / UV_SCALE,
                        ]);
                    }
                }
            }
        }

        let mut mesh = Mesh::new(
            PrimitiveTopology::TriangleList,
            RenderAssetUsages::RENDER_WORLD,
        );
        mesh.insert_indices(Indices::U32(indices));
        mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
        mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
        mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
        mesh
    }
}