diff --git a/editor/src/camera/mod.rs b/editor/src/camera/mod.rs index c2e00549e..d553fafea 100644 --- a/editor/src/camera/mod.rs +++ b/editor/src/camera/mod.rs @@ -32,7 +32,7 @@ use crate::{ graph::{BaseSceneGraph, SceneGraph, SceneGraphNode}, gui::message::{KeyCode, KeyboardModifiers, MouseButton}, renderer::{ - bundle::{ObserverInfo, RenderContext, RenderDataBundleStorage}, + bundle::{ObserverPosition, RenderContext, RenderDataBundleStorage}, DynamicSurfaceCache, }, scene::{ @@ -929,12 +929,12 @@ fn precise_ray_test( ignore_back_faces: bool, ) -> PreciseRayTestResult { let mut cache = DynamicSurfaceCache::new(); - let observer_info = ObserverInfo::from_camera(camera); - let mut bundle_storage = RenderDataBundleStorage::new_empty(observer_info.clone()); + let observer_position = ObserverPosition::from_camera(camera); + let mut bundle_storage = RenderDataBundleStorage::new_empty(observer_position.clone()); node.collect_render_data(&mut RenderContext { render_mask: BitMask::all(), elapsed_time: 0.0, - observer_info: &observer_info, + observer_position: &observer_position, frustum: Some(&camera.frustum()), storage: &mut bundle_storage, graph, diff --git a/editor/src/highlight.rs b/editor/src/highlight.rs index 1cda5d6d4..380bdaa27 100644 --- a/editor/src/highlight.rs +++ b/editor/src/highlight.rs @@ -24,7 +24,7 @@ use crate::{ fxhash::FxHashSet, graph::{BaseSceneGraph, SceneGraph}, renderer::{ - bundle::{BundleRenderContext, ObserverInfo, RenderContext, RenderDataBundleStorage}, + bundle::{BundleRenderContext, RenderContext, RenderDataBundleStorage}, cache::shader::{ binding, property, PropertyGroup, RenderMaterial, RenderPassContainer, }, @@ -127,23 +127,14 @@ impl SceneRenderPass for HighlightRenderPass { { let render_pass_name = ImmutableString::new("Forward"); - let observer_info = ObserverInfo { - observer_position: ctx.camera.global_position(), - z_near: ctx.camera.projection().z_near(), - z_far: ctx.camera.projection().z_far(), - view_matrix: ctx.camera.view_matrix(), - projection_matrix: ctx.camera.projection_matrix(), - }; - let mut render_bundle_storage = - RenderDataBundleStorage::new_empty(observer_info.clone()); + RenderDataBundleStorage::new_empty(ctx.observer.position.clone()); - let frustum = ctx.camera.frustum(); let mut render_context = RenderContext { - render_mask: *ctx.camera.render_mask, + render_mask: ctx.observer.render_mask, elapsed_time: ctx.elapsed_time, - observer_info: &observer_info, - frustum: Some(&frustum), + observer_position: &ctx.observer.position, + frustum: Some(&ctx.observer.frustum), storage: &mut render_bundle_storage, graph: &ctx.scene.graph, render_pass_name: &render_pass_name, @@ -160,8 +151,12 @@ impl SceneRenderPass for HighlightRenderPass { render_bundle_storage.sort(); - self.framebuffer - .clear(ctx.viewport, Some(Color::TRANSPARENT), Some(1.0), None); + self.framebuffer.clear( + ctx.observer.viewport, + Some(Color::TRANSPARENT), + Some(1.0), + None, + ); stats += render_bundle_storage.render_to_frame_buffer( ctx.server, @@ -178,7 +173,7 @@ impl SceneRenderPass for HighlightRenderPass { fallback_resources: ctx.fallback_resources, ambient_light: Default::default(), scene_depth: Some(ctx.depth_texture), - viewport: ctx.viewport, + viewport: ctx.observer.viewport, uniform_memory_allocator: ctx.uniform_memory_allocator, }, )?; @@ -186,7 +181,7 @@ impl SceneRenderPass for HighlightRenderPass { // Render full screen quad with edge detect shader to draw outline of selected objects. { - let frame_matrix = make_viewport_matrix(ctx.viewport); + let frame_matrix = make_viewport_matrix(ctx.observer.viewport); let frame_texture = &self.framebuffer.color_attachments()[0].texture; let color = Color::ORANGE.as_frgba(); @@ -207,7 +202,7 @@ impl SceneRenderPass for HighlightRenderPass { &ImmutableString::new("Primary"), ctx.framebuffer, &self.quad, - ctx.viewport, + ctx.observer.viewport, &material, ctx.uniform_buffer_cache, Default::default(), diff --git a/editor/src/overlay.rs b/editor/src/overlay.rs index 9945cd896..2efd83d71 100644 --- a/editor/src/overlay.rs +++ b/editor/src/overlay.rs @@ -94,8 +94,8 @@ impl SceneRenderPass for OverlayRenderPass { ctx: SceneRenderPassContext, ) -> Result { let mut stats = RenderPassStatistics::default(); - let view_projection = ctx.camera.view_projection_matrix(); - let inv_view = ctx.camera.inv_view_matrix().unwrap(); + let view_projection = ctx.observer.position.view_projection_matrix; + let inv_view = ctx.observer.position.view_matrix.try_inverse().unwrap(); let camera_up = -inv_view.up(); let camera_side = inv_view.side(); let sound_icon = ctx @@ -135,7 +135,7 @@ impl SceneRenderPass for OverlayRenderPass { &ImmutableString::new("Primary"), ctx.framebuffer, &self.quad, - ctx.viewport, + ctx.observer.viewport, &material, ctx.uniform_buffer_cache, Default::default(), diff --git a/fyrox-impl/src/renderer/bundle.rs b/fyrox-impl/src/renderer/bundle.rs index b8aeb8e45..cda32e607 100644 --- a/fyrox-impl/src/renderer/bundle.rs +++ b/fyrox-impl/src/renderer/bundle.rs @@ -23,7 +23,7 @@ #![allow(missing_docs)] // TODO use crate::material::Material; -use crate::scene::camera::Camera; +use crate::scene::camera::{Camera, ColorGradingLut, Exposure, Projection}; use crate::{ core::{ algebra::{Matrix4, Vector3, Vector4}, @@ -75,7 +75,7 @@ use crate::{ }, }; use fxhash::{FxBuildHasher, FxHashMap, FxHasher}; -use fyrox_core::algebra::Point3; +use fyrox_core::algebra::{Point3, Vector2}; use fyrox_graph::{SceneGraph, SceneGraphNode}; use fyrox_graphics::gpu_program::{SamplerFallback, ShaderResourceDefinition}; use std::{ @@ -83,30 +83,76 @@ use std::{ hash::{Hash, Hasher}, }; -/// Observer info contains all the data, that describes an observer. It could be a real camera, light source's -/// "virtual camera" that is used for shadow mapping, etc. +pub struct Observer { + pub handle: Handle, + pub render_target: Option, + pub position: ObserverPosition, + pub environment_map: Option, + pub skybox_map: Option, + pub render_mask: BitMask, + pub projection: Projection, + pub color_grading_lut: Option, + pub color_grading_enabled: bool, + pub exposure: Exposure, + pub viewport: Rect, + pub frustum: Frustum, +} + +impl Observer { + pub fn from_camera(camera: &Camera, mut frame_size: Vector2) -> Self { + let skybox_map = camera.skybox_ref().and_then(|s| s.cubemap.clone()); + if let Some(render_target) = camera.render_target() { + if let Some(size) = render_target + .data_ref() + .as_loaded_ref() + .and_then(|rt| rt.kind().rectangle_size().map(|size| size.cast::())) + { + frame_size = size; + } + } + Observer { + handle: camera.handle(), + environment_map: camera.environment_map().or_else(|| skybox_map.clone()), + skybox_map, + render_mask: *camera.render_mask, + projection: camera.projection().clone(), + position: ObserverPosition::from_camera(camera), + render_target: camera.render_target().cloned(), + color_grading_lut: camera.color_grading_lut(), + color_grading_enabled: camera.color_grading_enabled(), + exposure: camera.exposure(), + viewport: camera.viewport_pixels(frame_size), + frustum: camera.frustum(), + } + } +} + +/// Observer position contains all the data, that describes an observer position in 3D space. It +/// could be a real camera, light source's "virtual camera" that is used for shadow mapping, etc. #[derive(Clone, Default)] -pub struct ObserverInfo { +pub struct ObserverPosition { /// World-space position of the observer. - pub observer_position: Vector3, - /// Location of the near clipping plane. + pub translation: Vector3, + /// Position of the near clipping plane. pub z_near: f32, - /// Location of the far clipping plane. + /// Position of the far clipping plane. pub z_far: f32, - /// View matrix of the observer. + /// The view matrix of the observer. pub view_matrix: Matrix4, /// Projection matrix of the observer. pub projection_matrix: Matrix4, + pub view_projection_matrix: Matrix4, } -impl ObserverInfo { +impl ObserverPosition { pub fn from_camera(camera: &Camera) -> Self { - ObserverInfo { - observer_position: camera.global_position(), + Self { + translation: camera.global_position(), z_near: camera.projection().z_near(), z_far: camera.projection().z_far(), view_matrix: camera.view_matrix(), projection_matrix: camera.projection_matrix(), + view_projection_matrix: camera.view_projection_matrix(), } } } @@ -121,7 +167,7 @@ pub struct RenderContext<'a> { /// this value is **not** guaranteed to match real time. A user can change delta time with /// which the engine "ticks" and this delta time affects elapsed time. pub elapsed_time: f32, - pub observer_info: &'a ObserverInfo, + pub observer_position: &'a ObserverPosition, /// Frustum of the observer, it is built using observer's view and projection matrix. Use the frustum to do /// frustum culling. pub frustum: Option<&'a Frustum>, @@ -144,7 +190,7 @@ impl RenderContext<'_> { const RANGE_CENTER: u64 = u64::MAX / 2; const GRANULARITY: f32 = 1000.0; - let view_matrix = &self.observer_info.view_matrix; + let view_matrix = &self.observer_position.view_matrix; let world_space_point = Point3::from(global_position); let view_space_point = view_matrix.transform_point(&world_space_point); @@ -823,7 +869,8 @@ pub struct LightSource { /// rendering by reducing amount of state changes of OpenGL context. pub struct RenderDataBundleStorage { bundle_map: FxHashMap, - pub observer_info: ObserverInfo, + /// Position of an observer for which this data bundle was created. + pub observer_position: ObserverPosition, /// A sorted list of bundles. pub bundles: Vec, pub light_sources: Vec, @@ -842,10 +889,10 @@ impl Default for RenderDataBundleStorageOptions { } impl RenderDataBundleStorage { - pub fn new_empty(observer_info: ObserverInfo) -> Self { + pub fn new_empty(observer_position: ObserverPosition) -> Self { Self { bundle_map: Default::default(), - observer_info, + observer_position, bundles: Default::default(), light_sources: Default::default(), } @@ -858,7 +905,7 @@ impl RenderDataBundleStorage { graph: &Graph, render_mask: BitMask, elapsed_time: f32, - observer_info: ObserverInfo, + observer_position: &ObserverPosition, render_pass_name: ImmutableString, options: RenderDataBundleStorageOptions, dynamic_surface_cache: &mut DynamicSurfaceCache, @@ -867,13 +914,13 @@ impl RenderDataBundleStorage { let capacity = graph.node_count() as usize; let mut storage = Self { bundle_map: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher::default()), - observer_info: observer_info.clone(), + observer_position: observer_position.clone(), bundles: Vec::with_capacity(capacity), light_sources: Default::default(), }; let frustum = Frustum::from_view_projection_matrix( - observer_info.projection_matrix * observer_info.view_matrix, + observer_position.projection_matrix * observer_position.view_matrix, ) .unwrap_or_default(); @@ -883,11 +930,12 @@ impl RenderDataBundleStorage { for level in lod_group.levels.iter() { for &object in level.objects.iter() { if let Some(object_ref) = graph.try_get(object) { - let distance = observer_info - .observer_position + let distance = observer_position + .translation .metric_distance(&object_ref.global_position()); - let z_range = observer_info.z_far - observer_info.z_near; - let normalized_distance = (distance - observer_info.z_near) / z_range; + let z_range = observer_position.z_far - observer_position.z_near; + let normalized_distance = + (distance - observer_position.z_near) / z_range; let visible = normalized_distance >= level.begin() && normalized_distance <= level.end(); lod_filter[object.index() as usize] = visible; @@ -948,7 +996,7 @@ impl RenderDataBundleStorage { let mut ctx = RenderContext { render_mask, elapsed_time, - observer_info: &observer_info, + observer_position, frustum: Some(&frustum), storage: &mut storage, graph, @@ -1042,21 +1090,22 @@ impl RenderDataBundleStorage { // Upload camera uniforms. let inv_view = self - .observer_info + .observer_position .view_matrix .try_inverse() .unwrap_or_default(); - let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix; + let view_projection = + self.observer_position.projection_matrix * self.observer_position.view_matrix; let camera_up = inv_view.up(); let camera_side = inv_view.side(); let camera_uniforms = StaticUniformBuffer::<512>::new() .with(&view_projection) - .with(&self.observer_info.observer_position) + .with(&self.observer_position.translation) .with(&camera_up) .with(&camera_side) - .with(&self.observer_info.z_near) - .with(&self.observer_info.z_far) - .with(&(self.observer_info.z_far - self.observer_info.z_near)); + .with(&self.observer_position.z_near) + .with(&self.observer_position.z_far) + .with(&(self.observer_position.z_far - self.observer_position.z_near)); let camera_block = render_context .uniform_memory_allocator .allocate(camera_uniforms); @@ -1089,7 +1138,8 @@ impl RenderDataBundleStorage { { let global_uniforms = self.write_global_uniform_blocks(&mut render_context); - let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix; + let view_projection = + self.observer_position.projection_matrix * self.observer_position.view_matrix; let mut bundle_uniform_data_set = Vec::with_capacity(self.bundles.len()); for bundle in self.bundles.iter() { if !bundle_filter(bundle) { @@ -1228,26 +1278,27 @@ impl RenderDataBundleStorageTrait for RenderDataBundleStorage { #[cfg(test)] mod test { - use crate::renderer::bundle::{ObserverInfo, RenderContext, RenderDataBundleStorage}; + use crate::renderer::bundle::{ObserverPosition, RenderContext, RenderDataBundleStorage}; use fyrox_core::algebra::{Matrix4, Vector3}; //noinspection ALL #[test] fn test_calculate_sorting_index() { - let observer_info = ObserverInfo { - observer_position: Default::default(), + let observer_position = ObserverPosition { + translation: Default::default(), z_near: 0.0, z_far: 0.0, view_matrix: Matrix4::identity(), projection_matrix: Matrix4::identity(), + view_projection_matrix: Matrix4::identity(), }; let render_context = RenderContext { render_mask: Default::default(), elapsed_time: 0.0, - observer_info: &observer_info.clone(), + observer_position: &observer_position.clone(), frustum: None, - storage: &mut RenderDataBundleStorage::new_empty(observer_info), + storage: &mut RenderDataBundleStorage::new_empty(observer_position), graph: &Default::default(), render_pass_name: &Default::default(), dynamic_surface_cache: &mut Default::default(), diff --git a/fyrox-impl/src/renderer/gbuffer.rs b/fyrox-impl/src/renderer/gbuffer.rs index 202fd06a8..7ae0ef476 100644 --- a/fyrox-impl/src/renderer/gbuffer.rs +++ b/fyrox-impl/src/renderer/gbuffer.rs @@ -29,6 +29,7 @@ //! Every alpha channel is used for layer blending for terrains. This is inefficient, but for //! now I don't know better solution. +use crate::renderer::bundle::Observer; use crate::{ core::{ algebra::{Matrix4, Vector2}, @@ -58,7 +59,6 @@ use crate::{ FallbackResources, GeometryCache, QualitySettings, RenderPassStatistics, TextureCache, }, scene::{ - camera::Camera, decal::Decal, graph::Graph, mesh::{surface::SurfaceData, RenderPath}, @@ -79,7 +79,7 @@ pub struct GBuffer { pub(crate) struct GBufferRenderContext<'a, 'b> { pub server: &'a dyn GraphicsServer, - pub camera: &'b Camera, + pub observer: &'b Observer, pub geom_cache: &'a mut GeometryCache, pub bundle_storage: &'a RenderDataBundleStorage, pub texture_cache: &'a mut TextureCache, @@ -200,7 +200,7 @@ impl GBuffer { let GBufferRenderContext { server, - camera, + observer, geom_cache, bundle_storage, texture_cache, @@ -214,8 +214,6 @@ impl GBuffer { .. } = args; - let view_projection = camera.view_projection_matrix(); - if quality_settings.use_occlusion_culling { self.occlusion_tester.try_query_visibility_results(graph); }; @@ -231,7 +229,7 @@ impl GBuffer { let grid_cell = self .occlusion_tester .grid_cache - .cell(camera.global_position()); + .cell(observer.position.translation); let instance_filter = |instance: &SurfaceInstanceData| { !quality_settings.use_occlusion_culling @@ -272,14 +270,18 @@ impl GBuffer { unit_quad, objects.iter(), &self.framebuffer, - camera.global_position(), - view_projection, + observer.position.translation, + observer.position.view_projection_matrix, uniform_buffer_cache, fallback_resources, )?; } - let inv_view_proj = view_projection.try_inverse().unwrap_or_default(); + let inv_view_proj = observer + .position + .view_projection_matrix + .try_inverse() + .unwrap_or_default(); let depth = self.depth(); let decal_mask = self.decal_mask_texture(); let resolution = Vector2::new(self.width as f32, self.height as f32); @@ -289,7 +291,8 @@ impl GBuffer { // decals do not modify depth (only diffuse and normal maps). let unit_cube = &self.cube; for decal in graph.linear_iter().filter_map(|n| n.cast::()) { - let world_view_proj = view_projection * decal.global_transform(); + let world_view_proj = + observer.position.view_projection_matrix * decal.global_transform(); let diffuse_texture = decal .diffuse_texture() diff --git a/fyrox-impl/src/renderer/light.rs b/fyrox-impl/src/renderer/light.rs index 54f8c3638..a65b91c33 100644 --- a/fyrox-impl/src/renderer/light.rs +++ b/fyrox-impl/src/renderer/light.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::renderer::bundle::Observer; use crate::renderer::DynamicSurfaceCache; use crate::{ core::{ @@ -54,7 +55,6 @@ use crate::{ RenderPassStatistics, TextureCache, }, scene::{ - camera::Camera, mesh::{ buffer::{TriangleBuffer, VertexBuffer}, surface::SurfaceData, @@ -87,7 +87,7 @@ pub(crate) struct DeferredRendererContext<'a> { pub elapsed_time: f32, pub server: &'a dyn GraphicsServer, pub scene: &'a Scene, - pub camera: &'a Camera, + pub observer: &'a Observer, pub gbuffer: &'a mut GBuffer, pub ambient_color: Color, pub render_data_bundle: &'a RenderDataBundleStorage, @@ -298,7 +298,7 @@ impl DeferredLightRenderer { elapsed_time, server, scene, - camera, + observer, gbuffer, render_data_bundle, shader_cache, @@ -315,38 +315,41 @@ impl DeferredLightRenderer { } = args; let viewport = Rect::new(0, 0, gbuffer.width, gbuffer.height); - let frustum = Frustum::from_view_projection_matrix(camera.view_projection_matrix()) - .unwrap_or_default(); + let frustum = + Frustum::from_view_projection_matrix(observer.position.view_projection_matrix) + .unwrap_or_default(); let frame_matrix = make_viewport_matrix(viewport); - let projection_matrix = camera.projection_matrix(); - let view_projection = camera.view_projection_matrix(); - let inv_projection = projection_matrix.try_inverse().unwrap_or_default(); - let inv_view_projection = view_projection.try_inverse().unwrap_or_default(); - let camera_global_position = camera.global_position(); + let inv_projection = observer + .position + .projection_matrix + .try_inverse() + .unwrap_or_default(); + let inv_view_projection = observer + .position + .view_projection_matrix + .try_inverse() + .unwrap_or_default(); // Fill SSAO map. if settings.use_ssao { pass_stats += self.ssao_renderer.render( gbuffer, - projection_matrix, - camera.view_matrix().basis(), + observer.position.projection_matrix, + observer.position.view_matrix.basis(), uniform_buffer_cache, fallback_resources, )?; } // Render skybox (if any). - if let Some(skybox) = camera.skybox_ref() { - if let Some(texture_sampler_pair) = skybox - .cubemap_ref() - .and_then(|cube_map| textures.get(server, cube_map)) - { - let size = camera.projection().z_far() / 2.0f32.sqrt(); + if let Some(skybox) = observer.skybox_map.as_ref() { + if let Some(texture_sampler_pair) = textures.get(server, skybox) { + let size = observer.position.z_far / 2.0f32.sqrt(); let scale = Matrix4::new_scaling(size); - let wvp = Matrix4::new_translation(&camera.global_position()) * scale; - let wvp = view_projection * wvp; + let wvp = Matrix4::new_translation(&observer.position.translation) * scale; + let wvp = observer.position.view_projection_matrix * wvp; let properties = PropertyGroup::from([property("worldViewProjection", &wvp)]); let material = RenderMaterial::from([ binding( @@ -376,9 +379,9 @@ impl DeferredLightRenderer { } } - let environment_map = camera - .skybox_ref() - .and_then(|s| s.cubemap_ref()) + let environment_map = observer + .environment_map + .as_ref() .and_then(|c| { textures .get(server, c) @@ -401,7 +404,7 @@ impl DeferredLightRenderer { let properties = PropertyGroup::from([ property("worldViewProjection", &frame_matrix), property("ambientColor", &ambient_color), - property("cameraPosition", &camera_global_position), + property("cameraPosition", &observer.position.translation), property("invViewProj", &inv_view_projection), ]); let material = RenderMaterial::from([ @@ -462,7 +465,7 @@ impl DeferredLightRenderer { )?; for light in render_data_bundle.light_sources.iter() { - let distance_to_camera = (light.position - camera.global_position()).norm(); + let distance_to_camera = (light.position - observer.position.translation).norm(); let ( raw_radius, @@ -547,7 +550,11 @@ impl DeferredLightRenderer { let b1 = shadows_distance * 0.2; let b2 = shadows_distance * 0.4; let cascade_index = if distance_to_camera < b1 - || (camera.global_position().metric_distance(&light.position) <= light_radius) + || (observer + .position + .translation + .metric_distance(&light.position) + <= light_radius) { 0 } else if distance_to_camera > b1 && distance_to_camera < b2 { @@ -566,7 +573,7 @@ impl DeferredLightRenderer { let mut light_view_projection = Matrix4::identity(); // Mark lit areas in stencil buffer to do light calculations only on them. - let shape_wvp_matrix = view_projection * bounding_shape_matrix; + let shape_wvp_matrix = observer.position.view_projection_matrix * bounding_shape_matrix; for (cull_face, stencil_action) in [ (CullFace::Front, StencilAction::Incr), (CullFace::Back, StencilAction::Decr), @@ -611,10 +618,16 @@ impl DeferredLightRenderer { if !matches!(light.kind, LightSourceKind::Directional { .. }) && settings.use_light_occlusion_culling { - if visibility_cache.needs_occlusion_query(camera_global_position, light.handle) { + if visibility_cache + .needs_occlusion_query(observer.position.translation, light.handle) + { // Draw full screen quad, that will be used to count pixels that passed the stencil test // on the stencil buffer's content generated by two previous drawing commands. - visibility_cache.begin_query(server, camera_global_position, light.handle)?; + visibility_cache.begin_query( + server, + observer.position.translation, + light.handle, + )?; let properties = PropertyGroup::from([property("worldViewProjection", &frame_matrix)]); let material = RenderMaterial::from([binding("properties", &properties)]); @@ -632,7 +645,7 @@ impl DeferredLightRenderer { visibility_cache.end_query(); } - if !visibility_cache.is_visible(camera_global_position, light.handle) { + if !visibility_cache.is_visible(observer.position.translation, light.handle) { needs_lighting = false; } } @@ -665,7 +678,7 @@ impl DeferredLightRenderer { pass_stats += self.spot_shadow_map_renderer.render( server, &scene.graph, - *camera.render_mask, + observer.render_mask, elapsed_time, light.position, light_view_matrix, @@ -687,7 +700,7 @@ impl DeferredLightRenderer { pass_stats += self.point_shadow_map_renderer .render(PointShadowMapRenderContext { - render_mask: *camera.render_mask, + render_mask: observer.render_mask, elapsed_time, state: server, graph: &scene.graph, @@ -711,7 +724,7 @@ impl DeferredLightRenderer { state: server, graph: &scene.graph, light, - camera, + observer, geom_cache: geometry_cache, shader_cache, texture_cache: textures, @@ -774,7 +787,7 @@ impl DeferredLightRenderer { property("invViewProj", &inv_view_projection), property("lightPos", &light.position), property("lightColor", &color), - property("cameraPosition", &camera_global_position), + property("cameraPosition", &observer.position.translation), property("lightDirection", &emit_direction), property("lightRadius", &light_radius), property("halfHotspotConeAngleCos", &half_hotspot_cone_angle_cos), @@ -844,7 +857,7 @@ impl DeferredLightRenderer { property("invViewProj", &inv_view_projection), property("lightPos", &light.position), property("lightColor", &color), - property("cameraPosition", &camera_global_position), + property("cameraPosition", &observer.position.translation), property("lightRadius", &light_radius), property("shadowBias", &shadow_bias), property("lightIntensity", &light.intensity), @@ -916,15 +929,14 @@ impl DeferredLightRenderer { ]; let shadow_map_inv_size = 1.0 / (self.csm_renderer.size() as f32); let shadow_bias = csm_options.shadow_bias(); - let view_matrix = camera.view_matrix(); let properties = PropertyGroup::from([ property("worldViewProjection", &frame_matrix), - property("viewMatrix", &view_matrix), + property("viewMatrix", &observer.position.view_matrix), property("invViewProj", &inv_view_projection), property("lightViewProjMatrices", matrices.as_slice()), property("lightColor", &color), property("lightDirection", &emit_direction), - property("cameraPosition", &camera_global_position), + property("cameraPosition", &observer.position.translation), property("lightIntensity", &light.intensity), property("shadowsEnabled", &shadows_enabled), property("shadowBias", &shadow_bias), @@ -1006,9 +1018,9 @@ impl DeferredLightRenderer { light, gbuffer, &self.quad, - camera.view_matrix(), + observer.position.view_matrix, inv_projection, - view_projection, + observer.position.view_projection_matrix, viewport, &scene.graph, frame_buffer, diff --git a/fyrox-impl/src/renderer/mod.rs b/fyrox-impl/src/renderer/mod.rs index 63656ca00..d0a753a12 100644 --- a/fyrox-impl/src/renderer/mod.rs +++ b/fyrox-impl/src/renderer/mod.rs @@ -49,6 +49,7 @@ mod shadow; mod ssao; mod stats; +use crate::renderer::bundle::Observer; use crate::scene::node::Node; use crate::{ asset::{event::ResourceEvent, manager::ResourceManager}, @@ -65,12 +66,11 @@ use crate::{ uuid_provider, }, engine::error::EngineError, - graph::SceneGraph, gui::draw::DrawingContext, material::shader::{Shader, ShaderDefinition}, renderer::{ bloom::BloomRenderer, - bundle::{ObserverInfo, RenderDataBundleStorage, RenderDataBundleStorageOptions}, + bundle::{RenderDataBundleStorage, RenderDataBundleStorageOptions}, cache::{ geometry::GeometryCache, shader::{ @@ -899,10 +899,7 @@ pub struct SceneRenderPassContext<'a, 'b> { pub scene: &'b Scene, /// A camera from the scene that is used as "eyes". - pub camera: &'b Camera, - - /// A viewport of the camera. - pub viewport: Rect, + pub observer: &'b Observer, /// A handle of the scene being rendered. pub scene_handle: Handle, @@ -1038,6 +1035,24 @@ fn render_target_size(render_target: &TextureResource) -> Result, F }) } +fn collect_observers(scene: &Scene, frame_size: Vector2) -> Vec { + scene + .graph + .linear_iter() + .filter_map(|node| { + if node.is_globally_enabled() { + if let Some(camera) = node.cast::() { + if camera.is_enabled() { + return Some(camera); + } + } + } + None + }) + .map(|camera| Observer::from_camera(camera, frame_size)) + .collect() +} + impl Renderer { /// Creates a new renderer with the given graphics server. pub fn new( @@ -1520,19 +1535,13 @@ impl Renderer { .camera_data .retain(|h, _| graph.is_valid_handle(*h)); - for (camera_handle, camera) in graph.pair_iter().filter_map(|(handle, node)| { - if node.is_globally_enabled() { - if let Some(camera) = node.cast::() { - if camera.is_enabled() { - return Some((handle, camera)); - } - } - } - None - }) { - let (render_data, rt_size) = if let Some(render_target) = camera.render_target() { + let observers = collect_observers(scene, frame_size); + + for observer in &observers { + let render_data = if let Some(render_target) = observer.render_target.as_ref() { let rt_size = render_target_size(render_target)?; - let camera_render_data = match scene_render_data.camera_data.entry(camera_handle) { + let camera_render_data = match scene_render_data.camera_data.entry(observer.handle) + { Entry::Occupied(entry) => { let camera_render_data = entry.into_mut(); recreate_render_data_if_needed( @@ -1546,7 +1555,8 @@ impl Renderer { Entry::Vacant(entry) => { let render_data = entry.insert(RenderDataContainer::new(server, rt_size)?); info!( - "A new associated scene rendering data was created for camera {camera_handle}!" + "A new associated scene rendering data was created for camera {}!", + observer.handle ); render_data } @@ -1558,26 +1568,20 @@ impl Renderer { camera_render_data.ldr_scene_frame_texture().clone(), )?; - (camera_render_data, rt_size) + camera_render_data } else { - (&mut scene_render_data.scene_data, frame_size) + &mut scene_render_data.scene_data }; - let visibility_cache = self.visibility_cache.get_or_register(graph, camera_handle); - - let viewport = camera.viewport_pixels(rt_size); + let visibility_cache = self + .visibility_cache + .get_or_register(graph, observer.handle); let bundle_storage = RenderDataBundleStorage::from_graph( graph, - *camera.render_mask, + observer.render_mask, elapsed_time, - ObserverInfo { - observer_position: camera.global_position(), - z_near: camera.projection().z_near(), - z_far: camera.projection().z_far(), - view_matrix: camera.view_matrix(), - projection_matrix: camera.projection_matrix(), - }, + &observer.position, GBUFFER_PASS_NAME.clone(), RenderDataBundleStorageOptions { collect_lights: true, @@ -1592,7 +1596,7 @@ impl Renderer { render_data.statistics += render_data.gbuffer.fill(GBufferRenderContext { server, - camera, + observer, geom_cache: &mut self.geometry_cache, bundle_storage: &bundle_storage, texture_cache: &mut self.texture_cache, @@ -1611,7 +1615,7 @@ impl Renderer { render_data.copy_depth_stencil_to_scene_framebuffer(); render_data.hdr_scene_framebuffer.clear( - viewport, + observer.viewport, Some( scene .rendering_options @@ -1628,7 +1632,7 @@ impl Renderer { elapsed_time, server, scene, - camera, + observer, gbuffer: &mut render_data.gbuffer, ambient_color: scene.rendering_options.ambient_lighting_color, render_data_bundle: &bundle_storage, @@ -1656,7 +1660,7 @@ impl Renderer { shader_cache: &mut self.shader_cache, bundle_storage: &bundle_storage, framebuffer: &render_data.hdr_scene_framebuffer, - viewport, + viewport: observer.viewport, quality_settings: &self.quality_settings, fallback_resources: &self.fallback_resources, scene_depth: depth, @@ -1676,9 +1680,8 @@ impl Renderer { shader_cache: &mut self.shader_cache, quality_settings: &self.quality_settings, bundle_storage: &bundle_storage, - viewport, scene, - camera, + observer, scene_handle, fallback_resources: &self.fallback_resources, depth_texture: render_data.gbuffer.depth(), @@ -1708,12 +1711,12 @@ impl Renderer { render_data.hdr_scene_frame_texture(), render_data.bloom_renderer.result(), &render_data.ldr_scene_framebuffer, - viewport, + observer.viewport, quad, dt, - camera.exposure(), - camera.color_grading_lut_ref(), - camera.color_grading_enabled(), + observer.exposure, + observer.color_grading_lut.as_ref(), + observer.color_grading_enabled, &mut self.texture_cache, &mut self.uniform_buffer_cache, &self.fallback_resources, @@ -1722,7 +1725,7 @@ impl Renderer { // Apply FXAA if needed. if self.quality_settings.fxaa { render_data.statistics += self.fxaa_renderer.render( - viewport, + observer.viewport, render_data.ldr_scene_frame_texture(), &render_data.ldr_temp_framebuffer, &mut self.uniform_buffer_cache, @@ -1736,7 +1739,7 @@ impl Renderer { &render_data.ldr_scene_framebuffer, temp_frame_texture, &self.blit_shader, - viewport, + observer.viewport, quad, &self.fallback_resources, )?; @@ -1746,9 +1749,9 @@ impl Renderer { self.debug_renderer.set_lines(&scene.drawing_context.lines); render_data.statistics += self.debug_renderer.render( &mut self.uniform_buffer_cache, - viewport, + observer.viewport, &render_data.ldr_scene_framebuffer, - camera.view_projection_matrix(), + observer.position.view_projection_matrix, )?; for render_pass in self.scene_render_passes.iter() { @@ -1763,9 +1766,8 @@ impl Renderer { shader_cache: &mut self.shader_cache, quality_settings: &self.quality_settings, bundle_storage: &bundle_storage, - viewport, scene, - camera, + observer, scene_handle, fallback_resources: &self.fallback_resources, depth_texture: render_data.gbuffer.depth(), diff --git a/fyrox-impl/src/renderer/shadow/csm.rs b/fyrox-impl/src/renderer/shadow/csm.rs index b69362daa..07c72aea4 100644 --- a/fyrox-impl/src/renderer/shadow/csm.rs +++ b/fyrox-impl/src/renderer/shadow/csm.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::renderer::bundle::Observer; use crate::renderer::DynamicSurfaceCache; use crate::{ core::{ @@ -27,7 +28,7 @@ use crate::{ }, renderer::{ bundle::{ - BundleRenderContext, LightSource, LightSourceKind, ObserverInfo, + BundleRenderContext, LightSource, LightSourceKind, ObserverPosition, RenderDataBundleStorage, RenderDataBundleStorageOptions, }, cache::{ @@ -43,7 +44,6 @@ use crate::{ FallbackResources, RenderPassStatistics, ShadowMapPrecision, DIRECTIONAL_SHADOW_PASS_NAME, }, scene::{ - camera::Camera, graph::Graph, light::directional::{FrustumSplitOptions, CSM_NUM_CASCADES}, }, @@ -103,7 +103,7 @@ pub(crate) struct CsmRenderContext<'a, 'c> { pub state: &'a dyn GraphicsServer, pub graph: &'c Graph, pub light: &'c LightSource, - pub camera: &'c Camera, + pub observer: &'a Observer, pub geom_cache: &'a mut GeometryCache, pub shader_cache: &'a mut ShaderCache, pub texture_cache: &'a mut TextureCache, @@ -153,7 +153,7 @@ impl CsmRenderer { state, graph, light, - camera, + observer, geom_cache, shader_cache, texture_cache, @@ -178,16 +178,16 @@ impl CsmRenderer { let z_values = match csm_options.split_options { FrustumSplitOptions::Absolute { far_planes } => [ - camera.projection().z_near(), + observer.position.z_near, far_planes[0], far_planes[1], far_planes[2], ], FrustumSplitOptions::Relative { fractions } => [ - camera.projection().z_near(), - camera.projection().z_far() * fractions[0], - camera.projection().z_far() * fractions[1], - camera.projection().z_far() * fractions[2], + observer.position.z_near, + observer.position.z_far * fractions[0], + observer.position.z_far * fractions[1], + observer.position.z_far * fractions[2], ], }; @@ -202,16 +202,17 @@ impl CsmRenderer { z_far += MIN_DEPTH_DELTA * z_near; } - let projection_matrix = camera - .projection() + let projection_matrix = observer + .projection .clone() .with_z_near(z_near) .with_z_far(z_far) .matrix(frame_size); - let frustum = - Frustum::from_view_projection_matrix(projection_matrix * camera.view_matrix()) - .unwrap_or_default(); + let frustum = Frustum::from_view_projection_matrix( + projection_matrix * observer.position.view_matrix, + ) + .unwrap_or_default(); let center = frustum.center(); let observer_position = center + light_direction; @@ -256,14 +257,15 @@ impl CsmRenderer { let bundle_storage = RenderDataBundleStorage::from_graph( graph, - *camera.render_mask, + observer.render_mask, elapsed_time, - ObserverInfo { - observer_position, + &ObserverPosition { + translation: observer_position, z_near, z_far, view_matrix: light_view_matrix, projection_matrix: cascade_projection_matrix, + view_projection_matrix: cascade_projection_matrix * light_view_matrix, }, DIRECTIONAL_SHADOW_PASS_NAME.clone(), RenderDataBundleStorageOptions { diff --git a/fyrox-impl/src/renderer/shadow/point.rs b/fyrox-impl/src/renderer/shadow/point.rs index 0bee44625..395143fdf 100644 --- a/fyrox-impl/src/renderer/shadow/point.rs +++ b/fyrox-impl/src/renderer/shadow/point.rs @@ -26,7 +26,7 @@ use crate::{ }, renderer::{ bundle::{ - BundleRenderContext, ObserverInfo, RenderDataBundleStorage, + BundleRenderContext, ObserverPosition, RenderDataBundleStorage, RenderDataBundleStorageOptions, }, cache::{shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator}, @@ -217,12 +217,13 @@ impl PointShadowMapRenderer { graph, render_mask, elapsed_time, - ObserverInfo { - observer_position: light_pos, + &ObserverPosition { + translation: light_pos, z_near, z_far, view_matrix: light_view_matrix, projection_matrix: light_projection_matrix, + view_projection_matrix: light_projection_matrix * light_view_matrix, }, POINT_SHADOW_PASS_NAME.clone(), RenderDataBundleStorageOptions { diff --git a/fyrox-impl/src/renderer/shadow/spot.rs b/fyrox-impl/src/renderer/shadow/spot.rs index 051e170ee..49a6653f7 100644 --- a/fyrox-impl/src/renderer/shadow/spot.rs +++ b/fyrox-impl/src/renderer/shadow/spot.rs @@ -28,7 +28,7 @@ use crate::{ }, renderer::{ bundle::{ - BundleRenderContext, ObserverInfo, RenderDataBundleStorage, + BundleRenderContext, ObserverPosition, RenderDataBundleStorage, RenderDataBundleStorageOptions, }, cache::{shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator}, @@ -146,12 +146,13 @@ impl SpotShadowMapRenderer { graph, render_mask, elapsed_time, - ObserverInfo { - observer_position: light_position, + &ObserverPosition { + translation: light_position, z_near, z_far, view_matrix: light_view_matrix, projection_matrix: light_projection_matrix, + view_projection_matrix: light_projection_matrix * light_view_matrix, }, SPOT_SHADOW_PASS_NAME.clone(), RenderDataBundleStorageOptions { diff --git a/fyrox-impl/src/scene/mesh/mod.rs b/fyrox-impl/src/scene/mesh/mod.rs index 719d5b7c8..2e59149c9 100644 --- a/fyrox-impl/src/scene/mesh/mod.rs +++ b/fyrox-impl/src/scene/mesh/mod.rs @@ -180,7 +180,7 @@ impl BatchContainer { descendant.collect_render_data(&mut RenderContext { render_mask: ctx.render_mask, elapsed_time: ctx.elapsed_time, - observer_info: ctx.observer_info, + observer_position: ctx.observer_position, frustum: None, storage: self, graph: ctx.graph, diff --git a/fyrox-impl/src/scene/particle_system/mod.rs b/fyrox-impl/src/scene/particle_system/mod.rs index 3ec4feb32..29014b352 100644 --- a/fyrox-impl/src/scene/particle_system/mod.rs +++ b/fyrox-impl/src/scene/particle_system/mod.rs @@ -588,7 +588,7 @@ impl NodeTrait for ParticleSystem { fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow { if !self.should_be_rendered(ctx.frustum, ctx.render_mask) - || self.is_distance_clipped(&ctx.observer_info.observer_position) + || self.is_distance_clipped(&ctx.observer_position.translation) { return RdcControlFlow::Continue; } @@ -598,8 +598,8 @@ impl NodeTrait for ParticleSystem { } let distance_to_observer = ctx - .observer_info .observer_position + .translation .metric_distance(&self.global_position()); let particle_alpha_factor = if distance_to_observer >= self.visible_distance() { @@ -614,7 +614,7 @@ impl NodeTrait for ParticleSystem { let actual_position = particle.position + self.base.global_position(); particle .sqr_distance_to_camera - .set((ctx.observer_info.observer_position - actual_position).norm_squared()); + .set((ctx.observer_position.translation - actual_position).norm_squared()); sorted_particles.push(i as u32); } } diff --git a/fyrox-impl/src/scene/terrain/mod.rs b/fyrox-impl/src/scene/terrain/mod.rs index 841625ed3..10e89af5a 100644 --- a/fyrox-impl/src/scene/terrain/mod.rs +++ b/fyrox-impl/src/scene/terrain/mod.rs @@ -2650,7 +2650,7 @@ impl NodeTrait for Terrain { let quad_tree = chunk.quad_tree.lock(); let levels = (0..=quad_tree.max_level) .map(|n| { - ctx.observer_info.z_far + ctx.observer_position.z_far * ((quad_tree.max_level - n) as f32 / quad_tree.max_level as f32) .powf(3.0) }) @@ -2669,7 +2669,7 @@ impl NodeTrait for Terrain { self.height_map_size(), self.chunk_size(), ctx.frustum, - ctx.observer_info.observer_position, + ctx.observer_position.translation, &levels, &mut selection, );