use cgmath::num_traits::pow;
use cgmath::{vec3, ElementWise, Vector3};
use dot_vox::load;
use slotmap::{SlotMap, new_key_type};
use std::{collections::{HashMap, VecDeque}, path::Path};
use bytemuck::{Pod, Zeroable};
use wgpu::util::DeviceExt;
use wide::f32x4;
new_key_type! {
pub struct NodeKey;
}
#[derive(Debug)]
pub struct Node64 {
child_mask: u64,
children: Vec<NodeKey>,
voxel_data: [u8; 3],
is_leaf: bool,
}
impl Default for Node64 {
fn default() -> Self {
Self {
child_mask: 0,
children: Vec::new(),
voxel_data: [0, 0, 0],
is_leaf: false,
}
}
}
pub struct Sparse64Tree {
pub nodes: SlotMap<NodeKey, Node64>,
pub root: NodeKey,
}
impl Sparse64Tree {
pub fn new() -> Self {
let mut nodes = SlotMap::with_key();
let root = nodes.insert(Node64::default());
Self { nodes, root }
}
pub fn insert(&mut self, x: u32, y: u32, z: u32, depth: usize, color: [u8; 3]) {
let mut current_key = self.root;
for current_depth in 0..depth {
let child_index = self.compute_child_index(x, y, z, current_depth, depth);
let need_new_child;
let rank;
{
let node = self.nodes.get_mut(current_key).unwrap();
need_new_child = (node.child_mask & (1 << child_index)) == 0;
rank = (node.child_mask & ((1 << child_index) - 1)).count_ones() as usize;
if need_new_child {
node.child_mask |= 1 << child_index;
}
}
if need_new_child {
let new_key = self.nodes.insert(Node64::default());
let node = self.nodes.get_mut(current_key).unwrap();
node.children.insert(rank, new_key);
current_key = new_key;
} else {
let child_key = {
let node = self.nodes.get(current_key).unwrap(); node.children[rank]
};
current_key = child_key;
}
}
if let Some(leaf_node) = self.nodes.get_mut(current_key) {
leaf_node.voxel_data = color;
leaf_node.is_leaf = true;
}
}
fn compute_child_index(&self, x: u32, y: u32, z: u32, depth: usize, max_depth: usize) -> usize {
let shift = (max_depth - depth - 1) * 2; let x_part = ((x >> shift) & 0b11) as usize;
let y_part = ((y >> shift) & 0b11) as usize;
let z_part = ((z >> shift) & 0b11) as usize;
(z_part << 4) | (y_part << 2) | x_part
}
pub fn flatten(&self) -> Vec<u8> {
let mut flattened_nodes = Vec::new();
let mut key_to_index = HashMap::new();
let mut queue = VecDeque::new();
queue.push_back(self.root);
while let Some(key) = queue.pop_front() {
let index = flattened_nodes.len();
key_to_index.insert(key, index);
let node = self.nodes.get(key).unwrap();
flattened_nodes.push((key, node));
for &child_key in &node.children {
queue.push_back(child_key);
}
}
let gpu_nodes: Vec<GpuNode64> = flattened_nodes
.iter()
.map(|(_key, node)| {
let child_ptr = if !node.children.is_empty() {
*key_to_index.get(&node.children[0]).unwrap() as u32
} else {
0
};
let leaf_flag = if node.is_leaf { 0x8000_0000 } else { 0 };
let child_ptr_and_leaf = child_ptr | leaf_flag;
let child_mask_low = (node.child_mask & 0xFFFF_FFFF) as u32;
let child_mask_high = (node.child_mask >> 32) as u32;
let color = [
node.voxel_data[0] as f32 / 255.0,
node.voxel_data[1] as f32 / 255.0,
node.voxel_data[2] as f32 / 255.0,
];
GpuNode64 {
child_mask_low,
child_mask_high,
child_ptr_and_leaf,
_padding: 0,
color,
_padding2: 0,
}
})
.collect();
bytemuck::cast_slice(&gpu_nodes).to_vec()
}
pub fn generate_terrain_sdf_noise_simd(&mut self, aabb: AABB, max_depth: usize, noise: Perlin) {
let mut noise_cache = HashMap::new();
self.subdivide_node_noise_simd(self.root, aabb, 0, max_depth, &noise, &mut noise_cache);
}
fn subdivide_node_noise_simd(
&mut self,
node_key: NodeKey,
aabb: AABB,
depth: usize,
max_depth: usize,
noise: &Perlin,
cache: &mut HashMap<(i32, i32), f32>,
) {
let corners = aabb.corners();
let sdf_values = eval_corners_simd(noise, &corners, cache);
let center_sdf = sdf_values.iter().sum::<f32>() / 8.0;
if sdf_values.iter().all(|&v| v >= 0.0) {
return;
}
if sdf_values.iter().all(|&v| v < 0.0) {
if let Some(node) = self.nodes.get_mut(node_key) {
node.is_leaf = true;
node.voxel_data = [34 + (generate_0_to_255() / 5), 139, 34 + (generate_0_to_255() / 5)]; }
return;
}
if depth < max_depth {
let cell_size = (aabb.max - aabb.min) / 4.0;
for cz in 0..4 {
for cy in 0..4 {
for cx in 0..4 {
let offset = vec3(cx as f32, cy as f32, cz as f32);
let child_min = aabb.min + offset.mul_element_wise(cell_size);
let child_max = child_min + cell_size;
let child_aabb = AABB { min: child_min, max: child_max };
let child_corners = child_aabb.corners();
let child_sdf_values = eval_corners_simd(noise, &child_corners, cache);
if child_sdf_values.iter().all(|&v| v >= 0.0) {
continue;
}
let child_index = (cz << 4) | (cy << 2) | cx;
let new_child_key = self.nodes.insert(Node64::default());
{
let parent = self.nodes.get_mut(node_key).unwrap();
let rank = (parent.child_mask & ((1 << child_index) - 1)).count_ones() as usize;
if parent.child_mask & (1 << child_index) == 0 {
parent.child_mask |= 1 << child_index;
parent.children.insert(rank, new_child_key);
}
}
self.subdivide_node_noise_simd(new_child_key, child_aabb, depth + 1, max_depth, noise, cache);
}
}
}
} else {
if center_sdf < 0.0 {
if let Some(node) = self.nodes.get_mut(node_key) {
node.is_leaf = true;
node.voxel_data = [34 + (generate_0_to_255() / 5), 139, 34 + (generate_0_to_255() / 5)];
}
}
}
}
}
#[derive(Clone, Copy)]
pub struct AABB {
pub min: Vector3<f32>,
pub max: Vector3<f32>,
}
impl AABB {
pub fn corners(&self) -> [Vector3<f32>; 8] {
[
self.min,
Vector3::new(self.max.x, self.min.y, self.min.z),
Vector3::new(self.min.x, self.max.y, self.min.z),
Vector3::new(self.min.x, self.min.y, self.max.z),
Vector3::new(self.max.x, self.max.y, self.min.z),
Vector3::new(self.max.x, self.min.y, self.max.z),
Vector3::new(self.min.x, self.max.y, self.max.z),
self.max,
]
}
}
fn get_noise_value(
noise: &Perlin,
p: &Vector3<f32>,
cache: &mut HashMap<(i32, i32), f32>,
) -> f32 {
let key = (((p.x * 20.0).floor()) as i32, ((p.z * 20.0).floor()) as i32);
if let Some(&value) = cache.get(&key) {
value
} else {
let value = noise.get([p.x as f64 * 0.05, p.z as f64 * 0.05]) as f32;
cache.insert(key, value);
value
}
}
fn eval_corners_simd(
noise: &Perlin,
corners: &[Vector3<f32>; 8],
cache: &mut HashMap<(i32, i32), f32>,
) -> [f32; 8] {
let mut result = [0.0f32; 8];
for i in 0..2 {
let start = i * 4;
let xs = f32x4::from([
corners[start].x,
corners[start + 1].x,
corners[start + 2].x,
corners[start + 3].x,
]);
let ys = f32x4::from([
corners[start].y,
corners[start + 1].y,
corners[start + 2].y,
corners[start + 3].y,
]);
let raw_noise_arr = [
get_noise_value(noise, &corners[start], cache),
get_noise_value(noise, &corners[start + 1], cache),
get_noise_value(noise, &corners[start + 2], cache),
get_noise_value(noise, &corners[start + 3], cache),
];
let raw_noise = f32x4::from(raw_noise_arr);
let smooth_noise = raw_noise * raw_noise * (f32x4::splat(3.0) - f32x4::splat(2.0) * raw_noise);
let base_height = f32x4::splat(10.0);
let amplitude = f32x4::splat(5.0);
let interpolated_height = base_height + smooth_noise * amplitude;
let mut sdf = ys - interpolated_height;
let sdf_arr = sdf.as_array_mut();
result[start] = sdf_arr[0];
result[start + 1] = sdf_arr[1];
result[start + 2] = sdf_arr[2];
result[start + 3] = sdf_arr[3];
}
result
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct GpuNode64 {
pub child_mask_low: u32,
pub child_mask_high: u32,
pub child_ptr_and_leaf: u32,
pub _padding: u32,
pub color: [f32; 3],
pub _padding2: u32, }
pub struct Tree64GpuManager {
node_buffer: wgpu::Buffer,
num_nodes: u32,
pub contree_bind_group: wgpu::BindGroup,
pub contree_bind_group_layout: wgpu::BindGroupLayout,
}
impl Tree64GpuManager {
pub fn new(device: &wgpu::Device, contree: &Sparse64Tree) -> Self {
let node_buffer = Self::collect_nodes(contree, device);
let contree_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
label: Some("Contree Bind Group Layout"),
});
let contree_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &contree_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: node_buffer.as_entire_binding(),
}],
label: Some("Contree Bind Group"),
});
Self {
node_buffer,
num_nodes: contree.nodes.len() as u32,
contree_bind_group,
contree_bind_group_layout,
}
}
pub fn collect_nodes(tree: &Sparse64Tree, device: &wgpu::Device) -> wgpu::Buffer {
let node_data = tree.flatten();
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Node Buffer"),
contents: &node_data,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
})
}
pub fn upload_tree(&mut self, queue: &wgpu::Queue, tree: &Sparse64Tree) {
let node_data = tree.flatten();
self.num_nodes = tree.nodes.len() as u32;
queue.write_buffer(&self.node_buffer, 0, &node_data);
}
pub fn get_buffer(&self) -> &wgpu::Buffer {
&self.node_buffer
}
}
use noise::{NoiseFn, Perlin};
pub fn create_test_tree() -> Sparse64Tree {
let perlin = Perlin::new(1);
let mut tree = Sparse64Tree::new();
let depth = 4;
let past_grid: i32 = pow(4, depth);
let grid_size = past_grid as u32;
let noise_size = 128;
for x in 0..grid_size {
for z in 0..grid_size {
let val = ((perlin.get([(x as f64) / noise_size as f64, (z as f64) / noise_size as f64]) * 10.0) + 50.0).abs() as u32;
for y in 0..val {
if y > (val / 2) {
tree.insert(x, y, z, depth as usize, [0, 121 + (generate_0_to_255() / 5), 40]);
} else {
tree.insert(x, y, z, depth as usize, [121 + (generate_0_to_255() / 15), 60, 0]);
}
}
}
}
tree
}
pub fn add_vox_to_tree(file_path: &str, depth: usize, offsetx: u32, offsety: u32, offsetz: u32, tree: &mut Sparse64Tree){
let vox_data = match load(Path::new(file_path).to_str().expect("msg")) {
Ok(data) => data,
Err(e) => {
eprintln!("Failed to load .vox file: {}", e);
return;
}
};
for model in &vox_data.models {
let model_offset = (0, 0, 0);
for voxel in &model.voxels {
let x = voxel.x as u32 + model_offset.0;
let y = voxel.z as u32 + model_offset.1;
let z = voxel.y as u32 + model_offset.2;
let color_index = voxel.i as usize;
let color = if color_index < vox_data.palette.len() {
let c = vox_data.palette[color_index];
[c.r, c.g, c.b]
} else {
[255, 255, 255] };
tree.insert(x + offsetx, y + offsety, z + offsetz, depth, color);
}
}
}
pub fn create_test_tree_from_vox(file_path: &str, depth: usize) -> Sparse64Tree {
let mut tree = Sparse64Tree::new();
let vox_data = match load(Path::new(file_path).to_str().expect("msg")) {
Ok(data) => data,
Err(e) => {
eprintln!("Failed to load .vox file: {}", e);
return tree;
}
};
for model in &vox_data.models {
let model_offset = (0, 0, 0);
for voxel in &model.voxels {
let x = voxel.x as u32 + model_offset.0;
let y = voxel.z as u32 + model_offset.1;
let z = voxel.y as u32 + model_offset.2;
let color_index = voxel.i as usize;
let color = if color_index < vox_data.palette.len() {
let c = vox_data.palette[color_index];
[c.r, c.g, c.b]
} else {
[255, 255, 255] };
tree.insert(x, y, z, depth, color);
}
}
tree
}
use rand::Rng;
pub fn generate_0_to_255() -> u8 {
let mut rng = rand::rng();
rng.random_range(0..=255) as u8 }
pub struct TreeMemoryManager {
buffer: wgpu::Buffer,
root_node_buffer: wgpu::Buffer, capacity: usize,
allocations: HashMap<u32, usize>, free_slots: Vec<usize>, pub bind_group: wgpu::BindGroup,
pub bind_group_layout: wgpu::BindGroupLayout,
pub root_bind_group: wgpu::BindGroup,
pub root_bind_group_layout: wgpu::BindGroupLayout,
}
impl TreeMemoryManager {
pub fn new(device: &wgpu::Device, max_trees: usize, tree_size: usize) -> Self {
let buffer_size = max_trees * tree_size;
let root_buffer_size = max_trees * std::mem::size_of::<u32>();
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Sparse Voxel Tree Buffer"),
size: buffer_size as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let root_node_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Tree Root Node Buffer"),
size: root_buffer_size as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Tree Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Tree Bind Group"),
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
let root_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Root Node Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let root_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Root Node Bind Group"),
layout: &root_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: root_node_buffer.as_entire_binding(),
}],
});
let free_slots = (0..max_trees).map(|i| i * tree_size).collect();
Self {
buffer,
root_node_buffer,
capacity: max_trees,
allocations: HashMap::new(),
free_slots,
bind_group,
bind_group_layout,
root_bind_group,
root_bind_group_layout,
}
}
pub fn upload_tree(&mut self, queue: &wgpu::Queue, tree_id: u32, tree_data: &[u8], root_node_offset: u32) {
if let Some(&offset) = self.allocations.get(&tree_id) {
queue.write_buffer(&self.buffer, offset as u64, tree_data);
self.update_root_reference(queue, tree_id, root_node_offset);
} else if let Some(offset) = self.free_slots.pop() {
self.allocations.insert(tree_id, offset);
queue.write_buffer(&self.buffer, offset as u64, tree_data);
self.update_root_reference(queue, tree_id, root_node_offset);
} else {
panic!("No available slots for new trees!");
}
}
pub fn update_root_reference(&self, queue: &wgpu::Queue, tree_id: u32, root_node_offset: u32) {
let root_index = tree_id as usize * std::mem::size_of::<u32>();
queue.write_buffer(&self.root_node_buffer, root_index as u64, bytemuck::bytes_of(&root_node_offset));
}
pub fn remove_tree(&mut self, tree_id: u32) {
if let Some(offset) = self.allocations.remove(&tree_id) {
self.free_slots.push(offset);
}
}
pub fn get_buffer(&self) -> &wgpu::Buffer {
&self.buffer
}
pub fn get_root_node_buffer(&self) -> &wgpu::Buffer {
&self.root_node_buffer
}
}