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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use dotrix_core::{ Id };
use dotrix_core::assets::{ Texture, Mesh };
use dotrix_math::{ Vec3, InnerSpace };
use crate::{ Heightmap, Generator };
pub struct Terrain {
pub view_distance: f32,
pub max_lod: usize,
pub tile_size: usize,
pub spawn_if_moved_by: f32,
pub force_spawn: bool,
pub heightmap: Box<dyn Heightmap>,
pub texture: Id<Texture>,
pub texture_heights: Vec<f32>,
}
impl Terrain {
pub fn new(heightmap: Box<dyn Heightmap>, texture_heights: Vec<f32>) -> Self {
Self {
view_distance: 500.0,
max_lod: 4,
tile_size: 240,
spawn_if_moved_by: 256.0,
force_spawn: true,
heightmap,
texture: Id::default(),
texture_heights,
}
}
pub fn generate_tile_mesh(&self, tile_x: i32, tile_z: i32, lod: usize) -> Mesh {
let tile_size = self.tile_size;
let vertices_per_side = tile_size + 1;
let offset = self.tile_size as i32 / 2;
let scale = 2_i32.pow(lod as u32);
let capacity = vertices_per_side * vertices_per_side;
let mut positions = Vec::with_capacity(capacity);
let mut uvs = Vec::with_capacity(capacity);
let mut normals = vec![[0.0, 0.0, 0.0]; capacity];
let mut indices = Vec::with_capacity(3 * 2 * self.tile_size * self.tile_size);
let half_world_size = ((self.heightmap.size() - 1) / 2) as i32;
for z in -offset..=offset {
let world_z = tile_z + z * scale;
let map_z = if world_z < -half_world_size { 0 } else { world_z + half_world_size };
for x in -offset..=offset {
let world_x = tile_x + x * scale;
let map_x = if world_x < -half_world_size { 0 } else { world_x + half_world_size};
let world_y = self.heightmap.value(map_x as usize, map_z as usize);
positions.push([world_x as f32, world_y, world_z as f32]);
uvs.push([
(x + offset) as f32 / 2.0 / offset as f32,
(z + offset) as f32 / 2.0 / offset as f32,
]);
}
}
for z in 0..tile_size {
let i = (z * vertices_per_side) as u32;
for x in 0..tile_size {
let i00 = i + x as u32;
let i10 = i00 + 1;
let i01 = i00 + vertices_per_side as u32;
let i11 = i01 + 1;
indices.push(i10);
indices.push(i00);
indices.push(i01);
indices.push(i10);
indices.push(i01);
indices.push(i11);
}
}
let indices_count = indices.len();
for i in (0..indices_count).step_by(3) {
let i0 = indices[i] as usize;
let i1 = indices[i + 1] as usize;
let i2 = indices[i + 2] as usize;
let p0 = Vec3::from(positions[i0]);
let p1 = Vec3::from(positions[i1]);
let p2 = Vec3::from(positions[i2]);
let n1 = p1 - p0;
let n2 = p2 - p0;
let normal = n1.cross(n2).normalize();
normals[i0] = (Vec3::from(normals[i0]) + normal).into();
normals[i1] = (Vec3::from(normals[i1]) + normal).into();
normals[i2] = (Vec3::from(normals[i2]) + normal).into();
}
for normal in normals.iter_mut() {
let normalized = Vec3::from(*normal).normalize();
normal[0] = normalized.x;
normal[1] = normalized.y;
normal[2] = normalized.z;
}
let mut mesh = Mesh::default();
mesh.with_vertices(&positions);
mesh.with_vertices(&normals);
mesh.with_vertices(&uvs);
mesh.with_indices(&indices);
mesh
}
pub fn uv_from_height(&self, height: f32) -> [f32; 2] {
let mut i = 0.0;
for (idx, &tx_height) in self.texture_heights.iter().enumerate() {
if height > tx_height {
i = idx as f32;
} else {
break;
}
}
let value = i / self.texture_heights.len() as f32;
[value, value]
}
}
impl Default for Terrain {
fn default() -> Self {
let texture_heights = vec![-1024.0, -128.0, -100.0, 0.0, 32.0];
Self::new(Box::new(Generator::default()), texture_heights)
}
}