1use crate::core::*;
2use crate::renderer::*;
3use std::sync::Arc;
4
5pub enum Lod {
7 High,
9 Medium,
11 Low,
13}
14
15const VERTICES_PER_SIDE: usize = 33;
16
17pub struct Terrain<M: Material> {
21 context: Context,
22 center: (i32, i32),
23 patches: Vec<Gm<TerrainPatch, M>>,
24 index_buffer1: Arc<ElementBuffer<u32>>,
25 index_buffer4: Arc<ElementBuffer<u32>>,
26 index_buffer16: Arc<ElementBuffer<u32>>,
27 material: M,
28 lod: Arc<dyn Fn(f32) -> Lod + Send + Sync>,
29 height_map: Arc<dyn Fn(f32, f32) -> f32 + Send + Sync>,
30 side_length: f32,
31 vertex_distance: f32,
32}
33impl<M: Material + Clone> Terrain<M> {
34 pub fn new(
39 context: &Context,
40 material: M,
41 height_map: Arc<dyn Fn(f32, f32) -> f32 + Send + Sync>,
42 side_length: f32,
43 vertex_distance: f32,
44 center: Vec2,
45 ) -> Self {
46 let index_buffer1 = Self::indices(context, 1);
47 let mut patches = Vec::new();
48 let (x0, y0) = pos2patch(vertex_distance, center);
49 let half_patches_per_side = half_patches_per_side(vertex_distance, side_length);
50 for ix in x0 - half_patches_per_side..x0 + half_patches_per_side + 1 {
51 for iy in y0 - half_patches_per_side..y0 + half_patches_per_side + 1 {
52 let patch = TerrainPatch::new(
53 context,
54 &*height_map.clone(),
55 (ix, iy),
56 index_buffer1.clone(),
57 vertex_distance,
58 );
59 patches.push(Gm::new(patch, material.clone()));
60 }
61 }
62 Self {
63 context: context.clone(),
64 center: (x0, y0),
65 patches,
66 index_buffer1,
67 index_buffer4: Self::indices(context, 4),
68 index_buffer16: Self::indices(context, 16),
69 lod: Arc::new(|_| Lod::High),
70 material,
71 height_map,
72 side_length,
73 vertex_distance,
74 }
75 }
76
77 pub fn height_at(&self, position: Vec2) -> f32 {
81 (*self.height_map)(position.x, position.y)
82 }
83
84 pub fn set_lod(&mut self, lod: Arc<dyn Fn(f32) -> Lod + Send + Sync>) {
89 self.lod = lod;
90 }
91
92 pub fn set_center(&mut self, center: Vec2) {
97 let (x0, y0) = pos2patch(self.vertex_distance, center);
98 let half_patches_per_side = half_patches_per_side(self.vertex_distance, self.side_length);
99
100 while x0 > self.center.0 {
101 self.center.0 += 1;
102 for iy in
103 self.center.1 - half_patches_per_side..self.center.1 + half_patches_per_side + 1
104 {
105 self.patches.push(Gm::new(
106 TerrainPatch::new(
107 &self.context,
108 &*self.height_map.clone(),
109 (self.center.0 + half_patches_per_side, iy),
110 self.index_buffer1.clone(),
111 self.vertex_distance,
112 ),
113 self.material.clone(),
114 ));
115 }
116 }
117
118 while x0 < self.center.0 {
119 self.center.0 -= 1;
120 for iy in
121 self.center.1 - half_patches_per_side..self.center.1 + half_patches_per_side + 1
122 {
123 self.patches.push(Gm::new(
124 TerrainPatch::new(
125 &self.context,
126 &*self.height_map.clone(),
127 (self.center.0 - half_patches_per_side, iy),
128 self.index_buffer1.clone(),
129 self.vertex_distance,
130 ),
131 self.material.clone(),
132 ));
133 }
134 }
135 while y0 > self.center.1 {
136 self.center.1 += 1;
137 for ix in
138 self.center.0 - half_patches_per_side..self.center.0 + half_patches_per_side + 1
139 {
140 self.patches.push(Gm::new(
141 TerrainPatch::new(
142 &self.context,
143 &*self.height_map.clone(),
144 (ix, self.center.1 + half_patches_per_side),
145 self.index_buffer1.clone(),
146 self.vertex_distance,
147 ),
148 self.material.clone(),
149 ));
150 }
151 }
152
153 while y0 < self.center.1 {
154 self.center.1 -= 1;
155 for ix in
156 self.center.0 - half_patches_per_side..self.center.0 + half_patches_per_side + 1
157 {
158 self.patches.push(Gm::new(
159 TerrainPatch::new(
160 &self.context,
161 &*self.height_map.clone(),
162 (ix, self.center.1 - half_patches_per_side),
163 self.index_buffer1.clone(),
164 self.vertex_distance,
165 ),
166 self.material.clone(),
167 ));
168 }
169 }
170
171 self.patches.retain(|p| {
172 let (ix, iy) = p.index();
173 (x0 - ix).abs() <= half_patches_per_side && (y0 - iy).abs() <= half_patches_per_side
174 });
175
176 self.patches.iter_mut().for_each(|p| {
177 let distance = p.center().distance(center);
178 p.index_buffer = match (*self.lod)(distance) {
179 Lod::Low => self.index_buffer16.clone(),
180 Lod::Medium => self.index_buffer4.clone(),
181 Lod::High => self.index_buffer1.clone(),
182 };
183 })
184 }
185
186 fn indices(context: &Context, resolution: u32) -> Arc<ElementBuffer<u32>> {
187 let mut indices: Vec<u32> = Vec::new();
188 let stride = VERTICES_PER_SIDE as u32;
189 let max = (stride - 1) / resolution;
190 for r in 0..max {
191 for c in 0..max {
192 indices.push(r * resolution + c * resolution * stride);
193 indices.push(r * resolution + resolution + c * resolution * stride);
194 indices.push(r * resolution + (c * resolution + resolution) * stride);
195 indices.push(r * resolution + (c * resolution + resolution) * stride);
196 indices.push(r * resolution + resolution + c * resolution * stride);
197 indices.push(r * resolution + resolution + (c * resolution + resolution) * stride);
198 }
199 }
200 Arc::new(ElementBuffer::new_with_data(context, &indices))
201 }
202}
203
204impl<'a, M: Material> IntoIterator for &'a Terrain<M> {
205 type Item = &'a dyn Object;
206 type IntoIter = std::vec::IntoIter<&'a dyn Object>;
207
208 fn into_iter(self) -> Self::IntoIter {
209 self.patches
210 .iter()
211 .map(|m| m as &dyn Object)
212 .collect::<Vec<_>>()
213 .into_iter()
214 }
215}
216
217fn patch_size(vertex_distance: f32) -> f32 {
218 vertex_distance * (VERTICES_PER_SIDE - 1) as f32
219}
220
221fn half_patches_per_side(vertex_distance: f32, side_length: f32) -> i32 {
222 let patch_size = patch_size(vertex_distance);
223 let patches_per_side = (side_length / patch_size).ceil() as u32;
224 (patches_per_side as i32 - 1) / 2
225}
226
227fn pos2patch(vertex_distance: f32, position: Vec2) -> (i32, i32) {
228 let patch_size = vertex_distance * (VERTICES_PER_SIDE - 1) as f32;
229 (
230 (position.x / patch_size).floor() as i32,
231 (position.y / patch_size).floor() as i32,
232 )
233}
234
235struct TerrainPatch {
236 context: Context,
237 index: (i32, i32),
238 positions_buffer: VertexBuffer<Vec3>,
239 normals_buffer: VertexBuffer<Vec3>,
240 center: Vec2,
241 aabb: AxisAlignedBoundingBox,
242 pub index_buffer: Arc<ElementBuffer<u32>>,
243}
244
245impl TerrainPatch {
246 pub fn new(
247 context: &Context,
248 height_map: impl Fn(f32, f32) -> f32 + Clone,
249 index: (i32, i32),
250 index_buffer: Arc<ElementBuffer<u32>>,
251 vertex_distance: f32,
252 ) -> Self {
253 let patch_size = patch_size(vertex_distance);
254 let offset = vec2(index.0 as f32 * patch_size, index.1 as f32 * patch_size);
255 let positions = Self::positions(height_map.clone(), offset, vertex_distance);
256 let aabb = AxisAlignedBoundingBox::new_with_positions(&positions);
257 let normals = Self::normals(height_map, offset, &positions, vertex_distance);
258
259 let positions_buffer = VertexBuffer::new_with_data(context, &positions);
260 let normals_buffer = VertexBuffer::new_with_data(context, &normals);
261 Self {
262 context: context.clone(),
263 index,
264 index_buffer,
265 positions_buffer,
266 normals_buffer,
267 aabb,
268 center: offset + vec2(0.5 * patch_size, 0.5 * patch_size),
269 }
270 }
271
272 pub fn center(&self) -> Vec2 {
273 self.center
274 }
275
276 pub fn index(&self) -> (i32, i32) {
277 self.index
278 }
279
280 fn positions(
281 height_map: impl Fn(f32, f32) -> f32,
282 offset: Vec2,
283 vertex_distance: f32,
284 ) -> Vec<Vec3> {
285 let mut data = vec![vec3(0.0, 0.0, 0.0); VERTICES_PER_SIDE * VERTICES_PER_SIDE];
286 for r in 0..VERTICES_PER_SIDE {
287 for c in 0..VERTICES_PER_SIDE {
288 let vertex_id = r * VERTICES_PER_SIDE + c;
289 let x = offset.x + r as f32 * vertex_distance;
290 let z = offset.y + c as f32 * vertex_distance;
291 data[vertex_id] = vec3(x, height_map(x, z), z);
292 }
293 }
294 data
295 }
296
297 fn normals(
298 height_map: impl Fn(f32, f32) -> f32,
299 offset: Vec2,
300 positions: &[Vec3],
301 vertex_distance: f32,
302 ) -> Vec<Vec3> {
303 let mut data = vec![vec3(0.0, 0.0, 0.0); VERTICES_PER_SIDE * VERTICES_PER_SIDE];
304 let h = vertex_distance;
305 for r in 0..VERTICES_PER_SIDE {
306 for c in 0..VERTICES_PER_SIDE {
307 let vertex_id = r * VERTICES_PER_SIDE + c;
308 let x = offset.x + r as f32 * vertex_distance;
309 let z = offset.y + c as f32 * vertex_distance;
310 let xp = if r == VERTICES_PER_SIDE - 1 {
311 height_map(x + h, z)
312 } else {
313 positions[vertex_id + VERTICES_PER_SIDE][1]
314 };
315 let xm = if r == 0 {
316 height_map(x - h, z)
317 } else {
318 positions[vertex_id - VERTICES_PER_SIDE][1]
319 };
320 let zp = if c == VERTICES_PER_SIDE - 1 {
321 height_map(x, z + h)
322 } else {
323 positions[vertex_id + 1][1]
324 };
325 let zm = if c == 0 {
326 height_map(x, z - h)
327 } else {
328 positions[vertex_id - 1][1]
329 };
330 let dx = xp - xm;
331 let dz = zp - zm;
332 data[vertex_id] = vec3(-dx, 2.0 * h, -dz).normalize();
333 }
334 }
335 data
336 }
337}
338
339impl Geometry for TerrainPatch {
340 fn vertex_shader_source(&self) -> String {
341 include_str!("shaders/terrain.vert").to_owned()
342 }
343
344 fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
345 program.use_uniform("viewProjectionMatrix", viewer.projection() * viewer.view());
346 program.use_vertex_attribute("position", &self.positions_buffer);
347 if program.requires_attribute("normal") {
348 program.use_vertex_attribute("normal", &self.normals_buffer);
349 }
350 program.draw_elements(render_states, viewer.viewport(), &self.index_buffer);
351 }
352
353 fn id(&self) -> GeometryId {
354 GeometryId::TerrainPatch
355 }
356
357 fn render_with_material(
358 &self,
359 material: &dyn Material,
360 viewer: &dyn Viewer,
361 lights: &[&dyn Light],
362 ) {
363 render_with_material(&self.context, viewer, &self, material, lights);
364 }
365
366 fn render_with_effect(
367 &self,
368 material: &dyn Effect,
369 viewer: &dyn Viewer,
370 lights: &[&dyn Light],
371 color_texture: Option<ColorTexture>,
372 depth_texture: Option<DepthTexture>,
373 ) {
374 render_with_effect(
375 &self.context,
376 viewer,
377 self,
378 material,
379 lights,
380 color_texture,
381 depth_texture,
382 )
383 }
384
385 fn aabb(&self) -> AxisAlignedBoundingBox {
386 self.aabb
387 }
388}