Terrain !
This commit is contained in:
+377
-16
@@ -1,3 +1,6 @@
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use glam::Vec3;
|
||||
use itertools::Itertools;
|
||||
|
||||
@@ -5,7 +8,7 @@ use crate::voxel::gpu::ExplicitNTreeNode;
|
||||
use crate::voxel::gpu::StructurePointer;
|
||||
use crate::voxel::sparse::Color;
|
||||
|
||||
pub struct SineGenerator<const N: usize>
|
||||
pub struct BallGenerator<const N: usize>
|
||||
{
|
||||
chunk_power: usize,
|
||||
}
|
||||
@@ -15,22 +18,28 @@ 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
|
||||
}
|
||||
|
||||
impl<const N: usize> SineGenerator<N>
|
||||
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
|
||||
{
|
||||
SineGenerator { chunk_power }
|
||||
BallGenerator { chunk_power }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn produce_node(
|
||||
&self,
|
||||
depth: usize,
|
||||
nx: usize,
|
||||
ny: usize,
|
||||
nz: usize,
|
||||
) -> ExplicitNTreeNode<N>
|
||||
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;
|
||||
@@ -49,23 +58,38 @@ where
|
||||
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 - 128., gvy as f32 - 128., gvz as f32 - 128.).length();
|
||||
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 - 128.).abs() <= child_diagonal_length
|
||||
if (dist - (global_size as f32 / 2.)).abs() <= child_diagonal_length
|
||||
{
|
||||
children.push(StructurePointer::new(depth <= 3, false, 0));
|
||||
//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 > 128.
|
||||
else if dist > (global_size as f32 / 2.)
|
||||
{
|
||||
children.push(StructurePointer::new(false, false, 0));
|
||||
children.push(StructurePointer(0));
|
||||
alpha = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
children.push(StructurePointer::new(false, false, 0));
|
||||
children.push(StructurePointer(0));
|
||||
alpha = 1.;
|
||||
}
|
||||
|
||||
@@ -86,3 +110,340 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>,
|
||||
|
||||
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
|
||||
{
|
||||
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 heightmap = match tiff_dec.read_image().unwrap()
|
||||
{
|
||||
tiff::decoder::DecodingResult::F32(vec) => vec,
|
||||
_ => panic!("Unsupported format"),
|
||||
};
|
||||
|
||||
let mut color = image::ImageReader::open(color_path).unwrap();
|
||||
color.no_limits();
|
||||
|
||||
let color = color.decode().unwrap();
|
||||
let colormap = color.as_rgb8().unwrap().to_vec();
|
||||
|
||||
let terrain_width = color.width() as usize;
|
||||
let terrain_height = color.height() as usize;
|
||||
|
||||
let heightmap_min = heightmap.iter().copied().reduce(f32::min).unwrap();
|
||||
let heightmap_max = heightmap.iter().copied().reduce(f32::max).unwrap();
|
||||
|
||||
// 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);
|
||||
|
||||
Self {
|
||||
chunk_power,
|
||||
heightmap_width,
|
||||
heightmap_height,
|
||||
heightmap_min,
|
||||
heightmap_max,
|
||||
terrain_width,
|
||||
terrain_height,
|
||||
heightmap,
|
||||
colormap,
|
||||
|
||||
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;
|
||||
for (x, z) 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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user