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 { 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 where [(); N * N * N]:, { fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode; } impl BallGenerator where [(); N * N * N]:, { pub fn new(chunk_power: usize) -> Self { BallGenerator { chunk_power } } } impl Producer for BallGenerator where [(); N * N * N]:, { fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode { 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 { chunk_power: usize, } impl SineGenerator where [(); N * N * N]:, { pub fn new(chunk_power: usize) -> Self { Self { chunk_power } } } impl Producer for SineGenerator where [(); N * N * N]:, { fn produce_node(&self, depth: usize, nx: usize, ny: usize, nz: usize) -> ExplicitNTreeNode { 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 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, colormap: Vec, heightmap_low_width: usize, heightmap_low_height: usize, heightmap_low: Vec<(f32, f32)>, colormap_low_width: usize, colormap_low_height: usize, colormap_mip: Vec, pub chunk_width: usize, pub chunk_height: usize, pub chunk_alt: usize, } impl TerrainGenerator where [(); N * N * N]:, { pub fn new>( 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 ChunkedProducer for TerrainGenerator where [(); N * N * N]:, { fn produce_node( &self, depth: usize, nx: usize, ny: usize, nz: usize, chunk_pos: (usize, usize, usize), ) -> ExplicitNTreeNode { 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 where [(); N * N * N]:, { fn produce_node( &self, depth: usize, nx: usize, ny: usize, nz: usize, chunk_pos: (usize, usize, usize), ) -> ExplicitNTreeNode; }