diff --git a/native/src/scripts/objects.rs b/native/src/scripts/objects.rs index bc9cbbb..b771950 100644 --- a/native/src/scripts/objects.rs +++ b/native/src/scripts/objects.rs @@ -1,4 +1,5 @@ mod building; +mod camera; mod canon_upgrade; mod debugger_3_d; mod water_jet; diff --git a/native/src/scripts/objects/camera.rs b/native/src/scripts/objects/camera.rs new file mode 100644 index 0000000..ab174e7 --- /dev/null +++ b/native/src/scripts/objects/camera.rs @@ -0,0 +1,61 @@ +use godot::{builtin::Dictionary, classes::Camera3D, engine::Node3D, obj::Gd}; +use godot_rust_script::{godot_script_impl, CastToScript, GodotScript, RsRef}; +use itertools::Itertools; + +use crate::{ + debug, info, + scripts::world::solar_setup::{ISolarSetup, SolarSetup}, + warn, +}; + +#[derive(GodotScript, Debug)] +#[script(base = Camera3D)] +struct Camera { + #[export] + pub exposure_times: Dictionary, + + #[export] + pub solar_setup_node: Option>, + + solar_setup: Option>, + + base: Gd, +} + +#[godot_script_impl] +impl Camera { + pub fn _ready(&mut self) { + self.solar_setup = self.solar_setup_node.clone().map(|node| node.into_script()); + } + + pub fn _process(&mut self, _delta: f64) { + let Some(solar_setup) = self.solar_setup.as_mut() else { + warn!("Solar setup is not available!"); + return; + }; + + let Some(mut attributes) = self.base.get_attributes() else { + warn!("Camera has no attributes!"); + return; + }; + + let game_time = solar_setup.get_ingame_time_h(); + + let exposure = self + .exposure_times + .iter_shared() + .sorted_by(|(key_a, _), (key_b, _)| key_a.to::().total_cmp(&key_b.to::())) + .rev() + .find(|(key, _)| key.to::() <= game_time) + .map(|(_, value)| value.to::()) + .unwrap_or(0.0); + + if attributes.get_exposure_sensitivity() == exposure { + return; + } + + info!("Updating camera exposure to {} at {}", exposure, game_time); + + attributes.set_exposure_sensitivity(exposure); + } +} diff --git a/native/src/scripts/world.rs b/native/src/scripts/world.rs index 61bf89e..3c70202 100644 --- a/native/src/scripts/world.rs +++ b/native/src/scripts/world.rs @@ -1,2 +1,2 @@ mod buildings; -mod solar_setup; +pub mod solar_setup; diff --git a/native/src/scripts/world/solar_setup.rs b/native/src/scripts/world/solar_setup.rs index ae80879..4b0fdbd 100644 --- a/native/src/scripts/world/solar_setup.rs +++ b/native/src/scripts/world/solar_setup.rs @@ -1,3 +1,4 @@ +use godot::builtin::math::FloatExt; use godot::builtin::Vector3; use godot::engine::{light_3d, DirectionalLight3D, Node3D, Time}; use godot::obj::Gd; @@ -7,7 +8,7 @@ use crate::util::logger; #[derive(GodotScript, Debug)] #[script(base = Node3D)] -struct SolarSetup { +pub struct SolarSetup { /// Reference to the sun child node. #[export] pub sun: Option>, @@ -16,9 +17,7 @@ struct SolarSetup { #[export] pub moon: Option>, - cached_moon_brightness: f32, - - // duration from sun rise to sun set in minutes + /// duration from sun rise to sun set in minutes #[export(range(min = 1.0, max = 120.0, step = 1.0))] pub day_length: u64, @@ -27,20 +26,9 @@ struct SolarSetup { #[godot_script_impl] impl SolarSetup { - pub fn _ready(&mut self) { - let Some(ref mut moon) = self.moon else { - logger::error!("no moon is assinged to solar setup!"); - logger::error!("node path: {}", self.base.get_path()); - return; - }; - - self.cached_moon_brightness = moon.get_param(light_3d::Param::ENERGY); - } - pub fn _physics_process(&mut self, _delta: f64) { - let day_length = self.day_length * 60 * 1000; - let time = Time::singleton().get_ticks_msec() % (day_length * 2); - + let time = self.get_time(); + let day_length = self.day_length_ms(); let sun_pos = time as f32 * (360.0 / (day_length * 2) as f32); let Some(ref mut sun) = self.sun else { @@ -59,20 +47,54 @@ impl SolarSetup { self.base .set_rotation_degrees(Vector3::new(sun_pos, 0.0, 0.0)); + let sun_visible = sun_pos < 190.0; + let sun_zenit_distance = ((sun_pos - 90.0) / 90.0).abs().clamp(0.0, 1.0); + sun.set_param( light_3d::Param::ENERGY, - if sun_pos > 190.0 { 0.0 } else { 1.0 }, + if !sun_visible { 0.0 } else { 1.0 }, ); - sun.set_shadow(sun_pos < 190.0); + sun.set_shadow(sun_visible); + + if sun_visible { + sun.set_param( + light_3d::Param::INTENSITY, + 100000.0.lerp(400.0, sun_zenit_distance), + ); + sun.set_temperature((5500.0).lerp(1850.0, sun_zenit_distance)); + } moon.set_param( light_3d::Param::ENERGY, - if sun_pos > 180.0 { - self.cached_moon_brightness - } else { - 0.0 - }, + if sun_pos > 180.0 { 1.0 } else { 0.0 }, ); moon.set_shadow(sun_pos > 180.0); } + + /// Day length (sunrise to sunset) in ms. + fn day_length_ms(&self) -> u64 { + self.day_length * 60 * 1000 + } + + /// Get the current game time in ms. + pub fn get_time(&self) -> u64 { + let day_length = self.day_length_ms(); + + Time::singleton().get_ticks_msec() % (day_length * 2) + } + + // get the current in-game time in seconds since sunrise. + pub fn get_ingame_time_s(&self) -> f64 { + self.get_ingame_time_m() * 60.0 + } + + // get the current in-game time in minutes since sunrise. + pub fn get_ingame_time_m(&self) -> f64 { + self.get_ingame_time_h() * 60.0 + } + + // get the current in-game time in hours since sunrise. + pub fn get_ingame_time_h(&self) -> f64 { + self.get_time() as f64 / (self.day_length_ms() as f64 * 2.0 / 24.0) + } } diff --git a/project.godot b/project.godot index 0521ead..9f9133f 100644 --- a/project.godot +++ b/project.godot @@ -131,15 +131,25 @@ common/physics_ticks_per_second=120 [rendering] textures/vram_compression/import_s3tc_bptc=true +lights_and_shadows/use_physical_light_units=true lights_and_shadows/directional_shadow/size=8192 -lights_and_shadows/directional_shadow/soft_shadow_filter_quality=4 -lights_and_shadows/directional_shadow/16_bits=false +lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3 lights_and_shadows/positional_shadow/soft_shadow_filter_quality=3 -global_illumination/gi/use_half_resolution=true -textures/decals/filter=2 +global_illumination/voxel_gi/quality=1 +environment/ssao/half_size=false +environment/ssil/half_size=false +anti_aliasing/screen_space_roughness_limiter/enabled=false +scaling_3d/mode=2 +scaling_3d/scale=0.5 +scaling_3d/fsr_sharpness=0.2 +textures/decals/filter=5 +textures/light_projectors/filter=5 +environment/screen_space_reflection/roughness_quality=2 +environment/subsurface_scattering/subsurface_scattering_quality=2 +global_illumination/sdfgi/probe_ray_count=2 environment/volumetric_fog/volume_size=100 environment/volumetric_fog/volume_depth=100 +anti_aliasing/quality/use_taa=true occlusion_culling/use_occlusion_culling=true -lights_and_shadows/positional_shadow/atlas_16_bits=false environment/defaults/default_environment="res://resources/Environments/default_env.tres" threads/thread_model=2 diff --git a/resources/Environments/WorldEnv.tres b/resources/Environments/WorldEnv.tres index 2f28015..a1281ba 100644 --- a/resources/Environments/WorldEnv.tres +++ b/resources/Environments/WorldEnv.tres @@ -7,17 +7,33 @@ sky_material = ExtResource("1_inrw2") [resource] background_mode = 2 +background_intensity = 20000.0 sky = SubResource("Sky_gwtol") tonemap_mode = 3 -tonemap_exposure = 0.7 +tonemap_white = 6.0 ssr_enabled = true -ssr_max_steps = 80 -ssr_fade_out = 1.0 -ssr_depth_tolerance = 0.51 +ssr_max_steps = 128 +ssr_fade_in = 0.05 +ssr_fade_out = 1.5 ssao_enabled = true +ssao_radius = 1.2 +ssao_intensity = 1.1 +ssao_horizon = 0.0 +ssao_sharpness = 1.0 sdfgi_enabled = true +sdfgi_bounce_feedback = 1.0 +glow_enabled = true +glow_levels/1 = 1.0 +glow_levels/2 = 1.0 +glow_levels/4 = 1.0 +glow_levels/6 = 1.0 +glow_levels/7 = 1.0 +glow_intensity = 0.5 +glow_blend_mode = 1 fog_light_color = Color(0.65, 0.84, 0.95, 1) fog_density = 0.001 volumetric_fog_enabled = true volumetric_fog_density = 0.0 volumetric_fog_length = 2000.0 +adjustment_enabled = true +adjustment_saturation = 1.3 diff --git a/resources/Materials/ocean_material.tres b/resources/Materials/ocean_material.tres index 0879cf7..6f23b0a 100644 --- a/resources/Materials/ocean_material.tres +++ b/resources/Materials/ocean_material.tres @@ -27,7 +27,7 @@ shader_parameter/speed = 0.1 shader_parameter/Wave2_Dir = 0.569 shader_parameter/wave_height = 5.0 shader_parameter/Albedo = Color(0.129412, 0.180392, 0.262745, 1) -shader_parameter/Depth_Scale = 0.05 +shader_parameter/Depth_Scale = 0.2 shader_parameter/Roughness = 0.2 shader_parameter/Noise = SubResource("NoiseTexture2D_06ugh") shader_parameter/TextureUniform = SubResource("NoiseTexture2D_l65p1") diff --git a/resources/Objects/Spawner/fire_spawner.tscn b/resources/Objects/Spawner/fire_spawner.tscn index f890364..d7ea091 100644 --- a/resources/Objects/Spawner/fire_spawner.tscn +++ b/resources/Objects/Spawner/fire_spawner.tscn @@ -35,6 +35,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 10.1948, 0) [node name="FireLightSource" type="OmniLight3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.01561, -3.39593) +light_intensity_lumens = 3000.0 +light_temperature = 1850.0 light_color = Color(0.980392, 0.309804, 0.00392157, 1) light_volumetric_fog_energy = 0.0 diff --git a/resources/Shaders/ocean_visualshader.tres b/resources/Shaders/ocean_visualshader.tres index 087d36c..d5c9f12 100644 --- a/resources/Shaders/ocean_visualshader.tres +++ b/resources/Shaders/ocean_visualshader.tres @@ -487,7 +487,6 @@ void fragment() { } " -graph_offset = Vector2(263.253, -737.657) varyings/vary_world_uv = "0,3" varyings/vary_world_uv2 = "0,3" nodes/vertex/0/position = Vector2(3340, -540) diff --git a/resources/TestScenes/ocean.tscn b/resources/TestScenes/ocean.tscn index eaeee51..93205ec 100644 --- a/resources/TestScenes/ocean.tscn +++ b/resources/TestScenes/ocean.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://djrh870bg3ckr"] +[gd_scene load_steps=9 format=3 uid="uid://djrh870bg3ckr"] [ext_resource type="Material" uid="uid://bmp5rvu5slnnt" path="res://resources/Materials/ocean_material.tres" id="2"] [ext_resource type="Material" uid="uid://7h8w8gb1xjbk" path="res://resources/Materials/ocean_backdrop.tres" id="2_en16t"] @@ -20,6 +20,8 @@ background_mode = 2 sky = SubResource("4") tonemap_mode = 3 +[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_jtm2k"] + [sub_resource type="PlaneMesh" id="PlaneMesh_2q6u6"] size = Vector2(512, 512) @@ -79,6 +81,7 @@ shadow_enabled = true [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("5") +camera_attributes = SubResource("CameraAttributesPractical_jtm2k") [node name="CSGCombiner3D" type="CSGCombiner3D" parent="."] diff --git a/resources/main.tscn b/resources/main.tscn index 391f837..2d2de8d 100644 --- a/resources/main.tscn +++ b/resources/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3 uid="uid://cvh54xiw8586b"] +[gd_scene load_steps=20 format=3 uid="uid://cvh54xiw8586b"] [ext_resource type="Script" path="res://src/HUD/HUDController.gd" id="1"] [ext_resource type="Script" path="res://src/Objects/World/World.gd" id="3"] @@ -6,6 +6,7 @@ [ext_resource type="Script" path="res://src/HUD/LoadingScreen.gd" id="4"] [ext_resource type="Script" path="res://src/Objects/World/Backdrop.gd" id="6"] [ext_resource type="Environment" uid="uid://bl607wqoa882d" path="res://resources/Environments/WorldEnv.tres" id="8"] +[ext_resource type="Script" path="res://native/src/scripts/objects/camera.rs" id="8_qw8ew"] [ext_resource type="Script" path="res://src/Objects/Camera/CameraInterpolation.gd" id="10"] [ext_resource type="Script" path="res://src/Objects/Terrain/Terrain.gd" id="11"] [ext_resource type="Material" uid="uid://d3jryprnnxb6x" path="res://resources/Materials/terrain_material.tres" id="12"] @@ -17,10 +18,15 @@ [ext_resource type="Script" path="res://native/src/scripts/world/buildings.rs" id="15_23gpq"] [ext_resource type="PackedScene" uid="uid://cmv7rt4gqew38" path="res://resources/Objects/Helis/schweizer_300.tscn" id="16_e6k8r"] +[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_vnmmp"] + [sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_lhcij"] dof_blur_far_enabled = true -dof_blur_far_distance = 180.0 -dof_blur_far_transition = -1.0 +dof_blur_far_distance = 50.0 +dof_blur_far_transition = 50.0 +dof_blur_near_enabled = true +dof_blur_near_distance = 0.06 +dof_blur_near_transition = 0.02 [node name="HUDController" type="Control"] layout_mode = 3 @@ -65,7 +71,6 @@ stretch = true [node name="SubViewport" type="SubViewport" parent="SubViewportContainer" node_paths=PackedStringArray("current_camera_controller")] handle_input_locally = false use_occlusion_culling = true -scaling_3d_mode = 1 audio_listener_enable_3d = true size = Vector2i(1152, 648) size_2d_override_stretch = true @@ -80,7 +85,7 @@ world_constants = ExtResource("15") [node name="Environment" type="WorldEnvironment" parent="SubViewportContainer/SubViewport/World"] environment = ExtResource("8") -camera_attributes = SubResource("CameraAttributesPractical_lhcij") +camera_attributes = SubResource("CameraAttributesPractical_vnmmp") [node name="SolarSetup" type="Node3D" parent="SubViewportContainer/SubViewport/World/Environment" node_paths=PackedStringArray("sun", "moon")] transform = Transform3D(1, -3.48787e-16, 3.48787e-16, 3.48787e-16, 1, -3.48787e-16, -3.48787e-16, 3.48787e-16, 1, 0, 0, 0) @@ -93,20 +98,21 @@ day_length = 30 [node name="Sun" type="DirectionalLight3D" parent="SubViewportContainer/SubViewport/World/Environment/SolarSetup"] transform = Transform3D(1, -3.48787e-16, 3.48787e-16, -3.48787e-16, -1, 8.74228e-08, 3.48787e-16, -8.74228e-08, -1, 2.08165e-12, -5.50338e-09, 0.1) rotation_order = 0 -light_color = Color(1, 0.952941, 0.843137, 1) +light_angular_distance = 0.5 shadow_enabled = true -directional_shadow_mode = 1 -directional_shadow_split_1 = 0.3 +directional_shadow_split_1 = 0.05 directional_shadow_split_2 = 0.3 -directional_shadow_split_3 = 0.0 +directional_shadow_split_3 = 0.4 directional_shadow_blend_splits = true -directional_shadow_fade_start = 0.6 -directional_shadow_max_distance = 400.0 +directional_shadow_max_distance = 500.0 +directional_shadow_pancake_size = 40.0 [node name="Moon" type="DirectionalLight3D" parent="SubViewportContainer/SubViewport/World/Environment/SolarSetup"] transform = Transform3D(1, -3.48787e-16, 3.48787e-16, 3.48787e-16, 1, -3.48787e-16, -3.48787e-16, 3.48787e-16, 1, 2.08165e-12, 1.45043e-10, -0.1) rotation_order = 0 -light_energy = 0.475 +light_intensity_lux = 500.0 +light_temperature = 4100.0 +light_angular_distance = 0.5 shadow_enabled = true directional_shadow_mode = 1 directional_shadow_split_1 = 0.3 @@ -114,11 +120,20 @@ directional_shadow_blend_splits = true directional_shadow_fade_start = 0.6 directional_shadow_max_distance = 400.0 -[node name="Camera" type="Camera3D" parent="SubViewportContainer/SubViewport/World"] +[node name="Camera" type="Camera3D" parent="SubViewportContainer/SubViewport/World" node_paths=PackedStringArray("solar_setup_node")] transform = Transform3D(1, 0, 0, 0, 0.847724, 0.530437, 0, -0.530437, 0.847724, -45.4393, 5.66891, 25.269) +attributes = SubResource("CameraAttributesPractical_lhcij") doppler_tracking = 2 current = true near = 3.0 +script = ExtResource("8_qw8ew") +exposure_times = { +0.0: 100.0, +12.0: 600.0, +14.0: 1500.0, +23.5: 600.0 +} +solar_setup_node = NodePath("../Environment/SolarSetup") [node name="Schweizer_300" parent="SubViewportContainer/SubViewport/World" node_paths=PackedStringArray("child_camera", "child_main_camera", "child_debug_camera") groups=["player"] instance=ExtResource("16_e6k8r")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45.4393, -9.81614e-07, 17.9155)