//! [Legion](https://github.com/amethyst/legion) integration. use crate::camera::Camera; use crate::pass::{self, debug, mesh}; use crate::scene; use crate::viewport::{Viewport, ViewportInfo}; use crate::Renderer; use legion::systems::Builder; use legion::world::SubWorld; use legion::*; use rayon::prelude::*; use std::sync::Arc; /// Callbacks trait for acquiring renderer input dynamically. pub trait RenderCallbacks: 'static + Send + Sync { fn get_viewports(&mut self) -> Vec<(&dyn Viewport, Camera)>; fn present(&mut self); } /// Initializes the Cyborg renderer within a Legion world. /// /// Uses the provided [Builder] to run all rendering code. /// /// Required pre-initialized resources: /// - [Renderer] /// - [ViewportInfo] (TODO: dynamic viewport targeting) /// - [mesh::ShaderInfo] pub fn build_renderer( callbacks: T, resources: &mut Resources, builder: &mut Builder, ) { puffin::profile_function!(); let renderer = resources.get_mut::().unwrap(); let viewport_info = resources.get::().unwrap(); let mesh_shaders = resources.get::().unwrap(); let device = renderer.get_device(); let layouts = renderer.get_layouts(); let mesh_pass = mesh::MeshPass::new( device.to_owned(), layouts.to_owned(), viewport_info.to_owned(), mesh_shaders.to_owned(), ); let debug_pass = debug::DebugPass::new( device.to_owned(), layouts.to_owned(), viewport_info.to_owned(), ); let frames_in_flight = renderer.get_frames_in_flight(); let mesh_pass = pass::RenderPassBox::new(Arc::new(mesh_pass), frames_in_flight); let debug_pass = pass::RenderPassBox::new(Arc::new(debug_pass), frames_in_flight); // drop borrowed resources so that new ones can be inserted drop(renderer); drop(viewport_info); drop(mesh_shaders); resources.insert(mesh_pass); resources.insert(debug_pass); resources.insert(callbacks); builder.add_system(draw_transformed_meshes_system()); builder.add_system(draw_debug_system()); builder.add_system(draw_debug_transformed_system()); builder.add_system(render_system::()); builder.add_system(present_system::()); } #[system] fn render( #[resource] callbacks: &mut T, #[resource] renderer: &mut Renderer, #[resource] mesh_pass: &mut pass::RenderPassBox, #[resource] debug_pass: &mut pass::RenderPassBox, ) { puffin::profile_function!(); let viewports = { puffin::profile_scope!("RenderCallbacks::get_viewports()"); callbacks.get_viewports() }; if viewports.len() > 1 { eprintln!("Cyborg does not currently support more than one viewport at a time!"); return; } else if let Some((viewport, camera)) = viewports.get(0) { let mut passes: Vec<&mut dyn pass::RenderPassBoxTrait> = Vec::new(); passes.push(mesh_pass); passes.push(debug_pass); renderer.render(passes.as_mut_slice(), *viewport, camera); } } #[system] fn present(#[resource] callbacks: &mut T) { puffin::profile_function!(); callbacks.present(); } #[system] fn draw_transformed_meshes( #[resource] mesh_pass: &pass::RenderPassBox, world: &SubWorld, query: &mut Query<(&scene::Transform, &scene::Mesh)>, ) { query.par_iter_chunks(world).for_each(|mesh_chunk| { puffin::profile_function!(); let meshes: Vec<_> = mesh_chunk .into_iter() .map(|(transform, mesh)| mesh::TransformedMesh { transform: transform.transform, mesh: mesh.mesh, }) .collect(); mesh_pass.add_transformed_meshes(&meshes); }); } #[system(for_each)] #[filter(!component::())] fn draw_debug( #[resource] debug_pass: &pass::RenderPassBox, draw_list: &scene::DebugDrawList, ) { puffin::profile_function!(); debug_pass.add_draw_list(draw_list); } #[system] fn draw_debug_transformed( #[resource] debug_pass: &pass::RenderPassBox, world: &SubWorld, query: &mut Query<(&scene::DebugDrawList, &scene::Transform)>, ) { puffin::profile_function!(); query.par_iter_chunks(world).for_each(|draw_list_chunk| { let mut sub_list = scene::DebugDrawList::default(); for (draw_list, transform) in draw_list_chunk { let vertices_start = sub_list.vertices.len(); sub_list.merge(draw_list); for vertex in sub_list.vertices[vertices_start..].iter_mut() { let position = glam::Vec3::from_slice(&vertex.position); let transformed = transform.transform.transform_point3(position); vertex.position = transformed.to_array(); } } debug_pass.add_draw_list(&sub_list); }); }