Files
vxls/src/producers.rs
T

590 lines
19 KiB
Rust

use std::fs::File;
use std::path::Path;
use glam::Vec3;
use itertools::Itertools;
use crate::sparse_tree::Color;
use crate::voxel_cache::data::ExplicitNTreeNode;
use crate::voxel_cache::data::StructurePointer;
pub struct BallGenerator<const N: usize>
{
chunk_power: usize,
}
pub fn map(x: f32, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> f32
{
//((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min
let input_range = x_max.algebraic_sub(x_min);
let output_range = y_max.algebraic_sub(y_min);
(x.algebraic_sub(x_min).algebraic_div(input_range))
.algebraic_mul(output_range)
.algebraic_add(y_min)
}
pub trait Producer<const N: usize>
where
[(); N * N * N]:,
{
fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode<N>;
}
impl<const N: usize> BallGenerator<N>
where
[(); N * N * N]:,
{
pub fn new(chunk_power: usize) -> Self
{
BallGenerator { chunk_power }
}
}
impl<const N: usize> Producer<N> for BallGenerator<N>
where
[(); N * N * N]:,
{
fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode<N>
{
let node_size = N.pow((self.chunk_power - depth) as u32);
let child_size = node_size / N;
let global_size = N.pow(self.chunk_power as u32);
let mut children = vec![];
let mut children_color = vec![];
let gnx = nx * node_size;
let gny = ny * node_size;
let gnz = nz * node_size;
// Iterate on children of this node
for ((cx, cy), cz) in (0..N).cartesian_product(0..N).cartesian_product(0..N)
{
let gvx = gnx + cx * child_size + (child_size / 2);
let gvy = gny + cy * child_size + (child_size / 2);
let gvz = gnz + cz * child_size + (child_size / 2);
let dist = Vec3::new(
gvx as f32 - (global_size / 2) as f32,
gvy as f32 - (global_size / 2) as f32,
gvz as f32 - (global_size / 2) as f32,
)
.length();
let child_diagonal_length = (child_size as f32 / 2.) * f32::sqrt(3.);
let alpha;
if (dist - (global_size as f32 / 2.)).abs() <= child_diagonal_length
{
//children.push(StructurePointer::new(depth <= 3, false, 0));
children.push(StructurePointer(
if depth < (self.chunk_power - 1)
{
0xFFFFFFFF
}
else
{
0
},
));
alpha = if dist > 128. { 0. } else { 1. };
}
else if dist > (global_size as f32 / 2.)
{
children.push(StructurePointer(0));
alpha = 0.;
}
else
{
children.push(StructurePointer(0));
alpha = 1.;
}
// let alpha = 1.;
// children.push(StructurePointer::new(depth <= 3, false, 0));
children_color.push(Color(
gvx as f32 / global_size as f32,
gvy as f32 / global_size as f32,
gvz as f32 / global_size as f32,
alpha,
));
}
ExplicitNTreeNode {
structure: std::array::from_fn(|i| children[i]),
colors: std::array::from_fn(|i| children_color[i]),
}
}
}
pub struct SineGenerator<const N: usize>
{
chunk_power: usize,
}
impl<const N: usize> SineGenerator<N>
where
[(); N * N * N]:,
{
pub fn new(chunk_power: usize) -> Self
{
Self { chunk_power }
}
}
impl<const N: usize> Producer<N> for SineGenerator<N>
where
[(); N * N * N]:,
{
fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode<N>
{
let node_size = N.pow((self.chunk_power - depth) as u32);
let child_size = node_size / N;
let global_size = N.pow(self.chunk_power as u32);
let mut children = vec![];
let mut children_color = vec![];
let gnx = nx * node_size;
let gny = ny * node_size;
let gnz = nz * node_size;
// Iterate on children of this node
for ((cx, cy), cz) in (0..N).cartesian_product(0..N).cartesian_product(0..N)
{
let total_sub_voxels = child_size * child_size * child_size;
let mut filled_sub_voxels = 0;
let gcx = gnx + cx * child_size;
let gcy = gny + cy * child_size;
let gcz = gnz + cz * child_size;
// prepare 2d values
// Iterate on children
for (x, z) in (0..child_size).cartesian_product(0..child_size)
{
let gvx = gcx + x;
let gvz = gcz + z;
//let gvy = gcy;
let sx = map(gvx as f32, 0., global_size as f32, -8., 8.).abs();
let sz = map(gvz as f32, 0., global_size as f32, -8., 8.).abs();
let sample = (fastapprox::fast::cos(sx) + fastapprox::fast::cos(sz)) * 0.5;
let sample_height = map(sample, -1., 1., 0., 500.);
let prop = map(
sample_height,
gcy as f32,
(gcy + child_size) as f32,
0.,
child_size as f32,
)
.clamp(0., child_size as f32)
.floor() as usize;
filled_sub_voxels += prop;
}
let alpha;
if filled_sub_voxels == 0
{
children.push(StructurePointer(0));
alpha = 0.;
}
else if filled_sub_voxels >= total_sub_voxels
{
children.push(StructurePointer(0));
alpha = 1.;
}
else
{
children.push(StructurePointer(
if depth < (self.chunk_power - 1)
{
0xFFFFFFFF
}
else
{
0
},
));
//children.push(StructurePointer(0));
alpha = if filled_sub_voxels > total_sub_voxels / 2
{
1.
}
else
{
0.
};
}
children_color.push(Color(
(gcx + child_size / 2) as f32 / global_size as f32,
(gcy + child_size / 2) as f32 / global_size as f32,
(gcz + child_size / 2) as f32 / global_size as f32,
alpha,
));
}
ExplicitNTreeNode {
structure: std::array::from_fn(|i| children[i]),
colors: std::array::from_fn(|i| children_color[i]),
}
}
}
pub struct TerrainGenerator<const N: usize>
where
[(); N * N * N]:,
{
chunk_power: usize,
heightmap_width: usize,
heightmap_height: usize,
terrain_width: usize,
terrain_height: usize,
heightmap_min: f32,
heightmap_max: f32,
heightmap: Vec<f32>,
colormap: Vec<u8>,
heightmap_low_width: usize,
heightmap_low_height: usize,
heightmap_low: Vec<(f32, f32)>,
colormap_low_width: usize,
colormap_low_height: usize,
colormap_mip: Vec<u8>,
pub chunk_width: usize,
pub chunk_height: usize,
pub chunk_alt: usize,
}
impl<const N: usize> TerrainGenerator<N>
where
[(); N * N * N]:,
{
pub fn new<P: AsRef<Path>>(
chunk_power: usize,
height_path: P,
height_factor: f32,
color_path: P,
) -> Self
{
println!("Starting terrain producer");
println!("Loading height map.");
let mut tiff_dec = tiff::decoder::Decoder::new(File::open(height_path).unwrap()).unwrap();
let (heightmap_width, heightmap_height) = tiff_dec.dimensions().unwrap();
let (heightmap_width, heightmap_height) =
(heightmap_width as usize, heightmap_height as usize);
let mut heightmap = match tiff_dec.read_image().unwrap()
{
tiff::decoder::DecodingResult::F32(vec) => vec,
_ => panic!("Unsupported format"),
};
println!("Loading color map.");
let mut color = image::ImageReader::open(color_path).unwrap();
color.no_limits();
let color = color.decode().unwrap();
let mut colormap = color.as_rgb8().unwrap().to_vec();
println!("Converting color spaces");
colormap.iter_mut().for_each(|x| {
let normalized = map(*x as f32, 0., 255., 0., 1.);
let maped = normalized.powf(2.4);
*x = map(maped, 0., 1., 0., 255.) as u8;
});
let terrain_width = color.width() as usize;
let terrain_height = color.height() as usize;
println!("Computing heightmap min/max");
let heightmap_min = heightmap
.iter()
.copied()
.filter(|x| *x != -9999.)
.reduce(f32::min)
.unwrap();
let heightmap_max = heightmap.iter().copied().reduce(f32::max).unwrap();
heightmap
.iter_mut()
.filter(|x| **x == -9999.)
.for_each(|x| *x = heightmap_min);
// Decide size in chunks
let height_amplitude = heightmap_max - heightmap_min;
let chunk_size = N.pow(chunk_power as u32);
let chunk_width = terrain_width.div_ceil(chunk_size);
let chunk_height = terrain_height.div_ceil(chunk_size);
let chunk_alt = ((height_amplitude / height_factor) as usize).div_ceil(chunk_size);
// build the low heightmap
println!("Computing low res height/color maps");
let heightmap_low_width = heightmap_width / 8;
let heightmap_low_height = heightmap_height / 8;
let mut heightmap_low = vec![(0., 0.); heightmap_low_height * heightmap_low_width];
for y in 0..heightmap_low_height
{
for x in 0..heightmap_low_width
{
let mut min = heightmap_max;
let mut max = heightmap_min;
for sy in (y * 8)..(y * 8 + 8)
{
for sx in (x * 8)..(x * 8 + 8)
{
min = min.min(heightmap[sx + sy * heightmap_width]);
max = max.max(heightmap[sx + sy * heightmap_width]);
}
}
heightmap_low[x + y * heightmap_low_width] = (min, max);
}
}
// build the color map mip
let colormap_low_width = terrain_width / 8;
let colormap_low_height = terrain_height / 8;
let mut colormap_mip = vec![0u8; colormap_low_width * colormap_low_height * 3];
for y in 0..colormap_low_height
{
for x in 0..colormap_low_width
{
let mut r = 0u32;
let mut g = 0u32;
let mut b = 0u32;
for sy in (y * 8)..(y * 8 + 8)
{
for sx in (x * 8)..(x * 8 + 8)
{
r += colormap[(sx + sy * terrain_width) * 3] as u32;
g += colormap[(sx + sy * terrain_width) * 3 + 1] as u32;
b += colormap[(sx + sy * terrain_width) * 3 + 2] as u32;
}
}
colormap_mip[(x + y * colormap_low_width) * 3] =
((r as f32) / (8 * 8) as f32).clamp(0., 255.) as u8;
colormap_mip[(x + y * colormap_low_width) * 3 + 1] =
((g as f32) / (8 * 8) as f32).clamp(0., 255.) as u8;
colormap_mip[(x + y * colormap_low_width) * 3 + 2] =
((b as f32) / (8 * 8) as f32).clamp(0., 255.) as u8;
}
}
println!("Producer ready");
Self {
chunk_power,
heightmap_width,
heightmap_height,
heightmap_min,
heightmap_max,
terrain_width,
terrain_height,
heightmap,
colormap,
heightmap_low_width,
heightmap_low_height,
heightmap_low,
colormap_low_width,
colormap_low_height,
colormap_mip,
chunk_width,
chunk_height,
chunk_alt,
}
}
}
impl<const N: usize> ChunkedProducer<N> for TerrainGenerator<N>
where
[(); N * N * N]:,
{
fn produce_node(
&self,
depth: usize,
nx: usize,
ny: usize,
nz: usize,
chunk_pos: (usize, usize, usize),
) -> ExplicitNTreeNode<N>
{
let node_size = N.pow((self.chunk_power - depth) as u32);
let child_size = node_size / N;
let global_size = N.pow(self.chunk_power as u32);
let mut children = vec![StructurePointer(0); N * N * N];
let mut children_color = vec![Color(0., 0., 0., 0.); N * N * N];
let gnx = chunk_pos.0 * global_size + nx * node_size;
let gny = chunk_pos.1 * global_size + ny * node_size;
let gnz = chunk_pos.2 * global_size + nz * node_size;
// Iterate on children of this node
for (cx, cz) in (0..N).cartesian_product(0..N)
{
let gcx = gnx + cx * child_size;
let gcz = gnz + cz * child_size;
// prepare 2d values
// Iterate on children
let mut sample_max = self.heightmap_min;
let mut sample_min = self.heightmap_max;
let mut color_avg = Color(0., 0., 0., 0.);
let mut count = 0;
if depth <= 2
{
for (z, x) in (0..(child_size / 8)).cartesian_product(0..(child_size / 8))
{
let gvx = (gcx + x * 8) / 8;
let gvz = (gcz + z * 8) / 8;
if gvx < self.colormap_low_width && gvz < self.colormap_low_height
{
// Height sample
let height_x = (gvx * self.heightmap_low_width) / self.colormap_low_width;
let height_z = (gvz * self.heightmap_low_height) / self.colormap_low_height;
let sample =
self.heightmap_low[height_x + height_z * self.heightmap_low_width];
sample_min = sample_min.min(sample.0);
sample_max = sample_max.max(sample.1);
let sample_color_r =
self.colormap_mip[(gvx + gvz * self.colormap_low_width) * 3];
let sample_color_g =
self.colormap_mip[(gvx + gvz * self.colormap_low_width) * 3 + 1];
let sample_color_b =
self.colormap_mip[(gvx + gvz * self.colormap_low_width) * 3 + 2];
color_avg.0 += map(sample_color_r as f32, 0., 256., 0., 1.);
color_avg.1 += map(sample_color_g as f32, 0., 256., 0., 1.);
color_avg.2 += map(sample_color_b as f32, 0., 256., 0., 1.);
count += 1;
}
//let gvy = gcy;
}
}
else
{
for (z, x) in (0..child_size).cartesian_product(0..child_size)
{
let gvx = gcx + x;
let gvz = gcz + z;
if gvx < self.terrain_width && gvz < self.terrain_height
{
// Height sample
let height_x = (gvx * self.heightmap_width) / self.terrain_width;
let height_z = (gvz * self.heightmap_height) / self.terrain_height;
let sample = self.heightmap[height_x + height_z * self.heightmap_width];
sample_max = sample_max.max(sample);
sample_min = sample_min.min(sample);
let sample_color_r = self.colormap[(gvx + gvz * self.terrain_width) * 3];
let sample_color_g =
self.colormap[(gvx + gvz * self.terrain_width) * 3 + 1];
let sample_color_b =
self.colormap[(gvx + gvz * self.terrain_width) * 3 + 2];
color_avg.0 += map(sample_color_r as f32, 0., 256., 0., 1.);
color_avg.1 += map(sample_color_g as f32, 0., 256., 0., 1.);
color_avg.2 += map(sample_color_b as f32, 0., 256., 0., 1.);
count += 1;
}
//let gvy = gcy;
}
}
color_avg.0 /= count as f32;
color_avg.1 /= count as f32;
color_avg.2 /= count as f32;
let sample_min = map(
sample_min,
self.heightmap_min,
self.heightmap_max,
0.,
(self.chunk_alt * global_size) as f32,
) as usize;
let sample_max = map(
sample_max,
self.heightmap_min,
self.heightmap_max,
0.,
(self.chunk_alt * global_size) as f32,
) as usize;
for cy in 0..N
{
let gcy = gny + cy * child_size;
let index = cz * N * N + cy * N + cx;
let alpha;
if gcy > sample_max
{
children[index] = StructurePointer(0);
alpha = 0.;
}
else if gcy + child_size < sample_min
{
children[index] = StructurePointer(0);
alpha = 1.;
}
else
{
children[index] = StructurePointer(
if depth < (self.chunk_power - 1)
{
0xFFFFFFFF
}
else
{
0
},
);
//children.push(StructurePointer(0));
alpha = if (sample_max + sample_min / 2) > gcy + (child_size / 2)
{
1.
}
else
{
0.
};
}
children_color[index] = Color(color_avg.0, color_avg.1, color_avg.2, alpha);
}
}
ExplicitNTreeNode {
structure: std::array::from_fn(|i| children[i]),
colors: std::array::from_fn(|i| children_color[i]),
}
}
}
pub trait ChunkedProducer<const N: usize>
where
[(); N * N * N]:,
{
fn produce_node(
&self,
depth: usize,
nx: usize,
ny: usize,
nz: usize,
chunk_pos: (usize, usize, usize),
) -> ExplicitNTreeNode<N>;
}