feo_oop_engine/scene/game_object/light/
directional_light.rs

1//! A light that shines from a direction
2//! 
3//! TODO
4//! 
5use {
6    crate::{
7        registration::{
8            relation::{
9                ParentWrapper, 
10                Parent, Child
11            }, 
12            named::Named,
13            id::ID
14        },
15        scripting::{
16            Script,
17            executor::Spawner,
18            globals::{Global, EngineGlobals}, 
19            Scriptable
20        }, // TODO: import in proc-macro
21        graphics::{ 
22            Drawable, 
23            draw_pass_manager::DrawPassManager,
24            lighting_pass_manager::LightingPassManager,
25            graphics_system::{GraphicsSystem, GraphicsSystemTrait}, 
26            pass_builder::PassBuilder
27        },
28        shaders::{
29            fs_lighting_directional, 
30            vs_lighting
31        }, 
32        components::{
33            triangle_mesh::TriangleMesh,
34            RGB, 
35            ScreenPos
36        },
37        event::UserEvent
38    },
39    vulkano::{
40        buffer::{
41            BufferUsage,
42            CpuAccessibleBuffer,
43        }, 
44        command_buffer::{
45            AutoCommandBuffer, 
46            AutoCommandBufferBuilder,
47            DynamicState
48        }, 
49        device::Queue,
50        descriptor::{
51            descriptor_set::PersistentDescriptorSet,
52        },
53        framebuffer::{
54            RenderPassAbstract, 
55            Subpass
56        },
57        image::ImageViewAbstract,
58        
59        pipeline::{
60            GraphicsPipeline, 
61            GraphicsPipelineAbstract, 
62            blend::{
63                AttachmentBlend, 
64                BlendFactor, 
65                BlendOp
66            }, 
67            viewport::Viewport
68        },
69    },
70    feo_math::{
71        linear_algebra::{ 
72            vector3::Vector3,
73        }, 
74        rotation::quaternion::Quaternion, 
75        utils::space::Space
76    },
77    winit::event::Event,
78    std::{
79        sync::{
80            Arc, 
81            RwLock
82        },
83        any::Any,
84        mem
85    },
86    super::{
87        super::{
88            GameObject,
89            camera::Camera,
90        },
91        Light
92    }
93};
94
95/// Allows applying a directional light source to a scene.
96#[derive(GameObject, Drawable, Parent, Child, Named, Scriptable)]
97#[light] 
98pub struct DirectionalLight {
99    name: String,
100    id: ID,
101
102    pub subspace: Space,
103
104    intensity: f32,
105    color: RGB,
106
107    parent: ParentWrapper,
108    children: Vec<Arc<RwLock<dyn GameObject>>>,
109
110    script: Option<Box<Script<Self>>>,
111}
112
113impl std::fmt::Debug for DirectionalLight {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        f.debug_struct("DirectionalLight")
116            .field("name", &self.name)
117            .field("id", &self.id)
118            .field("subspace", &self.subspace)
119            .field("intensity", &self.intensity)
120            .field("color", &self.color)
121            .field("parent", &self.parent)
122            .field("children", &self.children)
123            .field("script", &self.script).finish()
124    }
125}
126
127impl Clone for DirectionalLight {
128    fn clone(&self) -> Self {
129        
130        let id = self.id.get_system().take();
131        DirectionalLight{
132            id,
133            name: self.name.clone(),
134            parent: self.parent.clone(),
135            subspace: self.subspace,
136            script: self.script.clone(),
137            children: self.children.clone().into_iter().map(|_child| {
138                // Dangerous
139                todo!();
140            }).collect::<Vec<Arc<RwLock<dyn GameObject>>>>(),
141            intensity: self.intensity,
142            color: self.color,
143        }
144    }
145}
146
147impl DirectionalLight {
148    /// Initializes the directional lighting system.
149    #[allow(clippy::too_many_arguments)]
150    pub fn new(
151        name: Option<&str>,
152        parent: Option<Arc<RwLock<dyn GameObject>>>,
153        intensity: f32,
154        color: RGB,
155        
156        position: Option<Vector3<f32>>, // no effect on actual lighting
157        rotation: Option<Quaternion<f32>>,
158        scale_factor: Option<Vector3<f32>>, // no effect on actual lighting
159        
160        script: Option<Box<Script<Self>>>,
161
162        engine_globals: EngineGlobals) -> Arc<RwLock<DirectionalLight>> {
163        let id = engine_globals.id_system.take();
164        Arc::new(RwLock::new( DirectionalLight {
165            name: match name {
166                Some(name) => name.to_string(),
167                None => String::from("directional_light_") + id.to_string().as_str()
168            },
169            id,
170
171            intensity,
172            color,
173            subspace: Space::new(position, rotation, scale_factor),
174
175            parent: match parent{
176                Some(game_object) => ParentWrapper::GameObject(game_object), 
177                None => ParentWrapper::Scene(engine_globals.scene),
178            },
179            children: Vec::new(),
180
181            script,
182        }))
183    }
184    
185    fn draw<C, N>(
186        &self, 
187        graphics_system: GraphicsSystem, 
188        viewport_dimensions: [u32; 2],
189        normals_input: N,
190        diffuse_input: C) -> AutoCommandBuffer
191    where
192        C: ImageViewAbstract + Send + Sync + 'static,
193        N: ImageViewAbstract + Send + Sync + 'static,
194    {
195        let push_constants = fs_lighting_directional::ty::PushConstants {
196            color: [self.color.r, self.color.g, self.color.b, 1.0],
197            direction: self.get_subspace().rotation.into(), // TODO: parse quaternion at other end
198        };
199
200        let layout = graphics_system.pipeline.descriptor_set_layout(0).unwrap();
201
202        let descriptor_set = PersistentDescriptorSet::start(layout.clone())
203            .add_image(normals_input).unwrap()
204            .add_image(diffuse_input).unwrap()
205            .build().unwrap();
206
207        let dynamic_state = DynamicState {
208            viewports: Some(vec![Viewport {
209                origin: [0.0, 0.0],
210                dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
211                depth_range: 0.0..1.0,
212            }]),
213            ..DynamicState::none()
214        };
215
216        let mut builder = AutoCommandBufferBuilder::secondary_graphics(
217            graphics_system.gfx_queue.device().clone(),
218            graphics_system.gfx_queue.family(),
219            graphics_system.pipeline.clone().subpass().clone(),
220        ).unwrap();
221
222        builder.draw(
223            graphics_system.pipeline.clone(),
224            &dynamic_state,
225            vec![graphics_system.vertex_buffer.clone()],
226            descriptor_set,
227            push_constants,
228            vec![],
229        ).unwrap();
230        
231        builder.build().unwrap()
232    }
233}
234
235impl Light for DirectionalLight {
236    fn as_any(&self) -> &dyn Any { self }
237    fn as_gameobject(&self) -> &dyn GameObject { self }
238    fn cast_gameobject_arc_rwlock(&self, this: Arc<RwLock<dyn Light>>) -> Arc<RwLock<dyn GameObject>> {
239        let this= Arc::into_raw(this).cast::<RwLock<Self>>();
240        let this = unsafe { Arc::from_raw(this) };
241        this as Arc<RwLock<dyn GameObject>>
242    }
243}
244
245impl GraphicsSystemTrait for DirectionalLight{
246    fn new_system<L>(gfx_queue: Arc<Queue>, subpass: Subpass<L>) -> crate::graphics::graphics_system::GraphicsSystem
247    where L: RenderPassAbstract + Sync + Send + 'static {
248        let vertex_buffer = {
249            CpuAccessibleBuffer::from_iter(
250                gfx_queue.device().clone(),
251                BufferUsage::all(),
252                false,
253                [
254                    ScreenPos {
255                        position: [-1.0, -1.0],
256                    },
257                    ScreenPos {
258                        position: [-1.0, 3.0],
259                    },
260                    ScreenPos {
261                        position: [3.0, -1.0],
262                    },
263                ].iter().cloned(),
264            ).expect("failed to create buffer")
265        };
266
267        let pipeline = {
268            let vs = vs_lighting::Shader::load(gfx_queue.device().clone())
269                .expect("failed to create shader");
270            let fs = fs_lighting_directional::Shader::load(gfx_queue.device().clone())
271                .expect("failed to create shader");
272
273            Arc::new(
274                GraphicsPipeline::start()
275                    .vertex_input_single_buffer::<ScreenPos>()
276                    .vertex_shader(vs.main_entry_point(), ())
277                    .triangle_list()
278                    .viewports_dynamic_scissors_irrelevant(1)
279                    .fragment_shader(fs.main_entry_point(), ())
280                    .blend_collective(AttachmentBlend {
281                        enabled: true,
282                        color_op: BlendOp::Add,
283                        color_source: BlendFactor::One,
284                        color_destination: BlendFactor::One,
285                        alpha_op: BlendOp::Max,
286                        alpha_source: BlendFactor::One,
287                        alpha_destination: BlendFactor::One,
288                        mask_red: true,
289                        mask_green: true,
290                        mask_blue: true,
291                        mask_alpha: true,
292                    })
293                    .render_pass(subpass)
294                    .build(gfx_queue.device().clone()).unwrap(),
295            ) as Arc<_>
296        };
297
298        GraphicsSystem {
299            gfx_queue,
300            vertex_buffer,
301            pipeline,
302        }
303    }
304
305    fn get_system_num(&self) -> usize { 1 }
306
307    fn pass<'b, 'p : 'b>(&self, pass_builder: &'b mut PassBuilder<'p>, gfx_system: GraphicsSystem){
308        let dims = pass_builder.framebuffer.dimensions();
309
310        let command_buffer = self.draw(
311            gfx_system,
312            [dims[0], dims[1]],
313            pass_builder.system.normals_buffer.clone(),
314            pass_builder.system.diffuse_buffer.clone()
315        );
316
317        pass_builder.command_buffer_builder
318            .as_mut().unwrap()
319            .execute_commands(command_buffer).unwrap();
320    }
321}