163 lines
4.9 KiB
Rust
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);
|
|
});
|
|
}
|