use crate::{
camera::{ActiveCameras, Camera},
render_graph::{CommandQueue, Node, ResourceSlots, SystemNode},
renderer::{
BufferId, BufferInfo, BufferUsage, RenderContext, RenderResourceBinding,
RenderResourceBindings, RenderResourceContext,
},
};
use bevy_core::AsBytes;
use bevy_ecs::{Commands, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World};
use bevy_transform::prelude::*;
use std::borrow::Cow;
pub struct CameraNode {
command_queue: CommandQueue,
camera_name: Cow<'static, str>,
}
impl CameraNode {
pub fn new<T>(camera_name: T) -> Self
where
T: Into<Cow<'static, str>>,
{
CameraNode {
command_queue: Default::default(),
camera_name: camera_name.into(),
}
}
}
impl Node for CameraNode {
fn update(
&mut self,
_world: &World,
_resources: &Resources,
render_context: &mut dyn RenderContext,
_input: &ResourceSlots,
_output: &mut ResourceSlots,
) {
self.command_queue.execute(render_context);
}
}
impl SystemNode for CameraNode {
fn get_system(&self, commands: &mut Commands) -> Box<dyn System> {
let system = camera_node_system.system();
commands.insert_local_resource(
system.id(),
CameraNodeState {
camera_name: self.camera_name.clone(),
command_queue: self.command_queue.clone(),
camera_buffer: None,
staging_buffer: None,
},
);
system
}
}
#[derive(Default)]
pub struct CameraNodeState {
command_queue: CommandQueue,
camera_name: Cow<'static, str>,
camera_buffer: Option<BufferId>,
staging_buffer: Option<BufferId>,
}
pub fn camera_node_system(
mut state: Local<CameraNodeState>,
active_cameras: Res<ActiveCameras>,
render_resource_context: Res<Box<dyn RenderResourceContext>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Camera, &Transform)>,
) {
let render_resource_context = &**render_resource_context;
let (camera, transform) = if let Some(camera_entity) = active_cameras.get(&state.camera_name) {
(
query.get::<Camera>(camera_entity).unwrap(),
query.get::<Transform>(camera_entity).unwrap(),
)
} else {
return;
};
let staging_buffer = if let Some(staging_buffer) = state.staging_buffer {
render_resource_context.map_buffer(staging_buffer);
staging_buffer
} else {
let size = std::mem::size_of::<[[f32; 4]; 4]>();
let buffer = render_resource_context.create_buffer(BufferInfo {
size,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
..Default::default()
});
render_resource_bindings.set(
&state.camera_name,
RenderResourceBinding::Buffer {
buffer,
range: 0..size as u64,
dynamic_index: None,
},
);
state.camera_buffer = Some(buffer);
let staging_buffer = render_resource_context.create_buffer(BufferInfo {
size,
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
mapped_at_creation: true,
});
state.staging_buffer = Some(staging_buffer);
staging_buffer
};
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
let camera_matrix: [f32; 16] =
(camera.projection_matrix * transform.value.inverse()).to_cols_array();
render_resource_context.write_mapped_buffer(
staging_buffer,
0..matrix_size as u64,
&mut |data, _renderer| {
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
},
);
render_resource_context.unmap_buffer(staging_buffer);
let camera_buffer = state.camera_buffer.unwrap();
state.command_queue.copy_buffer_to_buffer(
staging_buffer,
0,
camera_buffer,
0,
matrix_size as u64,
);
}