feo_oop_engine/scene/game_object/light/
point_light.rs

1//! A light that shines from a point
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        }, // 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_point, 
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        pipeline::{
59            GraphicsPipeline, 
60            GraphicsPipelineAbstract, 
61            blend::{
62                AttachmentBlend, 
63                BlendFactor, 
64                BlendOp
65            }, 
66            viewport::Viewport
67        },
68    },
69    feo_math::{
70        linear_algebra::{
71            matrix4::Matrix4, 
72            vector3::Vector3, 
73            vector4::Vector4
74        }, 
75        rotation::quaternion::Quaternion, 
76        utils::space::Space
77    },
78    winit::event::Event,
79    std::{
80        sync::{
81            Arc, 
82            RwLock
83        },
84        any::Any,
85        mem
86    },
87    super::{
88        super::{
89            GameObject,
90            camera::Camera,
91        },
92        Light
93    }
94};
95
96/// Allows applying a directional light source to a scene.
97#[derive(GameObject, Drawable, Parent, Child, Named, Scriptable)]
98#[light]
99pub struct PointLight {
100    name: String,
101    id: ID,
102
103    pub subspace: Space,
104
105    intensity: f32,
106    color: RGB,
107
108    parent: ParentWrapper,
109    children: Vec<Arc<RwLock<dyn GameObject>>>,
110
111    script: Option<Box<Script<Self>>>,
112}
113
114impl std::fmt::Debug for PointLight {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        f.debug_struct("PointLight")
117            .field("name", &self.name)
118            .field("id", &self.id)
119            .field("subspace", &self.subspace)
120            .field("intensity", &self.intensity)
121            .field("color", &self.color)
122            .field("parent", &self.parent)
123            .field("children", &self.children)
124            .field("script", &self.script).finish()
125    }
126}
127
128impl Clone for PointLight {
129    fn clone(&self) -> Self {
130        let id = self.id.get_system().take();
131        PointLight{
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 PointLight {
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<PointLight>> {
163        let id = engine_globals.id_system.take();
164        Arc::new( RwLock::new( PointLight {
165            name: match name {
166                Some(name) => name.to_string(),
167                None => String::from("point_light_") + id.to_string().as_str()
168            },
169            id, // TODO make id removable
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    #[allow(clippy::too_many_arguments)]
186    pub fn draw<N, C, S, D>(
187        &self,
188        graphics_system: GraphicsSystem, 
189        viewport_dimensions: [u32; 2],
190        normals_input: N,
191        diffuse_input: C,
192        specular_input: S,
193        depth_input: D,
194        screen_to_camera: Matrix4<f32>,
195        to_camera_space: Space
196    ) -> AutoCommandBuffer
197    where
198        N: ImageViewAbstract + Send + Sync + 'static,
199        C: ImageViewAbstract + Send + Sync + 'static,
200        S: ImageViewAbstract + Send + Sync + 'static,
201        D: ImageViewAbstract + Send + Sync + 'static,
202    {
203        let position_camera_space = Vector4::from_vector3(
204            (to_camera_space.build() * Vector4::from_vector3(self.get_subspace().center, 1.0)).into(), // move item into space
205            0.0
206        ).into();
207
208        let push_constants = fs_lighting_point::ty::PushConstants {
209            screen_to_camera: screen_to_camera.transpose().into(),
210            color: [self.color.r, self.color.g, self.color.b, 1.0],
211            position_camera_space, // note scale position
212        };
213
214        let layout = graphics_system.pipeline.descriptor_set_layout(0).unwrap();
215
216        let descriptor_set = PersistentDescriptorSet::start(layout.clone())
217            .add_image(diffuse_input).unwrap()
218            .add_image(specular_input).unwrap()
219            .add_image(normals_input).unwrap()
220            .add_image(depth_input).unwrap()
221            .build().unwrap();
222
223        let dynamic_state = DynamicState {
224            viewports: Some(vec![Viewport {
225                origin: [0.0, 0.0],
226                dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
227                depth_range: 0.0..1.0,
228            }]),
229            ..DynamicState::none()
230        };
231
232        let mut builder = AutoCommandBufferBuilder::secondary_graphics(
233            graphics_system.gfx_queue.device().clone(),
234            graphics_system.gfx_queue.family(),
235            graphics_system.pipeline.clone().subpass().clone(),
236        ).unwrap();
237
238        builder.draw(
239            graphics_system.pipeline.clone(),
240            &dynamic_state,
241            vec![graphics_system.vertex_buffer.clone()],
242            descriptor_set,
243            push_constants,
244            vec![],
245        ).unwrap();
246        
247        builder.build().unwrap()
248    }
249}
250
251impl Light for PointLight {
252    fn as_any(&self) -> &dyn Any { self }
253
254    fn as_gameobject(&self) -> &dyn GameObject { self }
255
256    fn cast_gameobject_arc_rwlock(&self, this: Arc<RwLock<dyn Light>>) -> Arc<RwLock<dyn GameObject>> {
257        let this= Arc::into_raw(this).cast::<RwLock<Self>>();
258        let this = unsafe { Arc::from_raw(this) };
259        this as Arc<RwLock<dyn GameObject>>
260    }
261
262}
263
264impl GraphicsSystemTrait for PointLight {
265
266    fn new_system<L>(gfx_queue: Arc<Queue>, subpass: Subpass<L>) -> crate::graphics::graphics_system::GraphicsSystem
267    where L: RenderPassAbstract + Sync + Send + 'static {
268        let vertex_buffer = {
269            CpuAccessibleBuffer::from_iter(
270                gfx_queue.device().clone(),
271                BufferUsage::all(),
272                false,
273                [
274                    ScreenPos {
275                        position: [-1.0, -1.0],
276                    },
277                    ScreenPos {
278                        position: [-1.0, 3.0],
279                    },
280                    ScreenPos {
281                        position: [3.0, -1.0],
282                    },
283                ].iter().cloned(),
284            ).expect("failed to create buffer")
285        };
286
287        let pipeline = {
288            let vs = vs_lighting::Shader::load(gfx_queue.device().clone())
289                .expect("failed to create shader");
290            let fs = fs_lighting_point::Shader::load(gfx_queue.device().clone())
291                .expect("failed to create shader");
292
293            Arc::new(
294                GraphicsPipeline::start()
295                    .vertex_input_single_buffer::<ScreenPos>()
296                    .vertex_shader(vs.main_entry_point(), ())
297                    .triangle_list()
298                    .viewports_dynamic_scissors_irrelevant(1)
299                    .fragment_shader(fs.main_entry_point(), ())
300                    .blend_collective(AttachmentBlend {
301                        enabled: true,
302                        color_op: BlendOp::Add,
303                        color_source: BlendFactor::One,
304                        color_destination: BlendFactor::One,
305                        alpha_op: BlendOp::Max,
306                        alpha_source: BlendFactor::One,
307                        alpha_destination: BlendFactor::One,
308                        mask_red: true,
309                        mask_green: true,
310                        mask_blue: true,
311                        mask_alpha: true,
312                    })
313                    .render_pass(subpass)
314                    .build(gfx_queue.device().clone()).unwrap(),
315            ) as Arc<_>
316        };
317
318        GraphicsSystem {
319            gfx_queue,
320            vertex_buffer,
321            pipeline,
322        }
323    }
324
325    fn get_system_num(&self) -> usize { 2 }
326
327    fn pass<'b, 'p : 'b>(&self, pass_builder: &'b mut PassBuilder<'p>, gfx_system: GraphicsSystem){
328        let dims = pass_builder.framebuffer.dimensions();
329
330        let command_buffer = self.draw(
331            gfx_system,
332            [dims[0], dims[1]],
333            pass_builder.system.normals_buffer.clone(),
334            pass_builder.system.diffuse_buffer.clone(),
335            pass_builder.system.specular_buffer.clone(),
336            pass_builder.system.specular_buffer.clone(),
337            pass_builder.screen_to_camera,
338            pass_builder.to_camera_space
339        );
340
341        pass_builder.command_buffer_builder
342            .as_mut().unwrap()
343            .execute_commands(command_buffer).unwrap();
344    }
345}