590 lines
19 KiB
Rust
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>;
|
|
}
|