From b559bd5e08ba3893214a87a4a98f526b6715b1e7 Mon Sep 17 00:00:00 2001 From: zomseffen <steffen@tom.bi> Date: Wed, 12 Feb 2025 14:48:52 +0100 Subject: [PATCH] directional light --- shaders/compiled/frag_rt_quad.spv | Bin 43712 -> 44124 bytes shaders/rt_quad.frag | 20 +++- src/main.rs | 8 +- src/scene/empty_volume.rs | 62 +++++++----- src/scene/light.rs | 163 ++++++++++++++++++++++++++++-- src/scene/memorizable.rs | 10 ++ src/scene/mod.rs | 63 ++++++++++-- src/scene/oct_tree.rs | 2 +- 8 files changed, 273 insertions(+), 55 deletions(-) create mode 100644 src/scene/memorizable.rs diff --git a/shaders/compiled/frag_rt_quad.spv b/shaders/compiled/frag_rt_quad.spv index 8b8614a0b3ce271441a2226b28ab4daaee5436e2..9e46dcc4d26c15e13c8abc5d395d6021b6ab2395 100644 GIT binary patch literal 44124 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 Din!xp literal 43712 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�CF`!g!@8K+%8 z&xN+k^`T(3+@Cntmx8P3oygK)wY(Eq2CP=TTUi!vo;-7y=UZ^~<XH}^mORUY)yh08 zz|E6q67#GGS5Kamz-q~}GFYwsj;8(H@2kLVr*&v||F-|mC2bkQ)xjA<=aD?u$~C}C zP`|pd&O0@+bIkiM^X!YyI`9K1kE;2)a5a6Me{$F1dX%mYOYf&PpxltdkM@mfyJd23 z3^qsjCSdEjvEDB>h3lj49wA?!!`#*-PVDC3vYlJN)oiEhLT)>@q%_vjc5Y3%4Tm4? z+tzl=<lYW!j_~clWjl9(>!WTv<y&!>+q%TYy65i%9!r^AJHs7={@T6^SS{CxUBSkv z8{@p$4eYti{oC$vwQ{cS(eT+5uI4<>?;z|2H&$D6e;Zup-W#rVTM@Sp+*oai`wqB_ z`z~DV?jmj&+*ob757`&2p8JsWyBp2EYxf+wm#JBw<2V9bj^jvpIgUMWwd5NGHby;d z-Va>1d4ITCxkh^%J_o?nd=H&*JP>ZIw)AT>SiRY=F=)0$yZstYsg{2Afy;jN!<~DX zA7jNF>DNKvRt#nOH4a?%Ydl=7oF5Y!J`>?;<@}fgH&$Evbud`H*{?&;Y>W2v<xsHw z^Zvwhb{JSq-}GHA{zrhzz8?uM`+gK$Eq(tU*ckQn{b+F6_haB{&foYP+weIKu2#;~ z<Kf0?OWgOtW$qK;YR;F;g&)9;)t0yu!DZY@aJ6y{{Sa=fwv6@3VD;u$pMqxJwL6Z7 zQ>s~?<9r&}^%8zM*z*wnBd{^K=R5;!jDFh8c`Bv4G2T=C7`#P2hVq}l_3_-w&jjnE zZjAfXPr;i}n(r*ITzodA{`#$h{ao;pwe1{AdFnkMylQPbk5ZohcFhG~@4d}!PGj{k zc3<=h!4oO7c76s|OTRAytL4bO_RrDGtv&6y7`!G&`uhv8n!ff)o0`{<Z7ALMEz_1u zz|LjwrES}#V6{<T<F%=k*Ql*)z9FUgER*jt@Y)>7cR5%s`LwB-Z#_!mH=s;^t^nI+ z?~z-WCmH{HF`moqIs9zHk$dEAYwUgAb{yv0fim~LSCMN_!LM)dn+oo|=`9U@PlNxd z!5?ezCma0Pn%h6;-qmp1oM)G7;A-xHd3SOxJnv4lC-yq9HqVRwlE-!fxXk-2xSIQB z^8Om0yxJ3cBUqbx9UpmYH-pQ(zk#c{w<Pax;mNB#vA+XrGq2+;kL^~leF(n|T-I|t zT+O{G_52>5dbB6@4zRY2>78KntLGl}F0guj-g-A!ZAsp9*gns@T5{eCb}Yj01DEZ) zAFk&9o%THdPy4hd_77lfY2Slj^Q)(Q4}sOwzCVK1(mv;*T5>)Nc1*+n3@+RE7r2_& zj<oNu@U%~RVjlr(OZ)x?Hotn>_b6CB?fW}eE$wq%s3qq=z@Fd4KMqz){1afc#Ji5v z68{v~@lX8IV70_Q16E7C>rO54{{%ZeiGL2PmiXtvYKeE9s`;GeKJWtA{UrA$UcX*M zQ@0NJOJLib_d_p(S;npRL;7lu?<-*ESbSdv>$?PpaoSV&Yhdfnvz>Llj;5Y`Z-DJ{ z^1TU8-TG>e?^|H!Pki47r*7l4r|y4&tvmOc*8L8edh)#scD$1BJ#gyQS9^Tl2Rk?7 z`)_dSHcor4dmn%u7u%<w*8sKhJ;|Q5$Inh2UIX0scdoJf{4NFFwZ{7FR^uHhcQ5ok zz+N-<qRh`uKBCTCGmM>%jl1L>1y}DXIs3`<CC5HP!Ob^QgU?cM=iY1uH~$<BK4-yK zz&>}uji0yR*0*@U&9_9seIK+`!S!FZ;QB9LbLWWsW5&lZ%-X<QeS)T*_4X-PE$<9I z1FMzq9zKVgC-dAq|3y<zo-e>^$@3*xt<3Wk+&r1H=J_9*dh&SjRZE^}bmC~g!)U(? zo)&I9twX!l1?Pgg^VxflPO$S$oAW@fjiMUFKh8gGtvLbD?{^Fa`}-ZnXw%1erJnd1 z!OmH2&KY^?of+&L*5>??$2Ke2T-uyVa&4~N*}+HFan38be$J`#{LKaLsr{T^a{ZiJ z%XZk0_J6x}9=JaXuzj|}`pj!S<McPqHK1?i%zR+ak=OI|cYZYWoTG1m)pCv&0IPY9 zvTuJA?sJDW>v68DS&wUBL9lg~KXX_Jt{$I-YoGG>1{Oh6&-2m{uyN|X6JCtckMB{m zEy|(h-XTsumjqkCKYva?hr-ouuWL`PziV%4u>L31{xef916SAIwJF!%wfQZu{@t~| zb8<Pjy8f<RdD^oASbu-Mo%XB<SJ&UQEzh&dO5m;VwVl>&K7EX}u9d;A37>W4tH6y> zH=lcrTKrc9yT{6%kE_8wXE~p%gFT<>`B};uVB@qU&zfL!C(l}7^CV_%us-U?)NNY_ cY%XoK&335y?2&V4nL5@5>*IAt>~-h=0pwk{2><{9 diff --git a/shaders/rt_quad.frag b/shaders/rt_quad.frag index 7094bd4..aca2709 100644 --- a/shaders/rt_quad.frag +++ b/shaders/rt_quad.frag @@ -149,11 +149,11 @@ uvec4 sample_color_from_scene_info(uint volume_start, uvec2 raster_pos, uint f) } vec3 get_light_position(uint light_index) { - return vec3(uintBitsToFloat(scene_info.infos[light_index]), uintBitsToFloat(scene_info.infos[light_index + 1]), uintBitsToFloat(scene_info.infos[light_index + 2])); + return vec3(uintBitsToFloat(scene_info.infos[light_index + 1]), uintBitsToFloat(scene_info.infos[light_index + 2]), uintBitsToFloat(scene_info.infos[light_index + 3])); } vec3 get_light_color(uint light_index) { - return vec3(float(scene_info.infos[light_index + 3]) / 255.0, float(scene_info.infos[light_index + 4]) / 255.0, float(scene_info.infos[light_index + 5]) / 255.0); + return vec3(float(scene_info.infos[light_index + 4]) / 255.0, float(scene_info.infos[light_index + 5]) / 255.0, float(scene_info.infos[light_index + 6]) / 255.0); } vec3 normal_for_facing(uint facing) { @@ -377,7 +377,7 @@ vec3 get_lighting_color(uint volume_start, vec3 starting_pos, vec4 orig_color_sa uint light_num = 0; // initialize color - vec3 color_sum = vec3(0.0, 0.0, 0.0) + (orig_color_sample.xyz * 0.01); + vec3 color_sum = vec3(0.0, 0.0, 0.0);// + (orig_color_sample.xyz * 0.01); uint max_iterations = max_num_lights * max_iterations_per_light; uint iteration = 0; @@ -388,10 +388,20 @@ vec3 get_lighting_color(uint volume_start, vec3 starting_pos, vec4 orig_color_sa // abort if there is no new light break; } - vec3 light_direction = get_light_position(light_index) - starting_pos; + vec3 light_direction; + float max_factor; + if (scene_info.infos[light_index] == 0) { + //point light + light_direction = get_light_position(light_index) - starting_pos; + max_factor = 1.0; + } else if (scene_info.infos[light_index] == 1) { + // directional light + light_direction = -normalize(get_light_position(light_index)); + max_factor = pos_infinity; + } vec3 light_color = get_light_color(light_index); - Tracing result = trace_ray(volume_start, starting_pos, light_direction, 1.0, iteration, max_iterations, false); + Tracing result = trace_ray(volume_start, starting_pos, light_direction, max_factor, iteration, max_iterations, false); // add result, if there is a hit the null vector will be added color_sum += float(!result.has_hit) * result.color_mul * max(dot(normal, normalize(light_direction)), 0.0) * (orig_color_sample.xyz * light_color) / (length(light_direction) * length(light_direction)); diff --git a/src/main.rs b/src/main.rs index a81a664..93958e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -190,7 +190,7 @@ impl App { let entry = Entry::new(loader).map_err(|b| anyhow!("{}", b))?; let mut data = app_data::AppData::default(); data.use_geometry_shader = false; - data.num_lights_per_volume = 2; + data.num_lights_per_volume = 5; data.max_iterations_per_light = 20; data.diffuse_raster_steps = 0; data.diffuse_raster_size = 0.01; @@ -218,9 +218,9 @@ impl App { create_framebuffers(&device, &mut data)?; - //image::create_texture_image(&instance, &device, &mut data)?; - //image::create_texture_image_view(&device, &mut data)?; - //image::create_texture_sampler(&device, &mut data)?; + image::create_texture_image(&instance, &device, &mut data)?; + image::create_texture_image_view(&device, &mut data)?; + image::create_texture_sampler(&device, &mut data)?; scene_handler.prepare_data(&instance, &device, &mut data)?; diff --git a/src/scene/empty_volume.rs b/src/scene/empty_volume.rs index 4c36e55..5277686 100644 --- a/src/scene/empty_volume.rs +++ b/src/scene/empty_volume.rs @@ -10,7 +10,12 @@ use crate::primitives::cube::Cube; use crate::primitives::quad::Quad; use crate::scene::oct_tree::OctTree; +use super::memorizable::Memorizable; +use super::light::LightSource; use super::light::PointLight; +use super::AppData; +use super::LightsIter; +use super::Scene; pub struct EmptyVolume { pub memory_start: usize, @@ -863,12 +868,37 @@ impl EmptyVolume { } quads } + + pub fn select_lights(&self, lights: LightsIter, light_number: u32) -> Vec<u32> { + let center = self.position + Vector3{x: self.size_x / 2, y: self.size_y / 2, z: self.size_z / 2}; + let mut weighted_indices = vec![]; + for light in lights { + let weight = light.borrow().weighted_distance(center); + weighted_indices.push((weight, light.borrow().get_memory_start())); + } + weighted_indices.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + + let mut out_index = vec![]; + for index in 0..weighted_indices.len() { + out_index.push(weighted_indices[weighted_indices.len() - (index + 1)].1 as u32); + if out_index.len() == light_number as usize { + break; + } + } + while out_index.len() < light_number as usize { + out_index.push(0); + } + out_index + } +} + +impl Memorizable for EmptyVolume { // MARK: Get Buffer Mem Size - pub fn get_buffer_mem_size(&self, light_number: u32) -> u32 { + fn get_buffer_mem_size(&self, data: &AppData) -> u32 { let mut mem_size: u32 = 0; mem_size += 3; //pos mem_size += 3; //max sizes - mem_size += light_number; // light references + 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 @@ -890,7 +920,7 @@ impl EmptyVolume { mem_size } // MARK: insert into Memory - pub fn insert_into_memory(&self, mut v: Vec<u32>, light_number: u32, lights: &Vec<PointLight>) -> Vec<u32> { + 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.position.x as u32; @@ -907,7 +937,7 @@ impl EmptyVolume { v[mem_index] = self.size_z as u32; mem_index += 1; //Todo: insert lights - let selected_lights = self.select_lights(lights, light_number); + let selected_lights = self.select_lights(scene.get_light_iter(), data.num_lights_per_volume); for light in selected_lights { v[mem_index] = light; mem_index += 1; @@ -1207,26 +1237,12 @@ impl EmptyVolume { v } - pub fn select_lights(&self, lights: &Vec<PointLight>, light_number: u32) -> Vec<u32> { - let center = self.position + Vector3{x: self.size_x / 2, y: self.size_y / 2, z: self.size_z / 2}; - let mut weighted_indices = vec![]; - for light in lights { - let weight = light.weighted_distance(center); - weighted_indices.push((weight, light.memory_start)); - } - weighted_indices.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + fn get_memory_start(&self) -> usize { + self.memory_start + } - let mut out_index = vec![]; - for index in 0..weighted_indices.len() { - out_index.push(weighted_indices[weighted_indices.len() - (index + 1)].1 as u32); - if out_index.len() == light_number as usize { - break; - } - } - while out_index.len() < light_number as usize { - out_index.push(0); - } - out_index + fn set_memory_start(&mut self, memory_start: usize) { + self.memory_start = memory_start; } } diff --git a/src/scene/light.rs b/src/scene/light.rs index 20a33ef..d6a4c82 100644 --- a/src/scene/light.rs +++ b/src/scene/light.rs @@ -1,6 +1,22 @@ use cgmath::{InnerSpace, MetricSpace, Vector3}; use crate::vertex; +use super::memorizable::Memorizable; +use super::AppData; +use super::Scene; + +pub enum LightType { + POINT, + DIRECTION +} + +pub trait Light { + fn get_light_type(&self) -> LightType; + fn weighted_distance(&self, center: Vector3<usize>) -> f32; +} + +pub trait LightSource: Light + Memorizable {} + #[derive(Clone, Debug, PartialEq)] pub struct PointLight{ @@ -9,26 +25,151 @@ pub struct PointLight{ pub memory_start: usize, } -impl PointLight { - pub fn get_buffer_mem_size(&self) -> u32 { - 3 + 3 +impl Memorizable for PointLight { + fn get_buffer_mem_size(&self, data: &AppData) -> u32 { + 1 + 3 + 3 } - pub fn insert_into_memory(&self, mut v: Vec<u32>) -> Vec<u32> { - v[self.memory_start] = u32::from_ne_bytes(self.pos.x.to_ne_bytes()); - v[self.memory_start + 1] = u32::from_ne_bytes(self.pos.y.to_ne_bytes()); - v[self.memory_start + 2] = u32::from_ne_bytes(self.pos.z.to_ne_bytes()); + fn insert_into_memory(&self, mut v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32> { + v[self.memory_start] = LightType::POINT as u32; - v[self.memory_start + 3] = (self.color.x * 255.0) as u32; - v[self.memory_start + 4] = (self.color.y * 255.0) as u32; - v[self.memory_start + 5] = (self.color.z * 255.0) as u32; + v[self.memory_start + 1] = u32::from_ne_bytes(self.pos.x.to_ne_bytes()); + v[self.memory_start + 2] = u32::from_ne_bytes(self.pos.y.to_ne_bytes()); + v[self.memory_start + 3] = u32::from_ne_bytes(self.pos.z.to_ne_bytes()); + + v[self.memory_start + 4] = (self.color.x * 255.0) as u32; + v[self.memory_start + 5] = (self.color.y * 255.0) as u32; + v[self.memory_start + 6] = (self.color.z * 255.0) as u32; v } - pub fn weighted_distance(&self, center: Vector3<usize>) -> f32 { + fn get_memory_start(&self) -> usize { + self.memory_start + } + + fn set_memory_start(&mut self, memory_start: usize) { + self.memory_start = memory_start; + } +} + +impl Light for PointLight { + fn get_light_type(&self) -> LightType { + LightType::POINT + } + + fn weighted_distance(&self, center: Vector3<usize>) -> f32 { let distance = self.pos.distance(vertex::Vec3{x: center.x as f32, y: center.y as f32, z: center.z as f32}); let light_intensity = self.color.magnitude(); light_intensity / distance.powf(2.0) } +} + +impl LightSource for PointLight {} + +#[derive(Clone, Debug, PartialEq)] +pub struct DirectionalLight{ + pub direction: vertex::Vec3, + pub color: vertex::Vec3, + pub memory_start: usize, +} + +impl Memorizable for DirectionalLight { + fn get_buffer_mem_size(&self, data: &AppData) -> u32 { + 1 + 3 + 3 + } + + fn insert_into_memory(&self, mut v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32> { + v[self.memory_start] = LightType::DIRECTION as u32; + + v[self.memory_start + 1] = u32::from_ne_bytes(self.direction.x.to_ne_bytes()); + v[self.memory_start + 2] = u32::from_ne_bytes(self.direction.y.to_ne_bytes()); + v[self.memory_start + 3] = u32::from_ne_bytes(self.direction.z.to_ne_bytes()); + + v[self.memory_start + 4] = (self.color.x * 255.0) as u32; + v[self.memory_start + 5] = (self.color.y * 255.0) as u32; + v[self.memory_start + 6] = (self.color.z * 255.0) as u32; + v + } + + fn get_memory_start(&self) -> usize { + self.memory_start + } + + fn set_memory_start(&mut self, memory_start: usize) { + self.memory_start = memory_start; + } +} + +impl Light for DirectionalLight { + fn get_light_type(&self) -> LightType { + LightType::DIRECTION + } + + fn weighted_distance(&self, center: Vector3<usize>) -> f32 { + let light_intensity = self.color.magnitude(); + + light_intensity + } +} + +impl LightSource for DirectionalLight {} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_memorizable() { + let p = PointLight {pos: vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, color: vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}, memory_start: 0}; + let data= AppData::default(); + let scene = Scene::default(); + + let mem_size = p.get_buffer_mem_size(&data); + assert!(mem_size == 7); + + let mut memory = vec![0; 7]; + p.insert_into_memory(memory, &data, &scene); + } + + #[test] + #[should_panic] + fn test_mem_size() { + let p = PointLight {pos: vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, color: vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}, memory_start: 0}; + let data= AppData::default(); + let scene = Scene::default(); + + let mem_size = p.get_buffer_mem_size(&data); + assert!(mem_size == 7); + + let mut memory = vec![0; 6]; + p.insert_into_memory(memory, &data, &scene); + } + + #[test] + fn test_memorizable_directional_light() { + let p = DirectionalLight {direction: vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, color: vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}, memory_start: 0}; + let data= AppData::default(); + let scene = Scene::default(); + + let mem_size = p.get_buffer_mem_size(&data); + assert!(mem_size == 7); + + let mut memory = vec![0; 7]; + p.insert_into_memory(memory, &data, &scene); + } + + #[test] + #[should_panic] + fn test_mem_size_directional_light() { + let p = DirectionalLight {direction: vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, color: vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}, memory_start: 0}; + let data= AppData::default(); + let scene = Scene::default(); + + let mem_size = p.get_buffer_mem_size(&data); + assert!(mem_size == 7); + + let mut memory = vec![0; 6]; + p.insert_into_memory(memory, &data, &scene); + } } \ No newline at end of file diff --git a/src/scene/memorizable.rs b/src/scene/memorizable.rs new file mode 100644 index 0000000..22d8519 --- /dev/null +++ b/src/scene/memorizable.rs @@ -0,0 +1,10 @@ +use super::light::LightSource; +use super::AppData; +use super::Scene; + +pub trait Memorizable { + fn get_buffer_mem_size(&self, data: &AppData) -> u32; + fn insert_into_memory(&self, v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32>; + fn get_memory_start(&self) -> usize; + fn set_memory_start(&mut self, memory_start: usize); +} \ No newline at end of file diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 6672fc2..e9d19e7 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1,8 +1,10 @@ mod oct_tree; mod empty_volume; mod light; +mod memorizable; use anyhow::Ok; +use light::{DirectionalLight, LightSource}; use vulkanalia::prelude::v1_0::*; use anyhow::Result; @@ -11,6 +13,7 @@ use cgmath::{vec2, vec3, Vector3}; use std::cell::RefCell; use std::rc::Rc; +use crate::scene::memorizable::Memorizable; use crate::app_data::AppData; use crate::buffer; use crate::primitives::rec_cuboid::Cuboid; @@ -54,7 +57,8 @@ pub struct Scene { pub rt_memory: Vec<u32>, - pub point_lights: Vec<PointLight>, + point_lights: Vec<Rc<RefCell<PointLight>>>, + directional_lights: Vec<Rc<RefCell<DirectionalLight>>>, } impl Scene { @@ -99,8 +103,9 @@ impl Scene { }; oct_tree.set_cube(cube.clone()); - self.point_lights.push(PointLight { pos: vec3(11.0, 11.0, 11.0), color: vec3(1.0, 1.0, 1.0), memory_start: 0 }); - self.point_lights.push(PointLight { pos: vec3(9.0, 9.0, 11.0), color: vec3(0.5, 0.5, 0.5), memory_start: 0 }); + self.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(11.0, 11.0, 11.0), color: vec3(1.0, 1.0, 1.0), memory_start: 0 }))); + self.point_lights.push(Rc::new(RefCell::new(PointLight { pos: vec3(9.0, 9.0, 11.0), color: vec3(0.5, 0.5, 0.5), memory_start: 0 }))); + self.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 empty_volumes: Vec<Rc<RefCell<EmptyVolume>>>; (empty_volumes, _) = EmptyVolume::from_oct_tree(&oct_tree); @@ -156,14 +161,14 @@ impl Scene { // 3 - diffuse raster size // 4 - max recursive rays // 5 - diffuse rays per hit - for light in &mut self.point_lights { - light.memory_start = memory_index; - memory_index += light.get_buffer_mem_size() as usize; + for light in LightsIter::new(self) { + light.borrow_mut().set_memory_start(memory_index); + memory_index += light.borrow_mut().get_buffer_mem_size(data) as usize; } for volume in &empty_volumes { - volume.borrow_mut().memory_start = memory_index; - memory_index += volume.borrow().get_buffer_mem_size(data.num_lights_per_volume) as usize; + volume.borrow_mut().set_memory_start(memory_index); + memory_index += volume.borrow().get_buffer_mem_size(data) as usize; } for volume in &empty_volumes { @@ -181,10 +186,10 @@ impl Scene { volume_vec[5] = data.diffuse_rays_per_hit; for volume in &empty_volumes { - volume_vec = volume.borrow().insert_into_memory(volume_vec, data.num_lights_per_volume, &self.point_lights); + volume_vec = volume.borrow().insert_into_memory(volume_vec, data, &self); } - for light in &self.point_lights { - volume_vec = light.insert_into_memory(volume_vec); + for light in LightsIter::new(self) { + volume_vec = light.borrow().insert_into_memory(volume_vec, data, &self); } //println!("volume_vec print {:?}", volume_vec); @@ -229,4 +234,40 @@ impl Scene { device.destroy_buffer(self.vertex_buffer_quad, None); device.free_memory(self.vertex_buffer_memory_quad, None); } + + fn get_light_iter(&self) -> LightsIter { + LightsIter::new(self) + } +} + + +pub struct LightsIter<'a> { + light_index: usize, + scene: &'a Scene, +} + +impl<'a> LightsIter<'a> { + fn new(scene: &'a Scene) -> Self { + LightsIter {light_index: 0, scene: scene} + } +} + +impl<'a> Iterator for LightsIter<'a> { + type Item = Rc<RefCell<dyn LightSource>>; + + fn next(&mut self) -> Option<Self::Item> { + if self.light_index >= self.scene.point_lights.len() { + if self.light_index - self.scene.point_lights.len() >= self.scene.directional_lights.len() { + None + } else { + let result = self.scene.directional_lights[self.light_index - self.scene.point_lights.len()].clone(); + self.light_index += 1; + Some(result) + } + } else { + let result = self.scene.point_lights[self.light_index].clone(); + self.light_index += 1; + Some(result) + } + } } \ No newline at end of file diff --git a/src/scene/oct_tree.rs b/src/scene/oct_tree.rs index 7b8212d..c9f94fa 100644 --- a/src/scene/oct_tree.rs +++ b/src/scene/oct_tree.rs @@ -601,7 +601,7 @@ mod test { #[test] fn test_oct_tree(){ let mut test_tree: OctTree<Cube> = OctTree::create(512).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}; + 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());