From b581364f5a376131dd6e43428f6f58c7d7619aa3 Mon Sep 17 00:00:00 2001 From: zomseffen <steffen@tom.bi> Date: Thu, 6 Mar 2025 18:11:44 +0100 Subject: [PATCH] first implementation, clamp seems to be off? --- shaders/compiled/frag_rt_quad.spv | Bin 47560 -> 49312 bytes shaders/rt_quad.frag | 65 ++++++------ src/main.rs | 4 +- src/scene/empty_volume.rs | 166 ++++++++++++++++-------------- src/scene/generators.rs | 22 ++-- src/scene/light.rs | 8 +- src/scene/mod.rs | 2 +- src/scene/oct_tree.rs | 22 ++-- 8 files changed, 157 insertions(+), 132 deletions(-) diff --git a/shaders/compiled/frag_rt_quad.spv b/shaders/compiled/frag_rt_quad.spv index 6669b9359a3e387ab109a46747b81fec9ae0244f..d3b42758dd6695dd7320ed052f46ba1d4f98ceda 100644 GIT binary patch literal 49312 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 literal 47560 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 diff --git a/shaders/rt_quad.frag b/shaders/rt_quad.frag index 2e2a32a..a7f8001 100644 --- a/shaders/rt_quad.frag +++ b/shaders/rt_quad.frag @@ -45,9 +45,12 @@ uvec4 unpack_color(uint val) { return uvec4(val4, val3, val2, val1); } +uint array_descr_offset = 6 + max_num_lights; +uint color_array_offset = 24 + 1; + uint sample_neighbor_from_scene_info(uint volume_start, uvec2 raster_pos, uint f) { - uint array_descr_start = volume_start + 6 + max_num_lights; - uint color_array_start = array_descr_start + 24; + uint array_descr_start = volume_start + array_descr_offset; + uint color_array_start = array_descr_start + color_array_offset; uint top_color_size_u = scene_info.infos[array_descr_start]; uint top_color_size_v = scene_info.infos[array_descr_start + 1]; @@ -116,8 +119,8 @@ uint sample_neighbor_from_scene_info(uint volume_start, vec2 raster_pos, uint f) } uvec4 sample_color_from_scene_info(uint volume_start, uvec2 raster_pos, uint f) { - uint array_descr_start = volume_start + 6 + max_num_lights; - uint color_array_start = array_descr_start + 24; + uint array_descr_start = volume_start + array_descr_offset; + uint color_array_start = array_descr_start + color_array_offset; uint top_color_size_u = scene_info.infos[array_descr_start]; uint top_color_size_v = scene_info.infos[array_descr_start + 1]; @@ -216,9 +219,10 @@ Tracing trace_ray(uint volume_start, vec3 starting_pos, vec3 start_direction, fl uint cycle = start_cycle; // setup volume info uint volume_index = volume_start; - uint volume_pos_x = scene_info.infos[volume_index + 0]; - uint volume_pos_y = scene_info.infos[volume_index + 1]; - uint volume_pos_z = scene_info.infos[volume_index + 2]; + float volume_scale = uintBitsToFloat(scene_info.infos[volume_index + array_descr_offset + color_array_offset - 1]); + float volume_pos_x = uintBitsToFloat(scene_info.infos[volume_index + 0]); + float volume_pos_y = uintBitsToFloat(scene_info.infos[volume_index + 1]); + float volume_pos_z = uintBitsToFloat(scene_info.infos[volume_index + 2]); bool x_pos = direction.x > 0.0; bool x_null = (direction.x == 0.0); @@ -249,9 +253,9 @@ Tracing trace_ray(uint volume_start, vec3 starting_pos, vec3 start_direction, fl while (cycle < max_cycle) { cycle ++; - float x_border = float(volume_pos_x + (scene_info.infos[volume_index + 3]) * uint(x_pos)) - 0.5; - float y_border = float(volume_pos_y + (scene_info.infos[volume_index + 4]) * uint(y_pos)) - 0.5; - float z_border = float(volume_pos_z + (scene_info.infos[volume_index + 5]) * uint(z_pos)) - 0.5; + float x_border = volume_pos_x + float((scene_info.infos[volume_index + 3]) * uint(x_pos)) * volume_scale - 0.5 * volume_scale; + float y_border = volume_pos_y + float((scene_info.infos[volume_index + 4]) * uint(y_pos)) * volume_scale - 0.5 * volume_scale; + float z_border = volume_pos_z + float((scene_info.infos[volume_index + 5]) * uint(z_pos)) * volume_scale - 0.5 * volume_scale; bool needs_next_light = false; @@ -283,10 +287,10 @@ Tracing trace_ray(uint volume_start, vec3 starting_pos, vec3 start_direction, fl hit_facing = uint(is_x_smallest) * (2 + uint(x_pos)) + uint(is_y_smallest) * (4 + uint(y_pos)) + uint(is_z_smallest && !z_pos); float smallest_factor = min(min(x_factor, y_factor), z_factor); // maybe use multiplication instead? vec3 intersection_pos = pos + smallest_factor * direction; - u = uint(is_x_smallest) * (uint(round(intersection_pos.y)) - volume_pos_y) + - uint(is_y_smallest || is_z_smallest) * (uint(round(intersection_pos.x)) - volume_pos_x); - v = uint(is_x_smallest || is_y_smallest) * (uint(round(intersection_pos.z)) - volume_pos_z) + - uint(is_z_smallest) * (uint(round(intersection_pos.y)) - volume_pos_y); + u = uint(is_x_smallest) * (uint(round((intersection_pos.y - volume_pos_y) / volume_scale))) + + uint(is_y_smallest || is_z_smallest) * (uint(round((intersection_pos.x - volume_pos_x) / volume_scale))); + v = uint(is_x_smallest || is_y_smallest) * (uint(round((intersection_pos.z - volume_pos_z) / volume_scale))) + + uint(is_z_smallest) * (uint(round((intersection_pos.y - volume_pos_y) / volume_scale))); uint next_neighbor = sample_neighbor_from_scene_info(volume_index, uvec2(u, v), hit_facing); uvec4 color_sample = sample_color_from_scene_info(volume_index, uvec2(u, v), hit_facing); @@ -295,9 +299,10 @@ Tracing trace_ray(uint volume_start, vec3 starting_pos, vec3 start_direction, fl // not a color hit, so check neighbor if (next_neighbor != 0) { volume_index = next_neighbor; - volume_pos_x = scene_info.infos[volume_index + 0]; - volume_pos_y = scene_info.infos[volume_index + 1]; - volume_pos_z = scene_info.infos[volume_index + 2]; + volume_scale = uintBitsToFloat(scene_info.infos[volume_index + array_descr_offset + color_array_offset - 1]); + volume_pos_x = uintBitsToFloat(scene_info.infos[volume_index + 0]); + volume_pos_y = uintBitsToFloat(scene_info.infos[volume_index + 1]); + volume_pos_z = uintBitsToFloat(scene_info.infos[volume_index + 2]); } else { // neighbor miss end_color_transparent = uvec4(255, 0, 0, 255); @@ -311,9 +316,10 @@ Tracing trace_ray(uint volume_start, vec3 starting_pos, vec3 start_direction, fl color_mul_transparent = result.color_mul; volume_index = next_neighbor; - volume_pos_x = scene_info.infos[volume_index + 0]; - volume_pos_y = scene_info.infos[volume_index + 1]; - volume_pos_z = scene_info.infos[volume_index + 2]; + volume_scale = uintBitsToFloat(scene_info.infos[volume_index + array_descr_offset + color_array_offset - 1]); + volume_pos_x = uintBitsToFloat(scene_info.infos[volume_index + 0]); + volume_pos_y = uintBitsToFloat(scene_info.infos[volume_index + 1]); + volume_pos_z = uintBitsToFloat(scene_info.infos[volume_index + 2]); result.color_mul = result.color_mul * vec3(float(color_sample.x) / 255.0, float(color_sample.y) / 255.0, float(color_sample.z) / 255.0); result.has_transparent_hit = true; result.end_volume = volume_index; @@ -455,17 +461,18 @@ vec3 diffuse_tracing(uint volume_start, vec2 raster_pos, vec3 pos, uint f) { } vec3 clamp_to_volume(uint volume_start, vec3 position) { - uint volume_pos_x = scene_info.infos[volume_start + 0]; - uint volume_pos_y = scene_info.infos[volume_start + 1]; - uint volume_pos_z = scene_info.infos[volume_start + 2]; + float volume_pos_x = uintBitsToFloat(scene_info.infos[volume_start + 0]); + float volume_pos_y = uintBitsToFloat(scene_info.infos[volume_start + 1]); + float volume_pos_z = uintBitsToFloat(scene_info.infos[volume_start + 2]); + float volume_scale = uintBitsToFloat(scene_info.infos[volume_start + array_descr_offset + color_array_offset - 1]); - float high_x_border = float(volume_pos_x + (scene_info.infos[volume_start + 3])) - 0.5; - float high_y_border = float(volume_pos_y + (scene_info.infos[volume_start + 4])) - 0.5; - float high_z_border = float(volume_pos_z + (scene_info.infos[volume_start + 5])) - 0.5; + float high_x_border = volume_pos_x + float(scene_info.infos[volume_start + 3]) * volume_scale - 0.5 * volume_scale; + float high_y_border = volume_pos_y + float(scene_info.infos[volume_start + 4]) * volume_scale - 0.5 * volume_scale; + float high_z_border = volume_pos_z + float(scene_info.infos[volume_start + 5]) * volume_scale - 0.5 * volume_scale; - float low_x_border = float(volume_pos_x) - 0.5; - float low_y_border = float(volume_pos_y) - 0.5; - float low_z_border = float(volume_pos_z) - 0.5; + float low_x_border = float(volume_pos_x) - 0.5 * volume_scale; + float low_y_border = float(volume_pos_y) - 0.5 * volume_scale; + float low_z_border = float(volume_pos_z) - 0.5 * volume_scale; return vec3(min(max(position.x, low_x_border), high_x_border), min(max(position.y, low_y_border), high_y_border), min(max(position.z, low_z_border), high_z_border)); } diff --git a/src/main.rs b/src/main.rs index a1a99cd..51360d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -224,8 +224,8 @@ impl App { image::create_texture_image_view(&device, &mut data)?; image::create_texture_sampler(&device, &mut data)?; - //let cur_pos = generators::generate_test_scene(&mut scene_handler, &mut data)?; - let cur_pos = generators::generate_test_scene2(&mut scene_handler, &mut data, 21, 21,1, 5)?; + let cur_pos = generators::generate_test_scene(&mut scene_handler, &mut data)?; + //let cur_pos = generators::generate_test_scene2(&mut scene_handler, &mut data, 21, 21,1, 5)?; scene_handler.prepare_data(&instance, &device, &mut data)?; buffer::create_uniform_buffers(&instance, &device, &mut data)?; diff --git a/src/scene/empty_volume.rs b/src/scene/empty_volume.rs index 6466572..dd5be73 100644 --- a/src/scene/empty_volume.rs +++ b/src/scene/empty_volume.rs @@ -24,9 +24,8 @@ pub struct EmptyVolume { pub size_y: usize, pub size_z: usize, - pub tree_offset: Vector3<usize>, - pub tree_size: usize, - pub position: Vector3<usize>, + pub grid_position: Vector3<usize>, + pub real_position: Vector3<f32>, pub color_left: Vec<Vector3<u8>>, pub color_right: Vec<Vector3<u8>>, @@ -48,13 +47,21 @@ pub struct EmptyVolume { pub neighbor_bottom: Vec<Option<Rc<RefCell<Self>>>>, pub neighbor_back: Vec<Option<Rc<RefCell<Self>>>>, pub neighbor_front: Vec<Option<Rc<RefCell<Self>>>>, + + pub scale: f32, } impl EmptyVolume { - pub fn contains(&self, pos: &Vector3<usize>) -> bool { - self.position[0] + self.size_x > pos[0] && pos[0] >= self.position[0] && - self.position[1] + self.size_y > pos[1] && pos[1] >= self.position[1] && - self.position[2] + self.size_z > pos[2] && pos[2] >= self.position[2] + pub fn contains_grid_pos(&self, pos: &Vector3<usize>) -> bool { + self.grid_position[0] + self.size_x > pos[0] && pos[0] >= self.grid_position[0] && + self.grid_position[1] + self.size_y > pos[1] && pos[1] >= self.grid_position[1] && + self.grid_position[2] + self.size_z > pos[2] && pos[2] >= self.grid_position[2] + } + + pub fn contains_real_pos(&self, pos: &Vector3<f32>) -> bool { + self.real_position[0] + self.size_x as f32 > pos[0] && pos[0] >= self.real_position[0] && + self.real_position[1] + self.size_y as f32 > pos[1] && pos[1] >= self.real_position[1] && + self.real_position[2] + self.size_z as f32 > pos[2] && pos[2] >= self.real_position[2] } fn check_transparent(cube_result: Option<Cube>, transparent_color: &Vector3<f32>, transparent_roughness: &u8) -> bool { @@ -65,11 +72,11 @@ impl EmptyVolume { } // MARK: From Oct Tree - pub fn from_oct_tree(tree: &Rc<RefCell<OctTree<Cube>>>, tree_offset: Vector3<usize>) -> (Vec<Rc<RefCell<EmptyVolume>>>, OctTree<Rc<RefCell<EmptyVolume>>>) { + pub fn from_oct_tree(tree: &Rc<RefCell<OctTree<Cube>>>, tree_pos: Vector3<f32>) -> (Vec<Rc<RefCell<EmptyVolume>>>, OctTree<Rc<RefCell<EmptyVolume>>>) { // todo: ppotentially use a child exist check while going through the oct tree to find some obvios starting empty volumes. Will still need to check for possible expansions though let mut volumes: Vec<Rc<RefCell<EmptyVolume>>> = vec![]; - let mut neighbors: OctTree<Rc<RefCell<EmptyVolume>>> = OctTree::create(tree.borrow().size).unwrap(); + let mut neighbors: OctTree<Rc<RefCell<EmptyVolume>>> = OctTree::create(tree.borrow().size, tree.borrow().scale).unwrap(); let start_time = Instant::now(); // iterate over all block positions in the oct tree let mut check_its = 0; @@ -94,9 +101,9 @@ impl EmptyVolume { //if not check that it is not already inside of a volume let mut contained = false; for volume in &volumes { - if volume.borrow().contains(&Vector3{x: x_index, y: y_index, z: z_index}) { + if volume.borrow().contains_grid_pos(&Vector3{x: x_index, y: y_index, z: z_index}) { contained = true; - z_index = volume.borrow().size_z + volume.borrow().position.z; + z_index = volume.borrow().size_z + volume.borrow().grid_position.z; break; } } @@ -276,9 +283,8 @@ impl EmptyVolume { size_x: x_size + 1, size_y: y_size + 1, size_z: z_size + 1, - tree_offset, - tree_size: tree.borrow().size, - position: Vector3{x: x_index, y: y_index, z: z_index}, + grid_position: Vector3{x: x_index, y: y_index, z: z_index}, + real_position: tree_pos + Vector3{x: x_index as f32 * tree.borrow().scale, y: y_index as f32 * tree.borrow().scale, z: z_index as f32 * tree.borrow().scale}, color_left: vec![], color_right: vec![], color_top: vec![], @@ -297,6 +303,7 @@ impl EmptyVolume { neighbor_bottom: vec![], neighbor_back: vec![], neighbor_front: vec![], + scale: tree.borrow().scale, }; println!("adding neighbor references"); // MARK: fill in info in the neighbor octtree @@ -304,7 +311,7 @@ impl EmptyVolume { for x in 0..x_size+1 { for y in 0..y_size+1 { for z in 0..z_size+1 { - neighbors.set_element(reference.clone(), reference.borrow().position.x + x, reference.borrow().position.y + y, reference.borrow().position.z + z); + neighbors.set_element(reference.clone(), reference.borrow().grid_position.x + x, reference.borrow().grid_position.y + y, reference.borrow().grid_position.z + z); // fill only the edges /*if x == 0 || x == x_size || y == 0 || y == y_size || z==0 || z == z_size { neighbors.set_element(reference.clone(), reference.borrow().position.x + x, reference.borrow().position.y + y, reference.borrow().position.z + z) @@ -314,39 +321,39 @@ impl EmptyVolume { } println!("add the border information for color and roughness"); let x_min_pos; - if reference.borrow().position.x == 0 { + if reference.borrow().grid_position.x == 0 { // will result in an empty color and roughness map. x_min_pos = 0; } else { - x_min_pos = reference.borrow().position.x -1; + x_min_pos = reference.borrow().grid_position.x -1; } let y_min_pos; - if reference.borrow().position.y == 0 { + if reference.borrow().grid_position.y == 0 { // will result in an empty color and roughness map. y_min_pos = 0; } else { - y_min_pos = reference.borrow().position.y -1; + y_min_pos = reference.borrow().grid_position.y -1; } let z_min_pos; - if reference.borrow().position.z == 0 { + if reference.borrow().grid_position.z == 0 { // will result in an empty color and roughness map. z_min_pos = 0; } else { - z_min_pos = reference.borrow().position.z -1; + z_min_pos = reference.borrow().grid_position.z -1; } - let x_max_pos = reference.borrow().position.x + reference.borrow().size_x; - let y_max_pos = reference.borrow().position.y + reference.borrow().size_y; - let z_max_pos = reference.borrow().position.z + reference.borrow().size_z; + let x_max_pos = reference.borrow().grid_position.x + reference.borrow().size_x; + let y_max_pos = reference.borrow().grid_position.y + reference.borrow().size_y; + let z_max_pos = reference.borrow().grid_position.z + reference.borrow().size_z; // MARK: bottom face of the volume let mut bottom_colors = vec![]; let mut bottom_roughness = vec![]; let mut bottom_elements_num = 0; for x in 0..x_size+1 { for y in 0..y_size+1 { - if let Some(c) = tree.borrow().get_element(reference.borrow().position.x + x, reference.borrow().position.y + y, z_min_pos) { + if let Some(c) = tree.borrow().get_element(reference.borrow().grid_position.x + x, reference.borrow().grid_position.y + y, z_min_pos) { bottom_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; bottom_colors.push(u8_color); @@ -372,7 +379,7 @@ impl EmptyVolume { let mut top_elements_num = 0; for x in 0..x_size+1 { for y in 0..y_size+1 { - if let Some(c) = tree.borrow().get_element(reference.borrow().position.x + x, reference.borrow().position.y + y, z_max_pos) { + if let Some(c) = tree.borrow().get_element(reference.borrow().grid_position.x + x, reference.borrow().grid_position.y + y, z_max_pos) { top_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; top_colors.push(u8_color); @@ -399,7 +406,7 @@ impl EmptyVolume { let mut back_elements_num = 0; for x in 0..x_size+1 { for z in 0..z_size+1 { - if let Some(c) = tree.borrow().get_element(reference.borrow().position.x + x, y_max_pos, reference.borrow().position.z + z) { + if let Some(c) = tree.borrow().get_element(reference.borrow().grid_position.x + x, y_max_pos, reference.borrow().grid_position.z + z) { back_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; back_colors.push(u8_color); @@ -426,7 +433,7 @@ impl EmptyVolume { let mut front_elements_num = 0; for x in 0..x_size+1 { for z in 0..z_size+1 { - if let Some(c) = tree.borrow().get_element(reference.borrow().position.x + x, y_min_pos, reference.borrow().position.z + z) { + if let Some(c) = tree.borrow().get_element(reference.borrow().grid_position.x + x, y_min_pos, reference.borrow().grid_position.z + z) { front_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; front_colors.push(u8_color); @@ -453,7 +460,7 @@ impl EmptyVolume { let mut left_elements_num = 0; for y in 0..y_size+1 { for z in 0..z_size+1 { - if let Some(c) = tree.borrow().get_element(x_min_pos, reference.borrow().position.y + y, reference.borrow().position.z + z) { + if let Some(c) = tree.borrow().get_element(x_min_pos, reference.borrow().grid_position.y + y, reference.borrow().grid_position.z + z) { left_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; left_colors.push(u8_color); @@ -480,7 +487,7 @@ impl EmptyVolume { let mut right_elements_num = 0; for y in 0..y_size+1 { for z in 0..z_size+1 { - if let Some(c) = tree.borrow().get_element(x_max_pos, reference.borrow().position.y + y, reference.borrow().position.z + z) { + if let Some(c) = tree.borrow().get_element(x_max_pos, reference.borrow().grid_position.y + y, reference.borrow().grid_position.z + z) { right_elements_num += 1; let u8_color = Vector3 {x: (c.color * 255.0).x.min(255.0).max(0.0) as u8, y: (c.color * 255.0).y.min(255.0).max(0.0) as u8, z: (c.color * 255.0).z.min(255.0).max(0.0) as u8}; right_colors.push(u8_color); @@ -516,39 +523,39 @@ impl EmptyVolume { // MARK: Neighbor Linkage for reference in volumes.iter_mut() { let x_min_pos; - if reference.borrow().position.x == 0 { + if reference.borrow().grid_position.x == 0 { // will result in an empty color and roughness map. x_min_pos = 0; } else { - x_min_pos = reference.borrow().position.x - 1; + x_min_pos = reference.borrow().grid_position.x - 1; } let y_min_pos; - if reference.borrow().position.y == 0 { + if reference.borrow().grid_position.y == 0 { // will result in an empty color and roughness map. y_min_pos = 0; } else { - y_min_pos = reference.borrow().position.y - 1; + y_min_pos = reference.borrow().grid_position.y - 1; } let z_min_pos; - if reference.borrow().position.z == 0 { + if reference.borrow().grid_position.z == 0 { // will result in an empty color and roughness map. z_min_pos = 0; } else { - z_min_pos = reference.borrow().position.z - 1; + z_min_pos = reference.borrow().grid_position.z - 1; } - let x_max_pos = reference.borrow().position.x + reference.borrow().size_x; - let y_max_pos = reference.borrow().position.y + reference.borrow().size_y; - let z_max_pos = reference.borrow().position.z + reference.borrow().size_z; + let x_max_pos = reference.borrow().grid_position.x + reference.borrow().size_x; + let y_max_pos = reference.borrow().grid_position.y + reference.borrow().size_y; + let z_max_pos = reference.borrow().grid_position.z + reference.borrow().size_z; // MARK: bottom face of the volume let mut bottom_neighbors = vec![]; let mut bottom_elements_num = 0; let mut all_same = true; for x in 0..reference.borrow().size_x { for y in 0..reference.borrow().size_y { - if let Some(c) = neighbors.get_element(reference.borrow().position.x + x, reference.borrow().position.y + y, z_min_pos) { + if let Some(c) = neighbors.get_element(reference.borrow().grid_position.x + x, reference.borrow().grid_position.y + y, z_min_pos) { bottom_elements_num += 1; bottom_neighbors.push(Some(c.clone())); all_same = all_same && (bottom_neighbors[0] == Some(c)); @@ -576,7 +583,7 @@ impl EmptyVolume { let mut all_same = true; for x in 0..reference.borrow().size_x { for y in 0..reference.borrow().size_y { - if let Some(c) = neighbors.get_element(reference.borrow().position.x + x, reference.borrow().position.y + y, z_max_pos) { + if let Some(c) = neighbors.get_element(reference.borrow().grid_position.x + x, reference.borrow().grid_position.y + y, z_max_pos) { top_elements_num += 1; top_neighbors.push(Some(c.clone())); all_same = all_same && (top_neighbors[0] == Some(c)); @@ -605,7 +612,7 @@ impl EmptyVolume { let mut all_same = true; for x in 0..reference.borrow().size_x { for z in 0..reference.borrow().size_z { - if let Some(c) = neighbors.get_element(reference.borrow().position.x + x, y_max_pos, reference.borrow().position.z + z) { + if let Some(c) = neighbors.get_element(reference.borrow().grid_position.x + x, y_max_pos, reference.borrow().grid_position.z + z) { back_elements_num += 1; back_neighbors.push(Some(c.clone())); all_same = all_same && (back_neighbors[0] == Some(c)); @@ -634,7 +641,7 @@ impl EmptyVolume { let mut all_same = true; for x in 0..reference.borrow().size_x { for z in 0..reference.borrow().size_z { - if let Some(c) = neighbors.get_element(reference.borrow().position.x + x, y_min_pos, reference.borrow().position.z + z) { + if let Some(c) = neighbors.get_element(reference.borrow().grid_position.x + x, y_min_pos, reference.borrow().grid_position.z + z) { front_elements_num += 1; front_neighbors.push(Some(c.clone())); all_same = all_same && (front_neighbors[0] == Some(c)); @@ -663,7 +670,7 @@ impl EmptyVolume { let mut all_same = true; for y in 0..reference.borrow().size_y { for z in 0..reference.borrow().size_z { - if let Some(c) = neighbors.get_element(x_min_pos, reference.borrow().position.y + y, reference.borrow().position.z + z) { + if let Some(c) = neighbors.get_element(x_min_pos, reference.borrow().grid_position.y + y, reference.borrow().grid_position.z + z) { left_elements_num += 1; left_neighbors.push(Some(c.clone())); all_same = all_same && (left_neighbors[0] == Some(c)); @@ -692,7 +699,7 @@ impl EmptyVolume { let mut all_same = true; for y in 0..reference.borrow().size_y { for z in 0..reference.borrow().size_z { - if let Some(c) = neighbors.get_element(x_max_pos, reference.borrow().position.y + y, reference.borrow().position.z + z) { + if let Some(c) = neighbors.get_element(x_max_pos, reference.borrow().grid_position.y + y, reference.borrow().grid_position.z + z) { right_elements_num += 1; right_neighbors.push(Some(c.clone())); all_same = all_same && (right_neighbors[0] == Some(c)); @@ -761,7 +768,7 @@ impl EmptyVolume { // MARK: To Quads pub fn to_quads(&self) -> Vec<Quad> { let mut quads = vec![]; - let float_pos = Vector3 {x: (self.tree_offset.x * self.tree_size + self.position.x) as f32, y: (self.tree_offset.y * self.tree_size + self.position.y) as f32, z: (self.tree_offset.z * self.tree_size + self.position.z) as f32}; + let float_pos = self.real_position; //bottom sides of the volumes, top side of the block let mut done = vec![]; for x in 0..self.size_x { @@ -785,10 +792,10 @@ impl EmptyVolume { let quad = Quad { - pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + y as f32, z: -0.5 }, - pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + y as f32, z: -0.5 }, - pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: 0.5 + (y + size_2) as f32, z: -0.5 }, - pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: 0.5 + (y + size_2) as f32, z: -0.5 }, + pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + y as f32, z: -0.5 } * self.scale, + pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + y as f32, z: -0.5 } * self.scale, + pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: 0.5 + (y + size_2) as f32, z: -0.5 } * self.scale, + pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: 0.5 + (y + size_2) as f32, z: -0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: x as u32, y: y as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -818,10 +825,10 @@ impl EmptyVolume { } } let quad = Quad { - pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + y as f32, z: self.size_z as f32 - 0.5 }, - pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + y as f32, z: self.size_z as f32 - 0.5 }, - pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: 0.5 + (y + size_2) as f32, z: self.size_z as f32 - 0.5 }, - pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: 0.5 + (y + size_2) as f32, z: self.size_z as f32 - 0.5 }, + pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + y as f32, z: self.size_z as f32 - 0.5 } * self.scale, + pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + y as f32, z: self.size_z as f32 - 0.5 } * self.scale, + pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: 0.5 + (y + size_2) as f32, z: self.size_z as f32 - 0.5 } * self.scale, + pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: 0.5 + (y + size_2) as f32, z: self.size_z as f32 - 0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: x as u32, y: y as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -852,10 +859,10 @@ impl EmptyVolume { } } let quad = Quad { - pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + 0 as f32, z: z as f32 - 0.5 }, - pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + 0 as f32, z: (z + size_2) as f32 + 0.5 }, - pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + 0 as f32, z: (z + size_2) as f32 + 0.5 }, - pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + 0 as f32, z: z as f32 - 0.5 }, + pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + 0 as f32, z: z as f32 - 0.5 } * self.scale, + pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + 0 as f32, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + 0 as f32, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + 0 as f32, z: z as f32 - 0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: x as u32, y: z as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -886,10 +893,10 @@ impl EmptyVolume { } } let quad = Quad { - pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + self.size_y as f32, z: z as f32 - 0.5 }, - pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + self.size_y as f32, z: (z + size_2) as f32 + 0.5 }, - pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + self.size_y as f32, z: (z + size_2) as f32 + 0.5 }, - pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + self.size_y as f32, z: z as f32 - 0.5 }, + pos1: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + self.size_y as f32, z: z as f32 - 0.5 } * self.scale, + pos2: float_pos + Vector3 { x: -0.5 + x as f32, y: -0.5 + self.size_y as f32, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos3: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + self.size_y as f32, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos4: float_pos + Vector3 { x: 0.5 + (x + size_1) as f32, y: -0.5 + self.size_y as f32, z: z as f32 - 0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: x as u32, y: z as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -920,10 +927,10 @@ impl EmptyVolume { } } let quad = Quad { - pos1: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: y as f32 - 0.5, z: z as f32 - 0.5 }, - pos2: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: y as f32 - 0.5, z: (z + size_2) as f32 + 0.5 }, - pos3: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: (y + size_1) as f32 + 0.5, z: (z + size_2) as f32 + 0.5 }, - pos4: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: (y + size_1) as f32 + 0.5, z: z as f32 - 0.5 }, + pos1: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: y as f32 - 0.5, z: z as f32 - 0.5 } * self.scale, + pos2: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: y as f32 - 0.5, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos3: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: (y + size_1) as f32 + 0.5, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos4: float_pos + Vector3 { x: -0.5 + 0.0 as f32, y: (y + size_1) as f32 + 0.5, z: z as f32 - 0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: y as u32, y: z as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -954,10 +961,10 @@ impl EmptyVolume { } } let quad = Quad { - pos2: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: y as f32 - 0.5, z: z as f32 - 0.5 }, - pos1: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: y as f32 - 0.5, z: (z + size_2) as f32 + 0.5 }, - pos4: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: (y + size_1) as f32 + 0.5, z: (z + size_2) as f32 + 0.5 }, - pos3: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: (y + size_1) as f32 + 0.5, z: z as f32 - 0.5 }, + pos2: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: y as f32 - 0.5, z: z as f32 - 0.5 } * self.scale, + pos1: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: y as f32 - 0.5, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos4: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: (y + size_1) as f32 + 0.5, z: (z + size_2) as f32 + 0.5 } * self.scale, + pos3: float_pos + Vector3 { x: -0.5 + self.size_x as f32, y: (y + size_1) as f32 + 0.5, z: z as f32 - 0.5 } * self.scale, raster_pos: cgmath::Vector2 { x: y as u32, y: z as u32 }, size: cgmath::Vector2 { x: (size_1 + 1) as u32, y: (size_2 + 1) as u32 }, volume_index: self.memory_start as u32, @@ -972,7 +979,7 @@ impl EmptyVolume { pub fn select_lights(&self, lights: LightsIter, light_number: u32, min_light_weight: f32) -> Vec<u32> { let mut weighted_indices = vec![]; for light in lights { - let weight = light.borrow().weighted_distance(self.position + self.tree_offset * self.tree_size, Vector3{x: self.size_x, y: self.size_y, z: self.size_z}); + let weight = light.borrow().weighted_distance(self.real_position, Vector3{x: self.size_x as f32, y: self.size_y as f32, z: self.size_z as f32} * self.scale); if weight >= min_light_weight { weighted_indices.push((weight, light.borrow().get_memory_start())); } @@ -1057,8 +1064,8 @@ impl EmptyVolume { let mask = Vector3 {x: 1, y: 1, z: 1} - (step_one + step_two); let negated_mask = (step_one + step_two); - let volume_start_first = negated_mask.mul_element_wise(volume.borrow().position) + first_pos.mul_element_wise(mask); - let volume_start_second = negated_mask.mul_element_wise(volume.borrow().position) + second_pos.mul_element_wise(mask); + let volume_start_first = negated_mask.mul_element_wise(volume.borrow().grid_position) + first_pos.mul_element_wise(mask); + let volume_start_second = negated_mask.mul_element_wise(volume.borrow().grid_position) + second_pos.mul_element_wise(mask); let size_u; let size_v; @@ -1178,6 +1185,7 @@ impl Memorizable for EmptyVolume { mem_size += data.num_lights_per_volume; // light references mem_size += 12; //color/roughness buffer sizes, 2 values each mem_size += 12; //neighbor buffer sizes, 2 values each + mem_size += 1; //scale of the volume, 1 float // this covers full color and roughness mem_size += (self.color_top.len() as u32).max(1); @@ -1200,11 +1208,11 @@ impl Memorizable for EmptyVolume { fn insert_into_memory(&self, mut v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32> { let mut mem_index = self.memory_start; //pos - v[mem_index] = (self.tree_offset.x * self.tree_size + self.position.x) as u32; + v[mem_index] = u32::from_ne_bytes(self.real_position.x.to_ne_bytes()); mem_index += 1; - v[mem_index] = (self.tree_offset.y * self.tree_size + self.position.y) as u32; + v[mem_index] = u32::from_ne_bytes(self.real_position.y.to_ne_bytes()); mem_index += 1; - v[mem_index] = (self.tree_offset.z * self.tree_size + self.position.z) as u32; + v[mem_index] = u32::from_ne_bytes(self.real_position.z.to_ne_bytes()); mem_index += 1; //max sizes v[mem_index] = self.size_x as u32; @@ -1328,6 +1336,10 @@ impl Memorizable for EmptyVolume { } mem_index += 2; + // scale factor + v[mem_index] = u32::from_ne_bytes(self.scale.to_ne_bytes()); + mem_index += 1; + //color and roughness //check which endian should be used in conjunction of the graphics card (might already be handled by vulkan) if self.color_top.len() > 0 { @@ -1525,7 +1537,7 @@ impl Memorizable for EmptyVolume { impl PartialEq for EmptyVolume { fn eq(&self, other: &Self) -> bool { - self.position == other.position + self.grid_position == other.grid_position && self.size_x == other.size_x && self.size_y == other.size_y && self.size_z == other.size_z diff --git a/src/scene/generators.rs b/src/scene/generators.rs index 0552b27..a3ead57 100644 --- a/src/scene/generators.rs +++ b/src/scene/generators.rs @@ -22,8 +22,10 @@ pub fn generate_test_scene(scene: &mut Scene, data: &mut AppData) -> Result<(Poi let mut rng = rand::thread_rng(); let grid_size = CHUNK_SIZE as i32; - let mut oct_tree1: OctTree<Cube> = OctTree::create(CHUNK_SIZE)?; - let mut oct_tree2: OctTree<Cube> = OctTree::create(CHUNK_SIZE)?; + let scale = 1.0; + + let mut oct_tree1: OctTree<Cube> = OctTree::create(CHUNK_SIZE, scale)?; + let mut oct_tree2: OctTree<Cube> = OctTree::create(CHUNK_SIZE, scale)?; for x_index in 0..grid_size { for y_index in 0..grid_size { @@ -89,24 +91,24 @@ pub fn generate_test_scene(scene: &mut Scene, data: &mut AppData) -> Result<(Poi }; oct_tree2.set_cube(cube.clone()); - scene.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(11.0 + grid_size as f32, 11.0 + grid_size as f32, 11.0), color: vec3(1.0, 1.0, 1.0), memory_start: 0 }))); - scene.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(9.0 + grid_size as f32, 9.0 + grid_size as f32, 11.0), color: vec3(0.5, 0.5, 0.5), memory_start: 0 }))); + scene.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(11.0 + grid_size as f32, 11.0 + grid_size as f32, 11.0) * scale, color: vec3(1.0, 1.0, 1.0), memory_start: 0 }))); + scene.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(9.0 + grid_size as f32, 9.0 + grid_size as f32, 11.0) * scale, color: vec3(0.5, 0.5, 0.5), memory_start: 0 }))); scene.directional_lights.push(Rc::new(RefCell::new(DirectionalLight { direction: vec3(1.0, 1.0, -1.0), color: vec3(0.1, 0.1, 0.1), memory_start: 0 }))); let cube = Cuboid { - pos: vec3(11.0 + grid_size as f32, 11.0 + grid_size as f32, 11.0), + pos: vec3(11.0 + grid_size as f32, 11.0 + grid_size as f32, 11.0) * scale, color: vec3(1.0, 1.0, 1.0), tex_coord: vec2(0.0, 0.0), - size: Vector3 {x: 0.5, y: 0.5, z: 0.5} + size: Vector3 {x: 0.5, y: 0.5, z: 0.5} * scale }; let index = scene.sized_vertices.len(); cube.draw(&data.topology, index, scene); let cube = Cuboid { - pos: vec3(9.0 + grid_size as f32, 9.0 + grid_size as f32, 11.0), + pos: vec3(9.0 + grid_size as f32, 9.0 + grid_size as f32, 11.0) * scale, color: vec3(1.0, 1.0, 1.0), tex_coord: vec2(0.0, 0.0), - size: Vector3 {x: 0.5, y: 0.5, z: 0.5} + size: Vector3 {x: 0.5, y: 0.5, z: 0.5} * scale }; let index = scene.sized_vertices.len(); cube.draw(&data.topology, index, scene); @@ -122,6 +124,8 @@ pub fn generate_test_scene(scene: &mut Scene, data: &mut AppData) -> Result<(Poi pub fn generate_test_scene2(scene: &mut Scene, data: &mut AppData, chunk_num_x: usize, chunk_num_y: usize, chunk_num_z: usize, num_gaussians: usize) -> Result<(Point3<f32>)> { let mut rng = rand::thread_rng(); + let scale = 1.0; + let grid_size = CHUNK_SIZE as i32; let max_x = chunk_num_x * grid_size as usize; @@ -152,7 +156,7 @@ pub fn generate_test_scene2(scene: &mut Scene, data: &mut AppData, chunk_num_x: for y in 0..chunk_num_y { oct_trees[z].push(vec![]); for x in 0..chunk_num_x { - oct_trees[z][y].push(Rc::new(RefCell::new(OctTree::<Cube>::create(CHUNK_SIZE)?))); + oct_trees[z][y].push(Rc::new(RefCell::new(OctTree::<Cube>::create(CHUNK_SIZE, scale)?))); } } } diff --git a/src/scene/light.rs b/src/scene/light.rs index bf39edc..a7e15c5 100644 --- a/src/scene/light.rs +++ b/src/scene/light.rs @@ -13,7 +13,7 @@ pub enum LightType { pub trait Light { fn get_light_type(&self) -> LightType; - fn weighted_distance(&self, pos: Vector3<usize>, size: Vector3<usize>) -> f32; + fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32; } pub trait LightSource: Light + Memorizable {} @@ -58,9 +58,9 @@ impl Light for PointLight { LightType::POINT } - fn weighted_distance(&self, pos: Vector3<usize>, size: Vector3<usize>) -> f32 { + fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32 { let low_end = vertex::Vec3{x: pos.x as f32, y: pos.y as f32, z: pos.z as f32}; - let high_end = vertex::Vec3{x: (pos.x + size.x) as f32, y: (pos.y + size.y) as f32, z: (pos.z + size.z) as f32}; + let high_end = pos + size; let distance; if low_end.x <= self.pos.x && self.pos.x <= high_end.x && low_end.y <= self.pos.y && self.pos.y <= high_end.y && low_end.z <= self.pos.z && self.pos.z <= high_end.z { let diff_low = self.pos - low_end; @@ -152,7 +152,7 @@ impl Light for DirectionalLight { LightType::DIRECTION } - fn weighted_distance(&self, pos: Vector3<usize>, size: Vector3<usize>) -> f32 { + fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32 { let light_intensity = self.color.magnitude(); light_intensity diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 2bfed34..b9e7b91 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -83,7 +83,7 @@ impl Scene { for oct_tree in oct_tree_line_y { let mut new_volumes: Vec<Rc<RefCell<EmptyVolume>>>; let new_neighbors; - (new_volumes, new_neighbors) = EmptyVolume::from_oct_tree(oct_tree, Vector3 { x: x_index, y: y_index, z: z_index }); + (new_volumes, new_neighbors) = EmptyVolume::from_oct_tree(oct_tree, Vector3 { x: (x_index * CHUNK_SIZE) as f32 * oct_tree.borrow().scale, y: (y_index * CHUNK_SIZE) as f32 * oct_tree.borrow().scale, z: (z_index * CHUNK_SIZE) as f32 * oct_tree.borrow().scale }); empty_volumes.append(&mut new_volumes); neighbor_trees[z_index][y_index].push(Rc::new(new_neighbors)); diff --git a/src/scene/oct_tree.rs b/src/scene/oct_tree.rs index e1c4970..9b80ac4 100644 --- a/src/scene/oct_tree.rs +++ b/src/scene/oct_tree.rs @@ -27,6 +27,7 @@ pub struct OctTree<T> { pub blocks: Vec<Option<T>>, pub size: usize, + pub scale: f32, } #[warn(non_snake_case)] @@ -43,7 +44,7 @@ impl OctTree<Cube> { } impl<T: Clone> OctTree<T> { - pub fn create(size: usize) -> Result<Self> { + 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 { @@ -68,6 +69,7 @@ impl<T: Clone> OctTree<T> { blocks: blocks, size, + scale, }) } @@ -89,7 +91,7 @@ impl<T: Clone> OctTree<T> { 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).unwrap(); + 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))); } @@ -101,7 +103,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x - mid_point, y - mid_point, z); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -115,7 +117,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x - mid_point, y, z - mid_point); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -127,7 +129,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x - mid_point, y, z); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -143,7 +145,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x, y - mid_point, z - mid_point); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -155,7 +157,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x, y - mid_point, z); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -169,7 +171,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x, y, z - mid_point); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -181,7 +183,7 @@ impl<T: Clone> OctTree<T> { child.borrow_mut().set_element_internal(element, x, y, z); }, None => { - let mut child = OctTree::create(self.size / 2).unwrap(); + 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))); } @@ -600,7 +602,7 @@ mod test { #[test] fn test_oct_tree(){ - let mut test_tree: OctTree<Cube> = OctTree::create(512).unwrap(); + 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());