use anyhow::Ok; use anyhow::Result; use std::cell::RefCell; use std::rc::Rc; use crate::primitives::cube::Cube; extern crate rand; pub const CHUNK_SIZE_EXPONENT: u32 = 6; pub const CHUNK_SIZE: usize = (2 as usize).pow(CHUNK_SIZE_EXPONENT); pub const MAX_TREE_DEPTH: usize = CHUNK_SIZE_EXPONENT as usize - 2; pub const MIN_CHUNK_SIZE: usize = CHUNK_SIZE / (2 as usize).pow(MAX_TREE_DEPTH as u32); #[derive(Clone, Debug)] #[allow(non_snake_case)] pub struct OctTree<T> { pub child_XYZ: Option<Rc<RefCell<Self>>>, pub child_xYZ: Option<Rc<RefCell<Self>>>, pub child_xyZ: Option<Rc<RefCell<Self>>>, pub child_XyZ: Option<Rc<RefCell<Self>>>, pub child_XYz: Option<Rc<RefCell<Self>>>, pub child_xYz: Option<Rc<RefCell<Self>>>, pub child_xyz: Option<Rc<RefCell<Self>>>, pub child_Xyz: Option<Rc<RefCell<Self>>>, pub blocks: Vec<Option<T>>, pub size: usize, pub scale: f32, } #[warn(non_snake_case)] impl OctTree<Cube> { pub fn set_cube(&mut self, cube: Cube) { let x = cube.pos.x as usize; let y = cube.pos.y as usize; let z = cube.pos.z as usize; assert!(x < self.size, "x value out of range!"); assert!(y < self.size, "y value out of range!"); assert!(z < self.size, "z value out of range!"); self.set_element_internal(cube, x, y, z); } } impl<T: Clone> OctTree<T> { pub fn create(size: usize, scale: f32) -> Result<Self> { let mut blocks: Vec<Option<T>> = vec![]; if size == MIN_CHUNK_SIZE { for _ in 0..MIN_CHUNK_SIZE { for _ in 0..MIN_CHUNK_SIZE { for _ in 0..MIN_CHUNK_SIZE { blocks.push(None); } } } } Ok(Self { child_XYZ: None, child_xYZ: None, child_xyZ: None, child_XyZ: None, child_XYz: None, child_xYz: None, child_xyz: None, child_Xyz: None, blocks: blocks, size, scale, }) } pub fn set_element(&mut self, element: T, x: usize, y: usize, z: usize) { assert!(x < self.size, "x value out of range!"); assert!(y < self.size, "y value out of range!"); assert!(z < self.size, "z value out of range!"); self.set_element_internal(element, x, y, z); } fn set_element_internal(&mut self, element: T, x: usize, y: usize, z: usize) { if self.size > MIN_CHUNK_SIZE { let mid_point = self.size / 2; if x >= mid_point { if y >= mid_point { if z >= mid_point { match &self.child_XYZ { Some(child) => { child.borrow_mut().set_element_internal(element, x - mid_point, y - mid_point, z - mid_point); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x - mid_point, y - mid_point, z - mid_point); self.child_XYZ = Some(Rc::new(RefCell::new(child))); } } } else { match &self.child_XYz { Some(child) => { child.borrow_mut().set_element_internal(element, x - mid_point, y - mid_point, z); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x - mid_point, y - mid_point, z); self.child_XYz = Some(Rc::new(RefCell::new(child))); } } } } else { if z >= mid_point { match &self.child_XyZ { Some(child) => { child.borrow_mut().set_element_internal(element, x - mid_point, y, z - mid_point); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x - mid_point, y, z - mid_point); self.child_XyZ = Some(Rc::new(RefCell::new(child))); } } } else { match &self.child_Xyz { Some(child) => { child.borrow_mut().set_element_internal(element, x - mid_point, y, z); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x - mid_point, y, z); self.child_Xyz = Some(Rc::new(RefCell::new(child))); } } } } } else { if y >= mid_point { if z >= mid_point { match &self.child_xYZ { Some(child) => { child.borrow_mut().set_element_internal(element, x, y - mid_point, z - mid_point); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x, y - mid_point, z - mid_point); self.child_xYZ = Some(Rc::new(RefCell::new(child))); } } } else { match &self.child_xYz { Some(child) => { child.borrow_mut().set_element_internal(element, x, y - mid_point, z); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x, y - mid_point, z); self.child_xYz = Some(Rc::new(RefCell::new(child))); } } } } else { if z >= mid_point { match &self.child_xyZ { Some(child) => { child.borrow_mut().set_element_internal(element, x, y, z - mid_point); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x, y, z - mid_point); self.child_xyZ = Some(Rc::new(RefCell::new(child))); } } } else { match &self.child_xyz { Some(child) => { child.borrow_mut().set_element_internal(element, x, y, z); }, None => { let mut child = OctTree::create(self.size / 2, self.scale).unwrap(); child.set_element_internal(element, x, y, z); self.child_xyz = Some(Rc::new(RefCell::new(child))); } } } } } } else { self.blocks[z * MIN_CHUNK_SIZE * MIN_CHUNK_SIZE + y * MIN_CHUNK_SIZE + x] = Some(element); } } pub fn clear_cube(&mut self, x: usize, y: usize, z: usize) { assert!(x < self.size, "x value out of range!"); assert!(y < self.size, "y value out of range!"); assert!(z < self.size, "z value out of range!"); //self.blocks.remove(&vec3(x as u32, y as u32, z as u32)); self.clear_cube_internal(x, y, z) } fn clear_cube_internal(&mut self, x: usize, y: usize, z: usize) { if self.size > MIN_CHUNK_SIZE { let mid_point = self.size / 2; if x >= mid_point { if y >= mid_point { if z >= mid_point { match &self.child_XYZ { Some(child) => { child.borrow_mut().clear_cube_internal(x - mid_point, y - mid_point, z - mid_point); }, None => {} } } else { match &self.child_XYz { Some(child) => { child.borrow_mut().clear_cube_internal(x - mid_point, y - mid_point, z); }, None => {} } } } else { if z >= mid_point { match &self.child_XyZ { Some(child) => { child.borrow_mut().clear_cube_internal(x - mid_point, y, z - mid_point); }, None => {} } } else { match &self.child_Xyz { Some(child) => { child.borrow_mut().clear_cube_internal(x - mid_point, y, z); }, None => {} } } } } else { if y >= mid_point { if z >= mid_point { match &self.child_xYZ { Some(child) => { child.borrow_mut().clear_cube_internal(x, y - mid_point, z - mid_point); }, None => {} } } else { match &self.child_xYz { Some(child) => { child.borrow_mut().clear_cube_internal(x, y - mid_point, z); }, None => {} } } } else { if z >= mid_point { match &self.child_xyZ { Some(child) => { child.borrow_mut().clear_cube_internal(x, y, z - mid_point); }, None => {} } } else { match &self.child_xyz { Some(child) => { child.borrow_mut().clear_cube_internal(x, y, z); }, None => {} } } } } } else { self.blocks[z * MIN_CHUNK_SIZE * MIN_CHUNK_SIZE + y * MIN_CHUNK_SIZE + x] = None; } } pub fn get_element(&self, x: usize, y: usize, z: usize) -> Option<T> { if x >= self.size || y >= self.size || z >= self.size { return None } if self.size > MIN_CHUNK_SIZE { let mid_point = self.size / 2; if x >= mid_point { if y >= mid_point { if z >= mid_point { match &self.child_XYZ { Some(child) => { child.borrow().get_element(x - mid_point, y - mid_point, z - mid_point) }, None => None } } else { match &self.child_XYz { Some(child) => { child.borrow().get_element( x - mid_point, y - mid_point, z) }, None => None } } } else { if z >= mid_point { match &self.child_XyZ { Some(child) => { child.borrow().get_element(x - mid_point, y, z - mid_point) }, None => None } } else { match &self.child_Xyz { Some(child) => { child.borrow().get_element(x - mid_point, y, z) }, None => None } } } } else { if y >= mid_point { if z >= mid_point { match &self.child_xYZ { Some(child) => { child.borrow().get_element(x, y - mid_point, z - mid_point) }, None => None } } else { match &self.child_xYz { Some(child) => { child.borrow().get_element(x, y - mid_point, z) }, None => None } } } else { if z >= mid_point { match &self.child_xyZ { Some(child) => { child.borrow().get_element(x, y, z - mid_point) }, None => None } } else { match &self.child_xyz { Some(child) => { child.borrow().get_element(x, y, z) }, None => None } } } } } else { self.blocks[z * MIN_CHUNK_SIZE * MIN_CHUNK_SIZE + y * MIN_CHUNK_SIZE + x].clone() } } pub fn test_element(&self, x: usize, y: usize, z: usize) -> (bool, usize, (usize, usize, usize), Option<T>) { self.test_element_internal(x, y, z, 0, 0, 0) } fn test_element_internal(&self, x: usize, y: usize, z: usize, node_start_x: usize, node_start_y: usize, node_start_z: usize) -> (bool, usize, (usize, usize, usize), Option<T>) { if x >= self.size || y >= self.size || z >= self.size { return (false, 0, (0, 0, 0), None) } if self.size > MIN_CHUNK_SIZE { let mid_point = self.size / 2; if x >= mid_point { if y >= mid_point { if z >= mid_point { match &self.child_XYZ { Some(child) => { child.borrow().test_element_internal(x - mid_point, y - mid_point, z - mid_point, node_start_x + mid_point, node_start_y + mid_point, node_start_z + mid_point) }, None => (false, mid_point, (node_start_x + mid_point, node_start_y + mid_point, node_start_z + mid_point), None) } } else { match &self.child_XYz { Some(child) => { child.borrow().test_element_internal( x - mid_point, y - mid_point, z, node_start_x + mid_point, node_start_y + mid_point, node_start_z) }, None => (false, mid_point, (node_start_x + mid_point, node_start_y + mid_point, node_start_z), None) } } } else { if z >= mid_point { match &self.child_XyZ { Some(child) => { child.borrow().test_element_internal(x - mid_point, y, z - mid_point, node_start_x + mid_point, node_start_y, node_start_z + mid_point) }, None => (false, mid_point, (node_start_x + mid_point, node_start_y, node_start_z + mid_point), None) } } else { match &self.child_Xyz { Some(child) => { child.borrow().test_element_internal(x - mid_point, y, z, node_start_x + mid_point, node_start_y, node_start_z) }, None => (false, mid_point, (node_start_x + mid_point, node_start_y, node_start_z), None) } } } } else { if y >= mid_point { if z >= mid_point { match &self.child_xYZ { Some(child) => { child.borrow().test_element_internal(x, y - mid_point, z - mid_point, node_start_x, node_start_y + mid_point, node_start_z + mid_point) }, None => (false, mid_point, (node_start_x, node_start_y + mid_point, node_start_z + mid_point), None) } } else { match &self.child_xYz { Some(child) => { child.borrow().test_element_internal(x, y - mid_point, z, node_start_x, node_start_y + mid_point, node_start_z) }, None => (false, mid_point, (node_start_x, node_start_y + mid_point, node_start_z), None) } } } else { if z >= mid_point { match &self.child_xyZ { Some(child) => { child.borrow().test_element_internal(x, y, z - mid_point, node_start_x, node_start_y, node_start_z + mid_point) }, None => (false, mid_point, (node_start_x, node_start_y, node_start_z + mid_point), None) } } else { match &self.child_xyz { Some(child) => { child.borrow().test_element_internal(x, y, z, node_start_x, node_start_y, node_start_z) }, None => (false, mid_point, (node_start_x , node_start_y, node_start_z), None) } } } } } else { if let Some(c) = &self.blocks[z * MIN_CHUNK_SIZE * MIN_CHUNK_SIZE + y * MIN_CHUNK_SIZE + x] { (true, 1, (node_start_x + x, node_start_y + y, node_start_z + z), Some(c.clone())) } else { (false, 1, (node_start_x + x, node_start_y + y, node_start_z + z), None) } } } } pub struct OctTreeIter<'a> { iter_x: usize, iter_y: usize, iter_z: usize, todo: Vec<Rc<RefCell<OctTree<Cube>>>>, chunk: &'a OctTree<Cube> } impl<'a> OctTreeIter<'a> { pub fn create(chunk: &'a OctTree<Cube>) -> Result<Self> { let mut out = Self { iter_x: 0, iter_y: 0, iter_z: 0, todo: vec![], chunk }; out.add_todo(&chunk); Ok(out) } fn add_todo(&mut self, oct_tree: &OctTree<Cube>) { match &oct_tree.child_XYZ { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_xYZ { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_xyZ { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_XyZ { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_XYz { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_xYz { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_xyz { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; match &oct_tree.child_Xyz { Some(child) => { self.todo.push(child.clone()); }, None => {}, }; } } impl<'a> Iterator for OctTreeIter<'a> { type Item = Option<Cube>; fn next(&mut self) -> Option<Self::Item> { if self.todo.len() != 0 { while self.todo.last().unwrap().borrow().blocks.len() == 0 { let oct_tree = self.todo.pop().unwrap(); self.add_todo(&oct_tree.borrow()); } if self.iter_x < MIN_CHUNK_SIZE && self.iter_y < MIN_CHUNK_SIZE && self.iter_z < MIN_CHUNK_SIZE { let result = self.todo.last().unwrap().borrow().blocks[self.iter_x + self.iter_y * MIN_CHUNK_SIZE + self.iter_z * MIN_CHUNK_SIZE * MIN_CHUNK_SIZE].clone(); self.iter_x += 1; if self.iter_x >= MIN_CHUNK_SIZE { self.iter_x = 0; self.iter_y += 1; } if self.iter_y >= MIN_CHUNK_SIZE { self.iter_y = 0; self.iter_z += 1; } if self.iter_z == MIN_CHUNK_SIZE { self.todo.pop(); self.iter_x = 0; self.iter_y = 0; self.iter_z = 0; } return Some(result) } } self.iter_x = 0; self.iter_y = 0; self.iter_z = 0; self.add_todo(&self.chunk); None } } #[cfg(test)] mod test { use cgmath::{Vector2, Vector3}; use super::*; #[test] fn test_oct_tree(){ let mut test_tree: OctTree<Cube> = OctTree::create(512, 1.0).unwrap(); let test_cube = Cube{color: Vector3 { x: 1.0, y: 0.0, z: 0.0 }, pos: Vector3 { x: 5.0, y: 2.0, z: 10.0 }, tex_coord: Vector2{x: 0.0, y: 0.0}, transparent: false, roughness: 128}; test_tree.set_cube(test_cube.clone()); let cube_result = test_tree.get_element(5, 2, 10).unwrap(); let cube_result2 = test_tree.get_element(300, 2, 10); assert_eq!(test_cube, cube_result); assert_eq!(cube_result2, None); let test_iter = OctTreeIter::create(&test_tree).unwrap(); let mut count = 0; for result in test_iter { if let Some(_) = result { count += 1; } } assert_eq!(count, 1); } }