cyborg/src/legion.rs

163 lines
4.9 KiB
Rust

//! [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<T: RenderCallbacks>(
callbacks: T,
resources: &mut Resources,
builder: &mut Builder,
) {
puffin::profile_function!();
let renderer = resources.get_mut::<Renderer>().unwrap();
let viewport_info = resources.get::<ViewportInfo>().unwrap();
let mesh_shaders = resources.get::<mesh::ShaderInfo>().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::<T>());
builder.add_system(present_system::<T>());
}
#[system]
fn render<T: RenderCallbacks>(
#[resource] callbacks: &mut T,
#[resource] renderer: &mut Renderer,
#[resource] mesh_pass: &mut pass::RenderPassBox<mesh::MeshPass>,
#[resource] debug_pass: &mut pass::RenderPassBox<debug::DebugPass>,
) {
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<T: RenderCallbacks>(#[resource] callbacks: &mut T) {
puffin::profile_function!();
callbacks.present();
}
#[system]
fn draw_transformed_meshes(
#[resource] mesh_pass: &pass::RenderPassBox<mesh::MeshPass>,
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::<scene::Transform>())]
fn draw_debug(
#[resource] debug_pass: &pass::RenderPassBox<debug::DebugPass>,
draw_list: &scene::DebugDrawList,
) {
puffin::profile_function!();
debug_pass.add_draw_list(draw_list);
}
#[system]
fn draw_debug_transformed(
#[resource] debug_pass: &pass::RenderPassBox<debug::DebugPass>,
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);
});
}