Improve figure meshing.

This commit is contained in:
Joshua Yanovski 2022-08-25 23:25:21 -07:00
parent 053941e16a
commit ae77c1814c
6 changed files with 131 additions and 63 deletions

View File

@ -25,6 +25,12 @@ impl CellData {
} }
pub fn is_hollow(&self) -> bool { self.attr.get() & HOLLOW != 0 } pub fn is_hollow(&self) -> bool { self.attr.get() & HOLLOW != 0 }
#[inline(always)]
pub fn is_glowy(&self) -> bool { self.attr.get() & GLOWY != 0 }
#[inline(always)]
pub fn is_shiny(&self) -> bool { self.attr.get() & SHINY != 0 }
} }
impl Default for CellData { impl Default for CellData {
@ -73,8 +79,10 @@ impl Cell {
} }
impl Vox for Cell { impl Vox for Cell {
#[inline(always)]
fn empty() -> Self { Cell::Empty } fn empty() -> Self { Cell::Empty }
#[inline(always)]
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
match self { match self {
Cell::Filled(_) => false, Cell::Filled(_) => false,

View File

@ -160,13 +160,13 @@ pub struct ColumnAccess;
impl Access for ColumnAccess { impl Access for ColumnAccess {
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize { fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize {
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize ((pos.z * sz.y as i32 + pos.y) * sz.x as i32 + pos.x) as usize
} }
fn pos(idx: usize, sz: Vec3<u32>) -> Vec3<i32> { fn pos(idx: usize, sz: Vec3<u32>) -> Vec3<i32> {
let z = idx as u32 % sz.z; let x = idx as u32 % sz.x;
let y = (idx as u32 / sz.z) % sz.y; let y = (idx as u32 / sz.x) % sz.y;
let x = idx as u32 / (sz.y * sz.z); let z = idx as u32 / (sz.y * sz.x);
Vec3::new(x, y, z).map(|e| e as i32) Vec3::new(x, y, z).map(|e| e as i32)
} }
} }

View File

@ -1,5 +1,4 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
#![feature(bool_to_option)]
#![recursion_limit = "2048"] #![recursion_limit = "2048"]
#[cfg(all( #[cfg(all(

View File

@ -49,46 +49,89 @@ where
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512); assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
// NOTE: Cast to usize is safe because of previous check, since all values fit // NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize. // into u16 which is safe to cast to usize.
let (flat, flat_get) = {
let (w_, h_, d_) = greedy_size.into_tuple();
let (w, h, d) = (w_ + 2, h_ + 2, d_ + 2);
let wh = w * h;
let flat = {
let mut flat_ = vec![Cell::empty(); (d * wh) as usize];
let flat = &mut flat_[0..(d * wh) as usize];
// NOTE: We can skip the outside edges because we know they're already empty.
flat.chunks_exact_mut(wh as usize).skip(1).take(d_ as usize).enumerate().for_each(|(z, flat)| {
let z = z as i32;
flat.chunks_exact_mut(w as usize).skip(1).take(h_ as usize).enumerate().for_each(|(y, flat)| {
let y = y as i32;
flat.into_iter().skip(1).take(w_ as usize).enumerate().for_each(|(x, flat)| {
let x = x as i32;
*flat = vol.get(Vec3::new(x, y, z)).copied().unwrap_or_else(|_| Cell::empty());
});
});
});
/* for z in -1..greedy_size.z + 1 {
for y in -1..greedy_size.y + 1 {
for x in -1..greedy_size.x + 1 {
let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or_else(|_| Cell::empty());
flat[i] = block;
i += 1;
}
}
} */
flat_
};
let wh = w * h;
// let offset = wh + w + 1;
let flat_get = #[inline(always)] move |flat: &Vec<Cell>, Vec3 { x, y, z }| /* match flat
.get((x * hd + y * d + z) as usize)
.copied()
{
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
}*/flat[(/*offset + */z * wh + y * w + x) as usize];
(flat, flat_get)
};
let greedy_size = greedy_size.as_::<usize>(); let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size; let greedy_size_cross = greedy_size;
let draw_delta = lower_bound; let draw_delta = lower_bound + 1;
let get_light = |vol: &mut V, pos: Vec3<i32>| { let get_light = #[inline(always)] move |flat: &mut _, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { if flat_get(flat, pos).is_empty() {
1.0 1.0
} else { } else {
0.0 0.0
} }
}; };
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0; let get_glow = #[inline(always)] |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty()); let get_opacity = #[inline(always)] move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
let should_draw = |vol: &mut V, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| { let should_draw = #[inline(always)] |_flat: &mut _, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| {
should_draw_greedy(pos, from, to,/* delta, */uv/*, |vox| { should_draw_greedy(pos, from, to,/* delta, */uv/*, |vox| {
vol.get(vox) vol.get(vox)
.map(|vox| *vox) .map(|vox| *vox)
.unwrap_or_else(|_| Cell::empty()) .unwrap_or_else(|_| Cell::empty())
} */) } */)
}; };
let create_opaque = |atlas_pos, pos, norm| { let create_opaque = #[inline(always)] |atlas_pos, pos, norm| {
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx) TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
}; };
greedy.push(GreedyConfig { greedy.push(GreedyConfig {
data: vol, data: flat,
draw_delta, draw_delta,
greedy_size, greedy_size,
greedy_size_cross, greedy_size_cross,
get_vox: |vol: &mut V, vox| { get_vox: #[inline(always)] move |flat: &mut _, vox| {
vol.get(vox) flat_get(flat, vox)
.map(|vox| *vox)
.unwrap_or_else(|_| Cell::empty())
}, },
get_ao: |_: &mut V, _: Vec3<i32>| 1.0, get_ao: #[inline(always)] |_: &mut _, _: Vec3<i32>| 1.0,
get_light, get_light,
get_glow, get_glow,
get_opacity, get_opacity,
should_draw, should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { push_quad: #[inline(always)] |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad( opaque_mesh.push_quad(greedy::create_quad(
atlas_origin, atlas_origin,
dim, dim,
@ -99,14 +142,14 @@ where
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
)); ));
}, },
make_face_texel: |vol: &mut V, pos, light, _, _| { make_face_texel: #[inline(always)] move |flat: &mut _, pos, light, _, _| {
let cell = vol.get(pos).ok(); let cell = match flat_get(flat, pos) {
let (glowy, shiny) = cell Cell::Filled(c) => c,
.map(|c| (c.is_glowy(), c.is_shiny())) _ => unreachable!("Face texels cannot be empty"),
.unwrap_or_default(); };
let col = cell let glowy = cell.is_glowy();
.and_then(|vox| vox.get_color()) let shiny = cell.is_shiny();
.unwrap_or_else(Rgb::zero); let col = cell.col;
TerrainVertex::make_col_light_figure(light, glowy, shiny, col) TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
}, },
}); });
@ -162,30 +205,44 @@ where
); );
let (flat, flat_get) = { let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple(); let (w_, h_, d_) = greedy_size.into_tuple();
let (w, h, d) = (w_ + 2, h_ + 2, d_ + 2);
let wh = w * h;
let flat = { let flat = {
let mut flat = vec![Cell::empty(); (w * h * d) as usize]; let mut flat_ = vec![Cell::empty(); (d * wh) as usize];
let mut i = 0; let flat = &mut flat_[0..(d * wh) as usize];
for x in -1..greedy_size.x + 1 { // NOTE: We can skip the outside edges because we know they're already empty.
flat.chunks_exact_mut(wh as usize).skip(1).take(d_ as usize).enumerate().for_each(|(z, flat)| {
let z = z as i32;
flat.chunks_exact_mut(w as usize).skip(1).take(h_ as usize).enumerate().for_each(|(y, flat)| {
let y = y as i32;
flat.into_iter().skip(1).take(w_ as usize).enumerate().for_each(|(x, flat)| {
let x = x as i32;
*flat = vol.get(Vec3::new(x, y, z)).copied().unwrap_or_else(|_| Cell::empty());
});
});
});
/* for z in -1..greedy_size.z + 1 {
for y in -1..greedy_size.y + 1 { for y in -1..greedy_size.y + 1 {
for z in -1..greedy_size.z + 1 { for x in -1..greedy_size.x + 1 {
let wpos = lower_bound + Vec3::new(x, y, z); let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or_else(|_| Cell::empty()); let block = vol.get(wpos).map(|b| *b).unwrap_or_else(|_| Cell::empty());
flat[i] = block; flat[i] = block;
i += 1; i += 1;
} }
} }
} } */
flat flat_
}; };
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat let wh = w * h;
.get((x * h * d + y * d + z) as usize) let flat_get = #[inline(always)] move |flat: &Vec<Cell>, Vec3 { x, y, z }| /* match flat
.get((x * hd + y * d + z) as usize)
.copied() .copied()
{ {
Some(b) => b, Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
}; }*/flat[(z * wh + y * w + x) as usize];
(flat, flat_get) (flat, flat_get)
}; };
@ -197,25 +254,25 @@ where
let greedy_size_cross = greedy_size; let greedy_size_cross = greedy_size;
let draw_delta = Vec3::new(1, 1, 1); let draw_delta = Vec3::new(1, 1, 1);
let get_light = move |flat: &mut _, pos: Vec3<i32>| { let get_light = #[inline(always)] move |flat: &mut _, pos: Vec3<i32>| {
if flat_get(flat, pos).is_empty() { if flat_get(flat, pos).is_empty() {
1.0 1.0
} else { } else {
0.0 0.0
} }
}; };
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0; let get_glow = #[inline(always)] |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color = move |flat: &mut _, pos: Vec3<i32>| { let get_color = #[inline(always)] move |flat: &mut _, pos: Vec3<i32>| {
flat_get(flat, pos).get_color().unwrap_or_else(Rgb::zero) flat_get(flat, pos).get_color().unwrap_or_else(Rgb::zero)
}; };
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty(); let get_opacity = #[inline(always)] move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
let should_draw = move |flat: &mut _, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| { let should_draw = #[inline(always)] move |_flat: &mut _, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| {
should_draw_greedy_ao(vertical_stripes, pos, from, to,/* delta, */uv/* , |vox| flat_get(flat, vox) */) should_draw_greedy_ao(vertical_stripes, pos, from, to,/* delta, */uv/* , |vox| flat_get(flat, vox) */)
}; };
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final // NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
// position, pos + mesh_delta, is guaranteed to fit in an f32). // position, pos + mesh_delta, is guaranteed to fit in an f32).
let mesh_delta = lower_bound.as_::<f32>(); let mesh_delta = lower_bound.as_::<f32>();
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| { let create_opaque = #[inline(always)] |atlas_pos, pos: Vec3<f32>, norm, _meta| {
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm) SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
}; };
@ -224,13 +281,13 @@ where
draw_delta, draw_delta,
greedy_size, greedy_size,
greedy_size_cross, greedy_size_cross,
get_vox: move |flat: &mut _, vox| flat_get(flat, vox), get_vox: #[inline(always)] move |flat: &mut _, vox| flat_get(flat, vox),
get_ao: |_: &mut _, _: Vec3<i32>| 1.0, get_ao: #[inline(always)] |_: &mut _, _: Vec3<i32>| 1.0,
get_light, get_light,
get_glow, get_glow,
get_opacity, get_opacity,
should_draw, should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| { push_quad: #[inline(always)] |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
opaque_mesh.push_quad(greedy::create_quad( opaque_mesh.push_quad(greedy::create_quad(
atlas_origin, atlas_origin,
dim, dim,
@ -241,7 +298,7 @@ where
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
)); ));
}, },
make_face_texel: move |flat: &mut _, pos, light, glow, ao| { make_face_texel: #[inline(always)] move |flat: &mut _, pos, light, glow, ao| {
TerrainVertex::make_col_light(light, glow, get_color(flat, pos), ao) TerrainVertex::make_col_light(light, glow, get_color(flat, pos), ao)
}, },
}); });
@ -283,29 +340,29 @@ where
let greedy_size_cross = greedy_size; let greedy_size_cross = greedy_size;
let draw_delta = lower_bound; let draw_delta = lower_bound;
let get_light = |vol: &mut V, pos: Vec3<i32>| { let get_light = #[inline(always)] |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0 1.0
} else { } else {
0.0 0.0
} }
}; };
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0; let get_glow = #[inline(always)] |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| { let get_color = #[inline(always)] |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos) vol.get(pos)
.ok() .ok()
.and_then(|vox| vox.get_color()) .and_then(|vox| vox.get_color())
.unwrap_or_else(Rgb::zero) .unwrap_or_else(Rgb::zero)
}; };
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty()); let get_opacity = #[inline(always)] |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| { let should_draw = #[inline(always)] |vol: &mut V, pos: Vec3<i32>, from: Cell, to: Cell, /*delta: Vec3<i32>, */uv| {
should_draw_greedy(pos, from, to,/* delta, */uv/*, |vox| { should_draw_greedy(pos, from, to,/* delta, */uv/*, |vox| {
vol.get(vox) vol.get(vox)
.map(|vox| *vox) .map(|vox| *vox)
.unwrap_or_else(|_| Cell::empty()) .unwrap_or_else(|_| Cell::empty())
}*/) }*/)
}; };
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm); let create_opaque = #[inline(always)] |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
let mut opaque_mesh = Mesh::new(); let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig { greedy.push(GreedyConfig {
@ -313,17 +370,17 @@ where
draw_delta, draw_delta,
greedy_size, greedy_size,
greedy_size_cross, greedy_size_cross,
get_vox: |vol: &mut V, vox| { get_vox: #[inline(always)] |vol: &mut V, vox| {
vol.get(vox) vol.get(vox)
.map(|vox| *vox) .map(|vox| *vox)
.unwrap_or_else(|_| Cell::empty()) .unwrap_or_else(|_| Cell::empty())
}, },
get_ao: |_: &mut V, _: Vec3<i32>| 1.0, get_ao: #[inline(always)] |_: &mut V, _: Vec3<i32>| 1.0,
get_light, get_light,
get_glow, get_glow,
get_opacity, get_opacity,
should_draw, should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { push_quad: #[inline(always)] |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad( opaque_mesh.push_quad(greedy::create_quad(
atlas_origin, atlas_origin,
dim, dim,
@ -334,7 +391,7 @@ where
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
)); ));
}, },
make_face_texel: move |vol: &mut V, pos, light, glow, ao| { make_face_texel: #[inline(always)] move |vol: &mut V, pos, light, glow, ao| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos), ao) TerrainVertex::make_col_light(light, glow, get_color(vol, pos), ao)
}, },
}); });

View File

@ -16,6 +16,7 @@ use common::{
}, },
vol::ReadVol, vol::ReadVol,
}; };
use common_base::prof_span;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use probability::{ use probability::{
prelude::{source, Gaussian, Inverse}, prelude::{source, Gaussian, Inverse},
@ -113,6 +114,7 @@ pub fn apply_trees_to(
dynamic_rng: &mut impl Rng, dynamic_rng: &mut impl Rng,
/* calendar: Option<&Calendar>, */ /* calendar: Option<&Calendar>, */
) { ) {
prof_span!("apply_trees_to");
// TODO: Get rid of this // TODO: Get rid of this
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum TreeModel { enum TreeModel {

View File

@ -16,6 +16,7 @@ use common::{
}, },
vol::ReadVol, vol::ReadVol,
}; };
use common_base::prof_span;
use fxhash::FxHasher64; use fxhash::FxHasher64;
use hashbrown::{/*hash_map::Entry, */HashMap}; use hashbrown::{/*hash_map::Entry, */HashMap};
use noisy_float::types::n32; use noisy_float::types::n32;
@ -2858,8 +2859,8 @@ impl Painter<'_> {
let aabb = Self::get_bounds(cache, tree, prim); let aabb = Self::get_bounds(cache, tree, prim);
/*if !(aabb.size().w > 8 || aabb.size().h > 8 || aabb.size().d > 16) */{ /*if !(aabb.size().w > 8 || aabb.size().h > 8 || aabb.size().d > 16) */{
let mut do_segment = || { let mut do_segment = || {
let distance = segment.end - segment.start; let distance = segment.start - segment.end;
let distance_proj = distance / distance.magnitude_squared(); let distance_proj = -distance / distance.magnitude_squared();
let segment_start = segment.start - 0.5; let segment_start = segment.start - 0.5;
if r0 == r1 { if r0 == r1 {
let radius_2 = r0 * r0; let radius_2 = r0 * r0;
@ -2873,7 +2874,7 @@ impl Painter<'_> {
let pos = pos.as_::<f32>(); let pos = pos.as_::<f32>();
let length = pos - segment_start; let length = pos - segment_start;
let t = length.dot(distance_proj).clamped(0.0, 1.0); let t = length.dot(distance_proj).clamped(0.0, 1.0);
let diff = distance * t - length; let diff = distance * t + length;
diff.magnitude_squared() < radius_2 diff.magnitude_squared() < radius_2
}, },
hit, hit,
@ -2888,7 +2889,7 @@ impl Painter<'_> {
let pos = pos.as_::<f32>(); let pos = pos.as_::<f32>();
let length = pos - segment_start; let length = pos - segment_start;
let t = length.dot(distance_proj).clamped(0.0, 1.0); let t = length.dot(distance_proj).clamped(0.0, 1.0);
let mut diff = distance * t - length; let mut diff = distance * t + length;
diff.z *= z_scale; diff.z *= z_scale;
diff.magnitude_squared() < radius_2 diff.magnitude_squared() < radius_2
}, },
@ -2905,7 +2906,7 @@ impl Painter<'_> {
let pos = pos.as_::<f32>(); let pos = pos.as_::<f32>();
let length = pos - segment_start; let length = pos - segment_start;
let t = length.dot(distance_proj).clamped(0.0, 1.0); let t = length.dot(distance_proj).clamped(0.0, 1.0);
let diff = distance * t - length; let diff = distance * t + length;
let radius = Lerp::lerp_unclamped(r0, r1, t); let radius = Lerp::lerp_unclamped(r0, r1, t);
diff.magnitude_squared() < radius * radius diff.magnitude_squared() < radius * radius
}, },
@ -2921,7 +2922,7 @@ impl Painter<'_> {
let pos = pos.as_::<f32>(); let pos = pos.as_::<f32>();
let length = pos - segment_start; let length = pos - segment_start;
let t = length.dot(distance_proj).clamped(0.0, 1.0); let t = length.dot(distance_proj).clamped(0.0, 1.0);
let mut diff = distance * t - length; let mut diff = distance * t + length;
diff.z *= z_scale; diff.z *= z_scale;
let radius = Lerp::lerp_unclamped(r0, r1, t); let radius = Lerp::lerp_unclamped(r0, r1, t);
diff.magnitude_squared() < radius * radius diff.magnitude_squared() < radius * radius
@ -5698,6 +5699,7 @@ pub fn render_collect<'b, 'c, F: Filler + 'c, Render: FnOnce(&Painter<'b>, &mut
fill.sample_at(arena, &mut bounds_cache, prim, prim_tree, render_area, &info, filler) fill.sample_at(arena, &mut bounds_cache, prim, prim_tree, render_area, &info, filler)
}; */ }; */
prof_span!("render");
render(&painter, &mut fill_fn); render(&painter, &mut fill_fn);
/* ( /* (
painter.prims.into_inner(), painter.prims.into_inner(),