1use crate::{
2 mesh::{
3 MeshGen,
4 greedy::{self, GreedyConfig, GreedyMesh},
5 terrain::FaceKind,
6 },
7 render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex, pipelines::FigureSpriteAtlasData},
8 scene::math,
9};
10use common::{
11 figure::{Cell, CellSurface},
12 terrain::Block,
13 vol::{BaseVol, FilledVox, ReadVol, SizedVol},
14};
15use core::convert::TryFrom;
16use vek::*;
17
18pub fn generate_mesh_base_vol_figure<'a: 'b, 'b, V>(
22 vol: V,
23 (greedy, opaque_mesh, offs, scale, bone_idx): (
24 &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
25 &'b mut Mesh<TerrainVertex>,
26 Vec3<f32>,
27 Vec3<f32>,
28 u8,
29 ),
30) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
31where
32 V: BaseVol<Vox = Cell> + ReadVol + SizedVol + 'a,
33{
34 assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
35 let max_size = greedy.max_size();
36 assert!(max_size.reduce_max() < 1 << 15);
41
42 let lower_bound = vol.lower_bound();
43 let upper_bound = vol.upper_bound();
44 assert!(
45 lower_bound.x <= upper_bound.x
46 && lower_bound.y <= upper_bound.y
47 && lower_bound.z <= upper_bound.z
48 );
49 let greedy_size = upper_bound - lower_bound + 1;
51 assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
52 let greedy_size = greedy_size.as_::<usize>();
55 let greedy_size_cross = greedy_size;
56 let draw_delta = lower_bound;
57
58 let get_light = |vol: &mut V, pos: Vec3<i32>| {
59 vol.get(pos).map_or(true, |vox| !vox.is_filled()) as i32 as f32
60 };
61 let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
62 let get_opacity =
63 |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| !vox.is_filled());
64 let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
65 should_draw_greedy(pos, delta, uv, |vox| {
66 vol.get(vox).copied().unwrap_or_else(|_| Cell::empty())
67 })
68 };
69 let create_opaque = |atlas_pos, pos, norm| {
70 TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
71 };
72
73 greedy.push(GreedyConfig {
74 data: vol,
75 draw_delta,
76 greedy_size,
77 greedy_size_cross,
78 get_ao: |_: &mut V, _: Vec3<i32>| 1.0,
79 get_light,
80 get_glow,
81 get_opacity,
82 should_draw,
83 push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
84 opaque_mesh.push_quad(greedy::create_quad(
85 atlas_origin,
86 dim,
87 origin,
88 draw_dim,
89 norm,
90 meta,
91 |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
92 ));
93 },
94 make_face_texel: |col_light: &mut [u8; 4], vol: &mut V, pos, light, _, _| {
95 let cell = vol.get(pos).ok();
96 let col = cell
97 .and_then(|vox| vox.get_color())
98 .unwrap_or_else(Rgb::zero);
99 let surf = cell
100 .and_then(|vox| vox.get_surf())
101 .unwrap_or(CellSurface::Matte);
102 *col_light = TerrainVertex::make_col_light_figure(light, col, surf);
103 },
104 });
105 let bounds = math::Aabb {
106 min: ((lower_bound.as_::<f32>() + offs) * scale),
108 max: ((upper_bound.as_::<f32>() + offs) * scale),
109 }
110 .made_valid();
111
112 (Mesh::new(), Mesh::new(), Mesh::new(), bounds)
113}
114
115pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V>(
119 vol: V,
120 (greedy, opaque_mesh, offs, scale, bone_idx): (
121 &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
122 &'b mut Mesh<TerrainVertex>,
123 Vec3<f32>,
124 Vec3<f32>,
125 u8,
126 ),
127) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
128where
129 V: BaseVol<Vox = Block> + ReadVol + SizedVol + 'a,
130{
131 assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
132 let max_size = greedy.max_size();
133 assert!(max_size.reduce_max() < 1 << 15);
138
139 let lower_bound = vol.lower_bound();
140 let upper_bound = vol.upper_bound();
141 assert!(
142 lower_bound.x <= upper_bound.x
143 && lower_bound.y <= upper_bound.y
144 && lower_bound.z <= upper_bound.z
145 );
146 let greedy_size = upper_bound - lower_bound + 1;
148 assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
149 let greedy_size = greedy_size.as_::<usize>();
152 let greedy_size_cross = greedy_size;
153 let draw_delta = lower_bound;
154
155 let get_light =
156 |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_fluid()) as i32 as f32;
157 let get_ao =
158 |vol: &mut V, pos: Vec3<i32>| vol.get(pos).is_ok_and(|vox| vox.is_opaque()) as i32 as f32;
159 let get_glow = |vol: &mut V, pos: Vec3<i32>| {
160 vol.get(pos)
161 .ok()
162 .and_then(|vox| vox.get_glow())
163 .unwrap_or(0) as f32
164 / 255.0
165 };
166 let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_fluid());
167 let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
168 super::terrain::should_draw_greedy(pos, delta, |vox| {
169 vol.get(vox).copied().unwrap_or_else(|_| Block::empty())
170 })
171 };
172
173 let create_opaque = |atlas_pos, pos, norm| {
174 TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
175 };
176
177 greedy.push(GreedyConfig {
178 data: vol,
179 draw_delta,
180 greedy_size,
181 greedy_size_cross,
182 get_ao,
183 get_light,
184 get_glow,
185 get_opacity,
186 should_draw,
187 push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
188 FaceKind::Opaque(meta) => {
189 opaque_mesh.push_quad(greedy::create_quad(
190 atlas_origin,
191 dim,
192 origin,
193 draw_dim,
194 norm,
195 meta,
196 |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
197 ));
198 },
199 FaceKind::Fluid => {},
200 },
201 make_face_texel: |col_light: &mut [u8; 4], vol: &mut V, pos, light, _, _| {
202 let block = vol.get(pos).ok();
203 let col = block
204 .and_then(|vox| vox.get_color())
205 .unwrap_or_else(Rgb::zero);
206 let surf = if block.is_some_and(|c| c.get_glow().is_some()) {
207 CellSurface::Glowy
208 } else {
209 CellSurface::Matte
210 };
211 *col_light = TerrainVertex::make_col_light_figure(light, col, surf);
212 },
213 });
214 let bounds = math::Aabb {
215 min: ((lower_bound.as_::<f32>() + offs) * scale),
217 max: ((upper_bound.as_::<f32>() + offs) * scale),
218 }
219 .made_valid();
220
221 (Mesh::new(), Mesh::new(), Mesh::new(), bounds)
222}
223
224pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V>(
225 vol: V,
226 (greedy, opaque_mesh, vertical_stripes): (
227 &'b mut GreedyMesh<'a, FigureSpriteAtlasData, greedy::SpriteAtlasAllocator>,
228 &'b mut Mesh<SpriteVertex>,
229 bool,
230 ),
231 offset: Vec3<f32>,
232) -> MeshGen<SpriteVertex, SpriteVertex, TerrainVertex, ()>
233where
234 V: BaseVol<Vox = Cell> + ReadVol + SizedVol + 'a,
235{
236 let max_size = greedy.max_size();
237 assert!(u32::from(max_size.reduce_max()) < 1 << 16);
242
243 let lower_bound = vol.lower_bound();
244 let upper_bound = vol.upper_bound();
245 assert!(
246 lower_bound.x <= upper_bound.x
247 && lower_bound.y <= upper_bound.y
248 && lower_bound.z <= upper_bound.z
249 );
250 assert!(
253 i16::try_from(lower_bound.x).is_ok()
254 && i16::try_from(lower_bound.y).is_ok()
255 && i16::try_from(lower_bound.z).is_ok(),
256 "Sprite offsets should fit in i16",
257 );
258 let greedy_size = upper_bound - lower_bound + 1;
259 assert!(
261 greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
262 "Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
263 greedy_size - 1
264 );
265
266 let (flat, flat_get) = {
267 let (w, h, d) = (greedy_size + 2).into_tuple();
268 let flat = {
269 let mut flat = vec![Cell::empty(); (w * h * d) as usize];
270 let mut i = 0;
271 for x in -1..greedy_size.x + 1 {
272 for y in -1..greedy_size.y + 1 {
273 for z in -1..greedy_size.z + 1 {
274 let wpos = lower_bound + Vec3::new(x, y, z);
275 let block = vol.get(wpos).copied().unwrap_or_else(|_| Cell::empty());
276 flat[i] = block;
277 i += 1;
278 }
279 }
280 }
281 flat
282 };
283
284 let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
285 .get((x * h * d + y * d + z) as usize)
286 .copied()
287 {
288 Some(b) => b,
289 None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
290 };
291
292 (flat, flat_get)
293 };
294
295 let greedy_size = greedy_size.as_::<usize>();
298
299 let greedy_size_cross = greedy_size;
300 let draw_delta = Vec3::new(1, 1, 1);
301
302 let get_light =
303 move |flat: &mut _, pos: Vec3<i32>| !flat_get(flat, pos).is_filled() as i32 as f32;
304 let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
305 let get_color = move |flat: &mut _, pos: Vec3<i32>| {
306 flat_get(flat, pos).get_color().unwrap_or_else(Rgb::zero)
307 };
308 let get_opacity = move |flat: &mut _, pos: Vec3<i32>| !flat_get(flat, pos).is_filled();
309 let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
310 should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
311 };
312 let mesh_delta = lower_bound.as_::<f32>();
315 let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
316 SpriteVertex::new(atlas_pos, pos + offset + mesh_delta, norm)
317 };
318
319 greedy.push(GreedyConfig {
320 data: flat,
321 draw_delta,
322 greedy_size,
323 greedy_size_cross,
324 get_ao: |_: &mut _, _: Vec3<i32>| 1.0,
325 get_light,
326 get_glow,
327 get_opacity,
328 should_draw,
329 push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
330 opaque_mesh.push_quad(greedy::create_quad(
331 atlas_origin,
332 dim,
333 origin,
334 draw_dim,
335 norm,
336 meta,
337 |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
338 ));
339 },
340 make_face_texel: move |col_light: &mut [u8; 4], flat: &mut _, pos, light, _glow, _ao| {
341 let cell = flat_get(flat, pos);
342 let surf = cell.get_surf().unwrap_or(CellSurface::Matte);
343 *col_light = TerrainVertex::make_col_light_figure(light, get_color(flat, pos), surf);
344 },
345 });
346
347 (Mesh::new(), Mesh::new(), Mesh::new(), ())
348}
349
350pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V>(
351 vol: V,
352 greedy: &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
353) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
354where
355 V: BaseVol<Vox = Cell> + ReadVol + SizedVol + 'a,
356{
357 let max_size = greedy.max_size();
358 assert!(u32::from(max_size.reduce_max()) < 1 << 16);
363
364 let lower_bound = vol.lower_bound();
365 let upper_bound = vol.upper_bound();
366 assert!(
367 lower_bound.x <= upper_bound.x
368 && lower_bound.y <= upper_bound.y
369 && lower_bound.z <= upper_bound.z
370 );
371 let greedy_size = upper_bound - lower_bound + 1;
372 assert!(
373 greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
374 "Particle size out of bounds: {:?} ≤ (15, 15, 63)",
375 greedy_size - 1
376 );
377 let greedy_size = greedy_size.as_::<usize>();
380
381 let greedy_size_cross = greedy_size;
382 let draw_delta = lower_bound;
383
384 let get_light = |vol: &mut V, pos: Vec3<i32>| {
385 vol.get(pos).map_or(true, |vox| !vox.is_filled()) as i32 as f32
386 };
387 let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
388 let get_color = |vol: &mut V, pos: Vec3<i32>| {
389 vol.get(pos)
390 .ok()
391 .and_then(|vox| vox.get_color())
392 .unwrap_or_else(Rgb::zero)
393 };
394 let get_opacity =
395 |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| !vox.is_filled());
396 let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
397 should_draw_greedy(pos, delta, uv, |vox| {
398 vol.get(vox).copied().unwrap_or_else(|_| Cell::empty())
399 })
400 };
401 let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
402
403 let mut opaque_mesh = Mesh::new();
404 greedy.push(GreedyConfig {
405 data: vol,
406 draw_delta,
407 greedy_size,
408 greedy_size_cross,
409 get_ao: |_: &mut V, _: Vec3<i32>| 1.0,
410 get_light,
411 get_glow,
412 get_opacity,
413 should_draw,
414 push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
415 opaque_mesh.push_quad(greedy::create_quad(
416 atlas_origin,
417 dim,
418 origin,
419 draw_dim,
420 norm,
421 meta,
422 |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
423 ));
424 },
425 make_face_texel: move |col_light: &mut [u8; 4], vol: &mut V, pos, light, _glow, _ao| {
426 *col_light = TerrainVertex::make_col_light_figure(
427 light,
428 get_color(vol, pos),
429 CellSurface::Matte,
430 );
431 },
432 });
433
434 (opaque_mesh, Mesh::new(), Mesh::new(), ())
435}
436
437fn should_draw_greedy(
438 pos: Vec3<i32>,
439 delta: Vec3<i32>,
440 _uv: Vec2<Vec3<i32>>,
441 flat_get: impl Fn(Vec3<i32>) -> Cell,
442) -> Option<(bool, ())> {
443 let from = flat_get(pos - delta);
444 let to = flat_get(pos);
445 let from_opaque = from.is_filled();
446 if from_opaque == to.is_filled() {
447 None
448 } else {
449 Some((from_opaque, ()))
452 }
453}
454
455fn should_draw_greedy_ao(
456 vertical_stripes: bool,
457 pos: Vec3<i32>,
458 delta: Vec3<i32>,
459 _uv: Vec2<Vec3<i32>>,
460 flat_get: impl Fn(Vec3<i32>) -> Cell,
461) -> Option<(bool, bool)> {
462 let from = flat_get(pos - delta);
463 let to = flat_get(pos);
464 let from_opaque = from.is_filled();
465 if from_opaque == to.is_filled() {
466 None
467 } else {
468 let faces_forward = from_opaque;
469 let ao = !vertical_stripes || (pos.z & 1) != 0;
470 Some((faces_forward, ao))
473 }
474}