use crate::ecs::world::CORE;
use crate::ecs::world::World;
use crate::render::wgpu::render_configs::{
CameraFrameInputs, CameraMatricesInputs, CameraProjectionParams, FrameOutputs,
LinesFrameInputs, RenderInputs, RendererCommand, UiFrameInputs, ViewInputs,
};
use crate::render_driver::{render_entity, scene_entity};
fn snapshot_camera(world: &World, entity: freecs::Entity) -> CameraFrameInputs {
let matrices =
crate::ecs::camera::queries::query_camera_matrices(world, entity).map(|matrices| {
CameraMatricesInputs {
view: matrices.view,
projection: matrices.projection,
camera_position: matrices.camera_position,
}
});
let projection = match world
.get::<crate::ecs::camera::components::Camera>(entity)
.map(|c| &c.projection)
{
Some(crate::ecs::camera::components::Projection::Perspective(persp)) => {
CameraProjectionParams {
z_near: persp.z_near,
z_far: persp.z_far.unwrap_or(1000.0),
y_fov_rad: persp.y_fov_rad,
aspect: persp.aspect_ratio.unwrap_or_else(|| {
crate::ecs::camera::queries::query_window_aspect_ratio(world).unwrap_or(1.78)
}),
orthographic: None,
}
}
Some(crate::ecs::camera::components::Projection::Orthographic(ortho)) => {
CameraProjectionParams {
z_near: ortho.z_near,
z_far: ortho.z_far,
y_fov_rad: std::f32::consts::FRAC_PI_4,
aspect: 1.78,
orthographic: Some((ortho.x_mag, ortho.y_mag)),
}
}
None => CameraProjectionParams::default(),
};
let shading = world
.get::<crate::ecs::camera::components::ViewportShading>(entity)
.copied()
.unwrap_or_default();
let post_process = world
.get::<crate::ecs::camera::components::CameraPostProcess>(entity)
.copied();
let culling_mask = world
.get::<crate::ecs::primitives::CameraCullingMask>(entity)
.copied();
let environment = world
.get::<crate::ecs::camera::components::CameraEnvironment>(entity)
.copied();
let effective_shading = crate::ecs::camera::components::effective_shading_for_camera(
&world.resources.render_settings,
&world.resources.debug_draw,
&world.resources.selection,
&shading,
post_process.as_ref(),
culling_mask.as_ref(),
environment.as_ref(),
);
CameraFrameInputs {
entity: render_entity(entity),
matrices,
projection,
constrained_aspect: world
.get::<crate::ecs::camera::components::ConstrainedAspect>(entity)
.map(|constrained| constrained.0),
effective_shading,
update_mode: world
.get::<crate::render::config::ViewportUpdateMode>(entity)
.copied()
.unwrap_or_default(),
world_transform: world
.get::<crate::ecs::transform::components::GlobalTransform>(entity)
.map(|gt| gt.0),
}
}
fn map_render_commands(world: &mut World) -> Vec<RendererCommand> {
std::mem::take(&mut world.resources.commands.render)
.into_iter()
.filter_map(|command| match command {
crate::ecs::world::RenderCommand::UploadUiImageLayer {
layer,
rgba_data,
width,
height,
} => Some(RendererCommand::UploadUiImageLayer {
layer,
rgba_data,
width,
height,
}),
crate::ecs::world::RenderCommand::ReloadTexture {
name,
rgba_data,
width,
height,
} => Some(RendererCommand::ReloadTexture {
name,
rgba_data,
width,
height,
}),
crate::ecs::world::RenderCommand::LoadHdrSkybox { hdr_data } => {
Some(RendererCommand::LoadHdrSkybox { hdr_data })
}
crate::ecs::world::RenderCommand::SetColorLut { data } => {
Some(RendererCommand::SetColorLut { data })
}
crate::ecs::world::RenderCommand::LoadHdrSkyboxFromPath { path } => {
match std::fs::read(&path) {
Ok(bytes) => {
tracing::info!("Successfully read HDR file: {}", path.display());
Some(RendererCommand::LoadHdrSkybox { hdr_data: bytes })
}
Err(error) => {
tracing::error!("Failed to read HDR file {}: {}", path.display(), error);
None
}
}
}
crate::ecs::world::RenderCommand::CaptureScreenshot {
path,
max_dimension,
} => Some(RendererCommand::CaptureScreenshot {
path,
max_dimension,
}),
})
.collect()
}
pub fn compose_render_inputs(world: &mut World) -> RenderInputs {
let commands = map_render_commands(world);
let cameras: Vec<CameraFrameInputs> = world
.resources
.user_interface
.required_cameras
.iter()
.map(|&entity| snapshot_camera(world, entity))
.collect();
let active_camera_frame = world
.resources
.active_camera
.map(|entity| snapshot_camera(world, entity));
let fallback_shading = crate::ecs::camera::components::effective_shading_from_graphics(
&world.resources.render_settings,
&world.resources.debug_draw,
&world.resources.selection,
);
let mut wants_normals = world.resources.debug_draw.show_normals;
let mut wants_bounds = world.resources.debug_draw.show_bounding_volumes;
let mut wants_wireframe = false;
for camera in cameras.iter().chain(active_camera_frame.iter()) {
wants_normals |= camera.effective_shading.show_normals;
wants_bounds |= camera.effective_shading.show_bounding_volumes;
wants_wireframe |= camera.effective_shading.show_wireframe;
}
let debug_lines = LinesFrameInputs {
lines: super::lines::gather_lines_data(world, wants_wireframe),
bounding_volumes: super::lines::gather_bounding_volume_data(world, wants_bounds),
normals: super::lines::gather_normal_data(world, wants_normals),
};
let pick_request = world
.resources
.gpu_picking
.take_pending_request()
.map(|request| (request.screen_x, request.screen_y));
let has_skinned_meshes = world.ecs.worlds[CORE]
.query_entities(crate::ecs::world::SKIN)
.next()
.is_some();
let dirty_world_spheres: Vec<(nalgebra_glm::Vec3, f32)> = {
let scene = &world.resources.renderer_state;
let mut spheres = Vec::new();
for entity in world
.resources
.mesh_render_state
.dirty_entities_for_culling()
{
let Some(dynamic) = scene.render_dynamic_objects.get(&entity) else {
continue;
};
let Some(bounding_volume) = scene.render_bounds.get(&entity) else {
continue;
};
let global_transform = dynamic.transform;
let world_center = global_transform
* nalgebra_glm::Vec4::new(
bounding_volume.center.x,
bounding_volume.center.y,
bounding_volume.center.z,
1.0,
);
let world_center =
nalgebra_glm::Vec3::new(world_center.x, world_center.y, world_center.z);
let scale = nalgebra_glm::length(&nalgebra_glm::Vec3::new(
global_transform[(0, 0)],
global_transform[(1, 0)],
global_transform[(2, 0)],
));
let world_radius = bounding_volume.sphere_radius * scale;
spheres.push((world_center, world_radius));
}
spheres
};
let global_dirty_signal = world
.resources
.mesh_render_state
.requires_full_invalidation();
let mesh_frame_state = Some(world.resources.mesh_render_state.take_frame_state());
let mouse_position = world.resources.input.mouse.position;
let force_render_cameras = world
.resources
.window
.force_render_cameras
.iter()
.copied()
.map(render_entity)
.collect();
let resources = &mut world.resources;
let retained = &mut resources.retained_ui;
RenderInputs {
settings: std::mem::take(&mut resources.render_settings),
debug_draw: std::mem::take(&mut resources.debug_draw),
scene: std::mem::take(&mut resources.renderer_state),
ibl_views: Default::default(),
shadow_atlas: Default::default(),
wind: resources.wind,
mesh_cache: std::mem::take(&mut resources.assets.mesh_cache),
texture_cache: std::mem::take(&mut resources.texture_cache),
pending_particle_textures: std::mem::take(&mut resources.loading.pending_particle_textures),
ui: UiFrameInputs {
rects: std::mem::take(&mut retained.frame.rects),
rect_entities: std::mem::take(&mut retained.frame.rect_entities)
.into_iter()
.map(|entity| entity.map(render_entity))
.collect(),
images: std::mem::take(&mut retained.frame.ui_images),
text_meshes: std::mem::take(&mut retained.frame.text_meshes),
render_slots: std::mem::take(&mut retained.render_slots),
background_color: retained.background_color,
},
view: ViewInputs {
active_camera: resources.active_camera.map(render_entity),
uptime_milliseconds: resources.window.timing.uptime_milliseconds,
delta_time: resources.window.timing.delta_time,
viewport_size: resources.window.cached_viewport_size,
camera_tile_rects: resources
.window
.camera_tile_rects
.iter()
.map(|(entity, rect)| (render_entity(*entity), *rect))
.collect(),
camera_tile_render_iteration: resources.window.camera_tile_render_iteration,
active_viewport_rect: resources.window.active_viewport_rect,
},
world_id: resources.world_id,
frame: crate::render::wgpu::render_configs::FrameInputs {
commands,
cameras,
active_camera_frame,
fallback_shading,
pick_request,
has_skinned_meshes,
mesh_frame_state,
dirty_world_spheres,
global_dirty_signal,
mouse_position,
force_render_cameras,
debug_lines,
},
#[cfg(feature = "grass")]
grass: std::mem::take(&mut resources.grass),
#[cfg(feature = "terrain")]
terrain: std::mem::take(&mut resources.terrain),
#[cfg(feature = "terrain")]
terrain_render: std::mem::take(&mut resources.terrain_render),
}
}
pub fn restore_render_inputs(world: &mut World, inputs: RenderInputs, outputs: FrameOutputs) {
let resources = &mut world.resources;
resources.render_settings = inputs.settings;
resources.debug_draw = inputs.debug_draw;
resources.renderer_state = inputs.scene;
resources.texture_cache = inputs.texture_cache;
resources.assets.mesh_cache = inputs.mesh_cache;
let mut pending = inputs.pending_particle_textures;
pending.extend(std::mem::take(
&mut resources.loading.pending_particle_textures,
));
resources.loading.pending_particle_textures = pending;
let retained = &mut resources.retained_ui;
retained.frame.rects = inputs.ui.rects;
retained.frame.rect_entities = inputs
.ui
.rect_entities
.into_iter()
.map(|entity| entity.map(scene_entity))
.collect();
retained.frame.ui_images = inputs.ui.images;
retained.frame.text_meshes = inputs.ui.text_meshes;
retained.render_slots = inputs.ui.render_slots;
#[cfg(feature = "grass")]
{
resources.grass = inputs.grass;
}
#[cfg(feature = "terrain")]
{
resources.terrain = inputs.terrain;
resources.terrain_render = inputs.terrain_render;
}
if outputs.frame_executed {
resources.user_interface.viewport_texture_sizes = outputs.viewport_texture_sizes;
}
for entity in outputs.force_render_cleared {
resources
.window
.force_render_cameras
.remove(&scene_entity(entity));
}
if let Some(unconsumed) = inputs.frame.mesh_frame_state {
resources.mesh_render_state.restore_unconsumed(unconsumed);
}
if let Some((screen_x, screen_y)) = inputs.frame.pick_request
&& resources.gpu_picking.pending_request.is_none()
{
resources.gpu_picking.pending_request =
Some(crate::ecs::gpu_picking::GpuPickRequest { screen_x, screen_y });
}
}