From c07796f8813c88ebdc77d6c8fe788ae09240ebc6 Mon Sep 17 00:00:00 2001 From: Overside srl Date: Mon, 16 Feb 2026 21:34:02 +0100 Subject: [PATCH] add rain and snow - fix sky color by time --- dynamic-sky/dynamic_sky.tscn | 40 ++-- dynamic-sky/environment.tres | 14 +- dynamic-sky/scripts/day_night_controller.gd | 74 +++++- dynamic-sky/scripts/dynamic_sky_root.gd | 226 ++++++++++++++---- dynamic-sky/scripts/sky_debug_panel.gd | 74 +++++- dynamic-sky/scripts/vfx_controller.gd | 3 +- dynamic-sky/scripts/weather_vfx.gd | 246 ++++++++++++++++++++ dynamic-sky/scripts/weather_vfx.gd.uid | 1 + scenes/test.tscn | 5 +- scripts/test_camera.gd | 69 ++++++ scripts/test_camera.gd.uid | 1 + 11 files changed, 670 insertions(+), 83 deletions(-) create mode 100644 dynamic-sky/scripts/weather_vfx.gd create mode 100644 dynamic-sky/scripts/weather_vfx.gd.uid create mode 100644 scripts/test_camera.gd create mode 100644 scripts/test_camera.gd.uid diff --git a/dynamic-sky/dynamic_sky.tscn b/dynamic-sky/dynamic_sky.tscn index e5c6160..11a2936 100644 --- a/dynamic-sky/dynamic_sky.tscn +++ b/dynamic-sky/dynamic_sky.tscn @@ -171,27 +171,27 @@ offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1) colors = PackedColorArray(1, 0.6, 0.4, 1, 1, 0.5, 0.3, 1, 1, 1, 0.95, 1, 1, 0.7, 0.4, 1, 1, 1, 1, 1) [sub_resource type="Curve" id="Curve_fm80q"] -_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.2, 0), 0.0, 0.0, 0, 0, Vector2(0.3, 0.8), 0.0, 0.0, 0, 0, Vector2(0.5, 1), 0.0, 0.0, 0, 0, Vector2(0.7, 0.8), 0.0, 0.0, 0, 0, Vector2(0.85, 0), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.2, 0), 0.0, 0.0, 0, 0, Vector2(0.3, 0.55), 0.0, 0.0, 0, 0, Vector2(0.5, 0.7), 0.0, 0.0, 0, 0, Vector2(0.7, 0.55), 0.0, 0.0, 0, 0, Vector2(0.85, 0), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] point_count = 7 [sub_resource type="Gradient" id="Gradient_kcyr7"] colors = PackedColorArray(0.7, 0.8, 1, 1, 0.7, 0.8, 1, 1) [sub_resource type="Curve" id="Curve_qti3v"] -_data = [Vector2(0, 0.4), 0.0, 0.0, 0, 0, Vector2(0.2, 0.3), 0.0, 0.0, 0, 0, Vector2(0.25, 0), 0.0, 0.0, 0, 0, Vector2(0.75, 0), 0.0, 0.0, 0, 0, Vector2(0.85, 0.3), 0.0, 0.0, 0, 0, Vector2(1, 0.4), 0.0, 0.0, 0, 0] +_data = [Vector2(0, 0.4), 0.0, 0.0, 0, 0, Vector2(0.15, 0.3), 0.0, 0.0, 0, 0, Vector2(0.22, 0), 0.0, 0.0, 0, 0, Vector2(0.85, 0), 0.0, 0.0, 0, 0, Vector2(0.92, 0.3), 0.0, 0.0, 0, 0, Vector2(1, 0.4), 0.0, 0.0, 0, 0] point_count = 6 [sub_resource type="Gradient" id="Gradient_bq8yq"] offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1) -colors = PackedColorArray(0.15, 0.18, 0.3, 1, 0.15, 0.18, 0.3, 1, 0.6, 0.65, 0.75, 1, 0.55, 0.5, 0.55, 1, 1, 1, 1, 1) +colors = PackedColorArray(0.08, 0.1, 0.18, 1, 0.5, 0.55, 0.65, 1, 0.6, 0.65, 0.75, 1, 0.55, 0.5, 0.55, 1, 0.08, 0.1, 0.18, 1) [sub_resource type="Curve" id="Curve_x6xf2"] -_data = [Vector2(0, 0.2), 0.0, 0.0, 0, 0, Vector2(0.25, 0.5), 0.0, 0.0, 0, 0, Vector2(0.5, 0.7), 0.0, 0.0, 0, 0, Vector2(0.75, 0.5), 0.0, 0.0, 0, 0, Vector2(1, 0.2), 0.0, 0.0, 0, 0] -point_count = 5 +_data = [Vector2(0, 0.05), 0.0, 0.0, 0, 0, Vector2(0.2, 0.1), 0.0, 0.0, 0, 0, Vector2(0.3, 0.5), 0.0, 0.0, 0, 0, Vector2(0.5, 0.6), 0.0, 0.0, 0, 0, Vector2(0.7, 0.5), 0.0, 0.0, 0, 0, Vector2(0.85, 0.1), 0.0, 0.0, 0, 0, Vector2(1, 0.05), 0.0, 0.0, 0, 0] +point_count = 7 [sub_resource type="Gradient" id="Gradient_tn5up"] offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1) -colors = PackedColorArray(0.1, 0.12, 0.2, 1, 0.1, 0.12, 0.2, 1, 0.7, 0.75, 0.85, 1, 0.7, 0.55, 0.5, 1, 1, 1, 1, 1) +colors = PackedColorArray(0.05, 0.06, 0.1, 1, 0.6, 0.65, 0.75, 1, 0.7, 0.75, 0.85, 1, 0.7, 0.55, 0.5, 1, 0.05, 0.06, 0.1, 1) [sub_resource type="Curve" id="Curve_ej5u1"] _data = [Vector2(0, 0.02), 0.0, 0.0, 0, 0, Vector2(0.25, 0.015), 0.0, 0.0, 0, 0, Vector2(0.5, 0.01), 0.0, 0.0, 0, 0, Vector2(0.75, 0.015), 0.0, 0.0, 0, 0, Vector2(1, 0.02), 0.0, 0.0, 0, 0] @@ -331,7 +331,7 @@ presets = Array[ExtResource("3_c6jpa")]([SubResource("Resource_ajt3x"), SubResou [node name="DayNightController" type="Node" parent="." unique_id=1083149574] script = ExtResource("3_day_night") -time_of_day = 0.31124353179020736 +time_of_day = 0.3159409316050229 current_day = 4 sun_color_curve = SubResource("Gradient_sxvon") sun_intensity_curve = SubResource("Curve_fm80q") @@ -353,10 +353,10 @@ script = ExtResource("5_clouds") layer_configs = Array[ExtResource("4_ssi15")]([SubResource("Resource_xh4oc"), SubResource("Resource_ahi10"), SubResource("Resource_a5xai"), SubResource("Resource_uxs4q")]) global_coverage = 0.2 global_wind_speed = 0.5 -sun_direction = Vector3(0.9166991, 0.37115902, 0.14799932) -sun_color = Color(1, 0.62245756, 0.45919484, 1) -sun_intensity = 0.80182046 -moon_direction = Vector3(0.9225648, -0.37353396, -0.09667771) +sun_direction = Vector3(0.9038672, 0.39738584, 0.15845723) +sun_color = Color(1, 0.63184845, 0.471403, 1) +sun_intensity = 0.80360174 +moon_direction = Vector3(0.91050637, -0.40030476, -0.10360649) moon_intensity = 0.0 [node name="PostProcessController" type="Node" parent="." unique_id=186747041] @@ -367,11 +367,11 @@ sky_top_color = Color(0.2, 0.4, 0.85, 1) sky_horizon_color = Color(0.6, 0.75, 0.95, 1) sky_bottom_color = Color(0.5, 0.6, 0.7, 1) skybox_texture = ExtResource("6_ssi15") -fog_color = Color(0.24694908, 0.27429652, 0.35919484, 1) -fog_density = 0.014247157 -sun_glow_intensity = 0.40091023 -sun_glow_color = Color(1, 0.62245756, 0.45919484, 1) -sun_direction = Vector3(0.9166991, 0.37115902, 0.14799932) +fog_color = Color(0.25821814, 0.28612906, 0.371403, 1) +fog_density = 0.014140322 +sun_glow_intensity = 0.40180087 +sun_glow_color = Color(1, 0.63184845, 0.471403, 1) +sun_direction = Vector3(0.9038672, 0.39738584, 0.15845723) [node name="VFXController" type="Node3D" parent="." unique_id=207251300] script = ExtResource("7_vfx") @@ -386,13 +386,13 @@ environment = ExtResource("13_2w5ig") compositor = SubResource("Compositor_ssi15") [node name="SunLight" type="DirectionalLight3D" parent="." unique_id=1278065589] -transform = Transform3D(0.15938424, -0.98704875, 0.018195797, 0, 0.018431408, 0.99983037, -0.9872164, -0.15935718, 0.0029376775, 0, 50, 0) -light_color = Color(1, 0.62245756, 0.45919484, 1) -light_energy = 0.80182046 +transform = Transform3D(0.17267685, -0.9848151, 0.017930198, 0, 0.018203637, 0.99983454, -0.98497826, -0.17264825, 0.0031433487, 0, 50, 0) +light_color = Color(1, 0.63184845, 0.471403, 1) +light_energy = 0.80360174 shadow_enabled = true [node name="MoonLight" type="DirectionalLight3D" parent="." unique_id=52072807] -transform = Transform3D(-0.10422167, -0.99438065, 0.01858649, 0, 0.018688265, 0.9998256, -0.99455404, 0.1042035, -0.0019477225, 0, 50, 0) +transform = Transform3D(-0.11306036, -0.9934189, 0.018351724, 0, 0.018470151, 0.99982965, -0.9935881, 0.11304111, -0.0020882422, 0, 50, 0) visible = false light_color = Color(0.7, 0.8, 1, 1) light_energy = 0.0 diff --git a/dynamic-sky/environment.tres b/dynamic-sky/environment.tres index 39af921..968eb0d 100644 --- a/dynamic-sky/environment.tres +++ b/dynamic-sky/environment.tres @@ -110,9 +110,9 @@ shader_parameter/sky_bottom_color = Color(0.5, 0.6, 0.7, 1) shader_parameter/gradient_intensity = 1.0 shader_parameter/horizon_blend = 0.5 shader_parameter/sun_glow_enabled = true -shader_parameter/sun_direction = Vector3(0.9166991, 0.37115902, 0.14799932) -shader_parameter/sun_glow_color = Color(1, 0.62245756, 0.45919484, 1) -shader_parameter/sun_glow_intensity = 0.40091023 +shader_parameter/sun_direction = Vector3(0.8560367, 0.48015037, 0.19145952) +shader_parameter/sun_glow_color = Color(1, 0.95, 0.85, 1) +shader_parameter/sun_glow_intensity = 0.3 shader_parameter/sun_glow_size = 0.2 shader_parameter/sun_glow_falloff = 2.0 shader_parameter/dawn_dusk_enabled = true @@ -128,9 +128,9 @@ sky_material = SubResource("ShaderMaterial_jmmec") [resource] background_mode = 2 sky = SubResource("Sky_ssi15") -ambient_light_color = Color(0.26021183, 0.2951101, 0.4102118, 1) -ambient_light_energy = 0.5301137 +ambient_light_color = Color(0.5, 0.6, 0.8, 1) +ambient_light_energy = 0.6 fog_enabled = true -fog_light_color = Color(0.24694908, 0.27429652, 0.35919484, 1) -fog_density = 0.014247157 +fog_light_color = Color(0.7, 0.8, 0.95, 1) +fog_density = 0.005 fog_height_density = 0.1 diff --git a/dynamic-sky/scripts/day_night_controller.gd b/dynamic-sky/scripts/day_night_controller.gd index 466b8b3..5daedf4 100644 --- a/dynamic-sky/scripts/day_night_controller.gd +++ b/dynamic-sky/scripts/day_night_controller.gd @@ -61,6 +61,11 @@ const NOON := 0.5 @export var fog_color_curve: Gradient @export var fog_density_curve: Curve +@export_group("Sky Gradient Curves") +@export var sky_top_color_curve: Gradient +@export var sky_horizon_color_curve: Gradient +@export var sky_bottom_color_curve: Gradient + @export_group("Stars") @export var stars_visibility_curve: Curve @@ -117,10 +122,10 @@ func _initialize_default_curves() -> void: if moon_intensity_curve == null: moon_intensity_curve = Curve.new() moon_intensity_curve.add_point(Vector2(0.0, 0.4)) - moon_intensity_curve.add_point(Vector2(0.2, 0.3)) - moon_intensity_curve.add_point(Vector2(0.25, 0.0)) - moon_intensity_curve.add_point(Vector2(0.75, 0.0)) - moon_intensity_curve.add_point(Vector2(0.85, 0.3)) + moon_intensity_curve.add_point(Vector2(0.15, 0.3)) + moon_intensity_curve.add_point(Vector2(0.22, 0.0)) + moon_intensity_curve.add_point(Vector2(0.85, 0.0)) + moon_intensity_curve.add_point(Vector2(0.92, 0.3)) moon_intensity_curve.add_point(Vector2(1.0, 0.4)) if ambient_color_curve == null: @@ -133,11 +138,13 @@ func _initialize_default_curves() -> void: if ambient_intensity_curve == null: ambient_intensity_curve = Curve.new() - ambient_intensity_curve.add_point(Vector2(0.0, 0.2)) - ambient_intensity_curve.add_point(Vector2(0.25, 0.5)) - ambient_intensity_curve.add_point(Vector2(0.5, 0.7)) - ambient_intensity_curve.add_point(Vector2(0.75, 0.5)) - ambient_intensity_curve.add_point(Vector2(1.0, 0.2)) + ambient_intensity_curve.add_point(Vector2(0.0, 0.05)) + ambient_intensity_curve.add_point(Vector2(0.2, 0.1)) + ambient_intensity_curve.add_point(Vector2(0.3, 0.5)) + ambient_intensity_curve.add_point(Vector2(0.5, 0.6)) + ambient_intensity_curve.add_point(Vector2(0.7, 0.5)) + ambient_intensity_curve.add_point(Vector2(0.85, 0.1)) + ambient_intensity_curve.add_point(Vector2(1.0, 0.05)) if fog_color_curve == null: fog_color_curve = Gradient.new() @@ -155,6 +162,37 @@ func _initialize_default_curves() -> void: fog_density_curve.add_point(Vector2(0.75, 0.015)) fog_density_curve.add_point(Vector2(1.0, 0.02)) + if sky_top_color_curve == null: + sky_top_color_curve = Gradient.new() + sky_top_color_curve.set_color(0, Color(0.01, 0.01, 0.04)) # Midnight + sky_top_color_curve.add_point(0.2, Color(0.03, 0.05, 0.1)) # Pre-dawn + sky_top_color_curve.add_point(0.3, Color(0.18, 0.35, 0.8)) # Morning + sky_top_color_curve.add_point(0.5, Color(0.15, 0.35, 0.82)) # Noon + sky_top_color_curve.add_point(0.75, Color(0.12, 0.15, 0.4)) # Sunset + sky_top_color_curve.add_point(0.85, Color(0.02, 0.02, 0.06)) # Dusk end + sky_top_color_curve.set_color(1, Color(0.01, 0.01, 0.04)) # Midnight + + if sky_horizon_color_curve == null: + sky_horizon_color_curve = Gradient.new() + sky_horizon_color_curve.set_color(0, Color(0.03, 0.04, 0.08)) # Midnight + sky_horizon_color_curve.add_point(0.2, Color(0.3, 0.22, 0.28)) # Pre-dawn + sky_horizon_color_curve.add_point(0.25, Color(0.85, 0.55, 0.45))# Dawn + sky_horizon_color_curve.add_point(0.35, Color(0.45, 0.6, 0.85))# Morning + sky_horizon_color_curve.add_point(0.5, Color(0.5, 0.65, 0.88)) # Noon + sky_horizon_color_curve.add_point(0.75, Color(0.95, 0.45, 0.25))# Sunset + sky_horizon_color_curve.add_point(0.85, Color(0.06, 0.06, 0.1))# Dusk end + sky_horizon_color_curve.set_color(1, Color(0.03, 0.04, 0.08)) # Midnight + + if sky_bottom_color_curve == null: + sky_bottom_color_curve = Gradient.new() + sky_bottom_color_curve.set_color(0, Color(0.02, 0.02, 0.05)) # Midnight + sky_bottom_color_curve.add_point(0.2, Color(0.12, 0.1, 0.15)) # Pre-dawn + sky_bottom_color_curve.add_point(0.3, Color(0.35, 0.45, 0.55)) # Morning + sky_bottom_color_curve.add_point(0.5, Color(0.35, 0.45, 0.55)) # Noon + sky_bottom_color_curve.add_point(0.75, Color(0.3, 0.2, 0.2)) # Sunset + sky_bottom_color_curve.add_point(0.85, Color(0.03, 0.03, 0.06))# Dusk end + sky_bottom_color_curve.set_color(1, Color(0.02, 0.02, 0.05)) # Midnight + if stars_visibility_curve == null: stars_visibility_curve = Curve.new() stars_visibility_curve.add_point(Vector2(0.0, 1.0)) @@ -286,6 +324,24 @@ func get_stars_visibility() -> float: return 0.0 +func get_sky_top_color() -> Color: + if sky_top_color_curve: + return sky_top_color_curve.sample(time_of_day) + return Color(0.2, 0.4, 0.8) + + +func get_sky_horizon_color() -> Color: + if sky_horizon_color_curve: + return sky_horizon_color_curve.sample(time_of_day) + return Color(0.6, 0.7, 0.9) + + +func get_sky_bottom_color() -> Color: + if sky_bottom_color_curve: + return sky_bottom_color_curve.sample(time_of_day) + return Color(0.4, 0.5, 0.6) + + func is_daytime() -> bool: return time_of_day >= SUNRISE_END and time_of_day < SUNSET_START diff --git a/dynamic-sky/scripts/dynamic_sky_root.gd b/dynamic-sky/scripts/dynamic_sky_root.gd index 8a18477..95862e5 100644 --- a/dynamic-sky/scripts/dynamic_sky_root.gd +++ b/dynamic-sky/scripts/dynamic_sky_root.gd @@ -14,6 +14,7 @@ signal preset_changed(preset: SkyPreset) @export var post_process_controller: PostProcessController @export var vfx_controller: VFXController @export var shadow_proxy_system: ShadowProxySystem +@export var weather_vfx: WeatherVFX @export_group("Environment") @export var world_environment: WorldEnvironment @@ -34,6 +35,8 @@ signal preset_changed(preset: SkyPreset) var _current_config: SkyConfig var preset_name: String = "" var _updating_preset_name: bool = false +var _debug_panel: SkyDebugPanel +var _debug_canvas_layer: CanvasLayer func _ready() -> void: @@ -41,9 +44,10 @@ func _ready() -> void: _ensure_controllers_exist() _connect_signals() - #property_list_changed_notify() - _sync_preset_name() _apply_seed() + _ensure_post_process_environment() + _load_initial_preset() + notify_property_list_changed() _sync_all_systems() @@ -52,6 +56,8 @@ func _process(delta: float) -> void: if show_debug_panel: _update_debug_info() + elif _debug_panel != null: + _debug_panel.visible = false func _ensure_controllers_exist() -> void: @@ -90,6 +96,11 @@ func _ensure_controllers_exist() -> void: shadow_proxy_system.name = "ShadowProxySystem" add_child(shadow_proxy_system) + if weather_vfx == null: + weather_vfx = WeatherVFX.new() + weather_vfx.name = "WeatherVFX" + add_child(weather_vfx) + if world_environment == null: _create_default_environment() @@ -178,6 +189,7 @@ func _sync_all_systems() -> void: _sync_clouds() _sync_post_process() _sync_shadows() + _sync_weather_vfx() func _sync_sun_moon() -> void: @@ -187,21 +199,48 @@ func _sync_sun_moon() -> void: var sun_dir := day_night_controller.get_sun_direction() var moon_dir := day_night_controller.get_moon_direction() + var sun_color: Color + var sun_energy: float + var moon_color: Color + var moon_energy: float + var ambient_color: Color + var ambient_energy: float + + if _current_config: + sun_color = _current_config.sun_color + sun_energy = _current_config.sun_intensity + moon_color = _current_config.moon_color + moon_energy = _current_config.moon_intensity + ambient_color = _current_config.ambient_color + ambient_energy = _current_config.ambient_intensity + else: + sun_color = day_night_controller.get_sun_color() + sun_energy = day_night_controller.get_sun_intensity() + moon_color = day_night_controller.get_moon_color() + moon_energy = day_night_controller.get_moon_intensity() + ambient_color = day_night_controller.get_ambient_color() + ambient_energy = day_night_controller.get_ambient_intensity() + if weather_controller: + var wp := weather_controller.get_current_profile() + if wp: + ambient_color *= wp.ambient_color_multiplier + ambient_energy *= wp.ambient_intensity_multiplier + if sun_light: sun_light.look_at(global_position - sun_dir, Vector3.UP) - sun_light.light_color = day_night_controller.get_sun_color() - sun_light.light_energy = day_night_controller.get_sun_intensity() - sun_light.visible = day_night_controller.get_sun_intensity() > 0.01 + sun_light.light_color = sun_color + sun_light.light_energy = sun_energy + sun_light.visible = sun_energy > 0.01 if moon_light: moon_light.look_at(global_position - moon_dir, Vector3.UP) - moon_light.light_color = day_night_controller.get_moon_color() - moon_light.light_energy = day_night_controller.get_moon_intensity() - moon_light.visible = day_night_controller.get_moon_intensity() > 0.01 + moon_light.light_color = moon_color + moon_light.light_energy = moon_energy + moon_light.visible = moon_energy > 0.01 if world_environment and world_environment.environment: - world_environment.environment.ambient_light_color = day_night_controller.get_ambient_color() - world_environment.environment.ambient_light_energy = day_night_controller.get_ambient_intensity() + world_environment.environment.ambient_light_color = ambient_color + world_environment.environment.ambient_light_energy = ambient_energy func _sync_clouds() -> void: @@ -209,19 +248,39 @@ func _sync_clouds() -> void: return if day_night_controller: + var sun_color: Color + var sun_intensity: float + var moon_color: Color + var moon_intensity: float + + if _current_config: + sun_color = _current_config.sun_color + sun_intensity = _current_config.sun_intensity + moon_color = _current_config.moon_color + moon_intensity = _current_config.moon_intensity + else: + sun_color = day_night_controller.get_sun_color() + sun_intensity = day_night_controller.get_sun_intensity() + moon_color = day_night_controller.get_moon_color() + moon_intensity = day_night_controller.get_moon_intensity() + cloud_system.set_sun_lighting( day_night_controller.get_sun_direction(), - day_night_controller.get_sun_color(), - day_night_controller.get_sun_intensity() + sun_color, + sun_intensity ) cloud_system.set_moon_lighting( day_night_controller.get_moon_direction(), - day_night_controller.get_moon_color(), - day_night_controller.get_moon_intensity() + moon_color, + moon_intensity ) if weather_controller: cloud_system.global_coverage = weather_controller.get_cloud_coverage() + elif _current_config: + cloud_system.global_coverage = _current_config.cloud_coverage + + if weather_controller: cloud_system.set_global_wind( weather_controller.get_wind_direction(), weather_controller.get_wind_speed() @@ -233,30 +292,23 @@ func _sync_post_process() -> void: return var sun_dir := day_night_controller.get_sun_direction() - var sun_color := day_night_controller.get_sun_color() - - post_process_controller.set_sun_glow( - sun_dir, - sun_color, - day_night_controller.get_sun_intensity() * 0.5, - 0.2 - ) - - post_process_controller.set_fog_settings( - day_night_controller.get_fog_color(), - day_night_controller.get_fog_density() - ) - - var dawn_dusk_blend := 0.0 - if day_night_controller.is_dawn() or day_night_controller.is_dusk(): - dawn_dusk_blend = 1.0 - post_process_controller.set_dawn_dusk_blend(dawn_dusk_blend) - - post_process_controller.set_stars_visibility( - day_night_controller.get_stars_visibility() - ) if _current_config: + post_process_controller.set_sun_glow( + sun_dir, + _current_config.sun_color, + _current_config.sun_glow_intensity, + _current_config.sun_glow_size + ) + post_process_controller.set_fog_settings( + _current_config.fog_color, + _current_config.fog_density + ) + post_process_controller.set_dawn_dusk_blend(0.0) + post_process_controller.set_stars_visibility( + _current_config.stars_visibility, + _current_config.stars_tint + ) post_process_controller.set_sky_colors( _current_config.sky_top_color, _current_config.sky_horizon_color, @@ -264,6 +316,34 @@ func _sync_post_process() -> void: ) post_process_controller.gradient_intensity = _current_config.gradient_intensity post_process_controller.apply_config(_current_config) + else: + var sun_color := day_night_controller.get_sun_color() + post_process_controller.set_sun_glow( + sun_dir, + sun_color, + day_night_controller.get_sun_intensity() * 0.5, + 0.2 + ) + var fog_color := day_night_controller.get_fog_color() + var fog_density := day_night_controller.get_fog_density() + if weather_controller: + var wp := weather_controller.get_current_profile() + if wp: + fog_color *= wp.fog_color_multiplier + fog_density *= wp.fog_density_multiplier + post_process_controller.set_fog_settings(fog_color, fog_density) + var dawn_dusk_blend := 0.0 + if day_night_controller.is_dawn() or day_night_controller.is_dusk(): + dawn_dusk_blend = 1.0 + post_process_controller.set_dawn_dusk_blend(dawn_dusk_blend) + post_process_controller.set_stars_visibility( + day_night_controller.get_stars_visibility() + ) + post_process_controller.set_sky_colors( + day_night_controller.get_sky_top_color(), + day_night_controller.get_sky_horizon_color(), + day_night_controller.get_sky_bottom_color() + ) func _apply_preset_assets(preset: SkyPreset) -> void: @@ -280,11 +360,20 @@ func _apply_preset_assets(preset: SkyPreset) -> void: cloud_system.rebuild_layers() +func _resolve_preset_library() -> SkyboxPresetLibrary: + if preset_library != null: + return preset_library + if has_node(NodePath("PresetLibrary")): + return get_node(NodePath("PresetLibrary")) as SkyboxPresetLibrary + return null + + func _get_property_list() -> Array: var properties := [] var names: PackedStringArray = [] - if preset_library: - names = preset_library.get_preset_names() + var lib := _resolve_preset_library() + if lib: + names = lib.get_preset_names() var hint_string := "" if names.size() > 0: @@ -307,8 +396,13 @@ func _set(property: StringName, value: Variant) -> bool: preset_name = value if _updating_preset_name: return true - if preset_library and String(preset_name) != "": - preset_library.apply_preset(String(preset_name)) + var lib := _resolve_preset_library() + if lib and String(preset_name) != "": + lib.apply_preset(String(preset_name)) + var p := lib.get_current_preset() + if p: + _on_preset_changed(p) + _sync_all_systems() return true return false @@ -330,6 +424,25 @@ func _sync_preset_name() -> void: _updating_preset_name = false +func _ensure_post_process_environment() -> void: + if post_process_controller and world_environment and world_environment.environment: + if post_process_controller.target_environment == null: + post_process_controller.target_environment = world_environment.environment + post_process_controller._setup_environment() + + +func _load_initial_preset() -> void: + if preset_library == null: + return + var current := preset_library.get_current_preset() + if current == null: + return + _sync_preset_name() + if current.config: + _current_config = current.config + _apply_preset_assets(current) + + func _sync_shadows() -> void: if shadow_proxy_system == null: return @@ -341,16 +454,47 @@ func _sync_shadows() -> void: shadow_proxy_system.sync_with_cloud_system(cloud_system) +func _sync_weather_vfx() -> void: + if weather_vfx == null or weather_controller == null: + return + weather_vfx.set_rain_intensity(weather_controller.get_rain_intensity()) + weather_vfx.set_snow_intensity(weather_controller.get_snow_intensity()) + + func _update_debug_info() -> void: - pass + if Engine.is_editor_hint(): + return + if _debug_panel == null: + _create_debug_panel() + _debug_panel.visible = true + + +func _create_debug_panel() -> void: + _debug_canvas_layer = CanvasLayer.new() + _debug_canvas_layer.name = "DebugCanvasLayer" + _debug_canvas_layer.layer = 100 + add_child(_debug_canvas_layer) + + _debug_panel = SkyDebugPanel.new() + _debug_panel.name = "SkyDebugPanel" + _debug_panel.dynamic_sky = self + _debug_panel.anchor_left = 0.0 + _debug_panel.anchor_top = 0.0 + _debug_panel.anchor_right = 0.0 + _debug_panel.anchor_bottom = 0.0 + _debug_panel.offset_left = 10.0 + _debug_panel.offset_top = 10.0 + _debug_canvas_layer.add_child(_debug_panel) func set_time_of_day(time: float) -> void: + _current_config = null if day_night_controller: day_night_controller.time_of_day = time func set_weather(state: WeatherProfile.WeatherState, transition_time: float = -1.0) -> void: + _current_config = null if weather_controller: weather_controller.set_weather(state, transition_time) diff --git a/dynamic-sky/scripts/sky_debug_panel.gd b/dynamic-sky/scripts/sky_debug_panel.gd index b55c1d1..d3fe26f 100644 --- a/dynamic-sky/scripts/sky_debug_panel.gd +++ b/dynamic-sky/scripts/sky_debug_panel.gd @@ -7,8 +7,15 @@ extends Control var _panel: PanelContainer var _vbox: VBoxContainer var _time_label: Label +var _period_label: Label +var _preset_label: Label var _weather_label: Label +var _sun_label: Label +var _moon_label: Label +var _ambient_label: Label var _cloud_label: Label +var _fog_label: Label +var _rain_label: Label var _seed_label: Label var _time_slider: HSlider var _weather_dropdown: OptionButton @@ -50,10 +57,18 @@ func _create_ui() -> void: _vbox.add_child(HSeparator.new()) + _preset_label = Label.new() + _preset_label.text = "Preset: —" + _vbox.add_child(_preset_label) + _time_label = Label.new() _time_label.text = "Time: 00:00 (0.00)" _vbox.add_child(_time_label) + _period_label = Label.new() + _period_label.text = "Period: —" + _vbox.add_child(_period_label) + var time_hbox := HBoxContainer.new() _vbox.add_child(time_hbox) @@ -76,6 +91,20 @@ func _create_ui() -> void: _vbox.add_child(HSeparator.new()) + _sun_label = Label.new() + _sun_label.text = "Sun: —" + _vbox.add_child(_sun_label) + + _moon_label = Label.new() + _moon_label.text = "Moon: —" + _vbox.add_child(_moon_label) + + _ambient_label = Label.new() + _ambient_label.text = "Ambient: —" + _vbox.add_child(_ambient_label) + + _vbox.add_child(HSeparator.new()) + _weather_label = Label.new() _weather_label.text = "Weather: Clear" _vbox.add_child(_weather_label) @@ -100,8 +129,16 @@ func _create_ui() -> void: _cloud_label.text = "Clouds: 0%" _vbox.add_child(_cloud_label) + _fog_label = Label.new() + _fog_label.text = "Fog: —" + _vbox.add_child(_fog_label) + + _rain_label = Label.new() + _rain_label.text = "Rain: 0% | Snow: 0%" + _vbox.add_child(_rain_label) + _seed_label = Label.new() - _seed_label.text = "Seed: 0" + _seed_label.text = "Seed: 0 | Day: 1" _vbox.add_child(_seed_label) var meteor_button := Button.new() @@ -120,6 +157,8 @@ func _update_labels() -> void: if dynamic_sky == null: return + _preset_label.text = "Preset: %s" % dynamic_sky.preset_name if dynamic_sky.preset_name != "" else "Preset: (none)" + var time := dynamic_sky.get_time_of_day() var hours := int(time * 24.0) var minutes := int(fmod(time * 24.0 * 60.0, 60.0)) @@ -128,6 +167,30 @@ func _update_labels() -> void: if not _time_slider.has_focus(): _time_slider.set_value_no_signal(time) + if dynamic_sky.day_night_controller: + _period_label.text = "Period: %s" % dynamic_sky.day_night_controller.get_time_period().to_upper() + _pause_button.set_pressed_no_signal(dynamic_sky.day_night_controller.paused) + + if dynamic_sky.sun_light: + _sun_label.text = "Sun: %.2f %s" % [ + dynamic_sky.sun_light.light_energy, + "visible" if dynamic_sky.sun_light.visible else "hidden" + ] + + if dynamic_sky.moon_light: + _moon_label.text = "Moon: %.2f %s" % [ + dynamic_sky.moon_light.light_energy, + "visible" if dynamic_sky.moon_light.visible else "hidden" + ] + + if dynamic_sky.world_environment and dynamic_sky.world_environment.environment: + var env := dynamic_sky.world_environment.environment + _ambient_label.text = "Ambient: %.2f" % env.ambient_light_energy + _fog_label.text = "Fog: %s density=%.4f" % [ + "ON" if env.fog_enabled else "OFF", + env.fog_density + ] + var weather := dynamic_sky.get_current_weather() var weather_name: String = WeatherProfile.WeatherState.keys()[weather] _weather_label.text = "Weather: %s" % weather_name @@ -140,10 +203,15 @@ func _update_labels() -> void: var progress := dynamic_sky.weather_controller.get_transition_progress() _weather_label.text += " (transitioning %.0f%%)" % (progress * 100) - _seed_label.text = "Seed: %d" % dynamic_sky.deterministic_seed + if dynamic_sky.weather_controller: + var rain := dynamic_sky.weather_controller.get_rain_intensity() + var snow := dynamic_sky.weather_controller.get_snow_intensity() + _rain_label.text = "Rain: %d%% | Snow: %d%%" % [int(rain * 100), int(snow * 100)] + var day_num := 1 if dynamic_sky.day_night_controller: - _pause_button.set_pressed_no_signal(dynamic_sky.day_night_controller.paused) + day_num = dynamic_sky.day_night_controller.current_day + _seed_label.text = "Seed: %d | Day: %d" % [dynamic_sky.deterministic_seed, day_num] func _on_time_slider_changed(value: float) -> void: diff --git a/dynamic-sky/scripts/vfx_controller.gd b/dynamic-sky/scripts/vfx_controller.gd index e8bbac3..c6bff05 100644 --- a/dynamic-sky/scripts/vfx_controller.gd +++ b/dynamic-sky/scripts/vfx_controller.gd @@ -207,10 +207,9 @@ func _create_star_trail(position: Vector3, direction: Vector3, is_meteor: bool = mesh_instance.material_override = material mesh_instance.position = position + add_child(mesh_instance) mesh_instance.look_at(position + direction, Vector3.UP) mesh_instance.rotate_object_local(Vector3.RIGHT, PI / 2.0) - - add_child(mesh_instance) return mesh_instance diff --git a/dynamic-sky/scripts/weather_vfx.gd b/dynamic-sky/scripts/weather_vfx.gd new file mode 100644 index 0000000..dc6559f --- /dev/null +++ b/dynamic-sky/scripts/weather_vfx.gd @@ -0,0 +1,246 @@ +## Manages rain and snow particle effects driven by the weather controller. +@tool +class_name WeatherVFX +extends Node3D + +@export_group("Rain") +@export var rain_amount: int = 4000 +@export var rain_area: Vector3 = Vector3(80.0, 40.0, 80.0) +@export var rain_speed: float = 20.0 +@export var rain_color: Color = Color(0.7, 0.75, 0.85, 0.4) + +@export_group("Snow") +@export var snow_amount: int = 2000 +@export var snow_area: Vector3 = Vector3(80.0, 30.0, 80.0) +@export var snow_speed: float = 3.0 +@export var snow_color: Color = Color(0.95, 0.95, 1.0, 0.8) + +@export_group("Lightning") +@export var lightning_color: Color = Color(0.9, 0.9, 1.0) +@export var lightning_flash_duration: float = 0.15 + +var _rain_particles: GPUParticles3D +var _snow_particles: GPUParticles3D +var _rain_material: ShaderMaterial +var _snow_material: ShaderMaterial +var _target_rain: float = 0.0 +var _target_snow: float = 0.0 +var _current_rain: float = 0.0 +var _current_snow: float = 0.0 +var _lightning_active: bool = false +var _lightning_frequency: float = 0.0 +var _lightning_timer: float = 0.0 +var _lightning_next: float = 0.0 +var _lightning_flash_timer: float = 0.0 +var _lightning_light: DirectionalLight3D + + +func _ready() -> void: + _create_rain_system() + _create_snow_system() + _create_lightning_light() + + +func _process(delta: float) -> void: + _current_rain = move_toward(_current_rain, _target_rain, delta * 0.5) + _current_snow = move_toward(_current_snow, _target_snow, delta * 0.3) + + _rain_particles.emitting = _current_rain > 0.01 + _rain_particles.amount = maxi(1, int(rain_amount * _current_rain)) + if _rain_material: + _rain_material.set_shader_parameter("alpha_mult", _current_rain) + + _snow_particles.emitting = _current_snow > 0.01 + _snow_particles.amount = maxi(1, int(snow_amount * _current_snow)) + if _snow_material: + _snow_material.set_shader_parameter("alpha_mult", _current_snow) + + _process_lightning(delta) + _follow_camera() + + +func _process_lightning(delta: float) -> void: + if _lightning_flash_timer > 0.0: + _lightning_flash_timer -= delta + if _lightning_light: + var flash := _lightning_flash_timer / lightning_flash_duration + _lightning_light.light_energy = flash * 3.0 + _lightning_light.visible = true + if _lightning_flash_timer <= 0.0 and _lightning_light: + _lightning_light.visible = false + + if not _lightning_active or _lightning_frequency <= 0.0: + return + + _lightning_timer += delta + if _lightning_timer >= _lightning_next: + _lightning_timer = 0.0 + _lightning_next = randf_range(1.0 / maxf(_lightning_frequency, 0.01) * 0.5, 1.0 / maxf(_lightning_frequency, 0.01) * 2.0) + _trigger_lightning() + + +func _trigger_lightning() -> void: + _lightning_flash_timer = lightning_flash_duration + if _lightning_light: + _lightning_light.light_color = lightning_color + _lightning_light.light_energy = 3.0 + _lightning_light.visible = true + _lightning_light.rotation_degrees.x = randf_range(-80.0, -50.0) + _lightning_light.rotation_degrees.y = randf_range(-180.0, 180.0) + + +func _create_lightning_light() -> void: + _lightning_light = DirectionalLight3D.new() + _lightning_light.name = "LightningFlash" + _lightning_light.light_color = lightning_color + _lightning_light.light_energy = 0.0 + _lightning_light.shadow_enabled = false + _lightning_light.visible = false + add_child(_lightning_light) + + +func _follow_camera() -> void: + var vp := get_viewport() + if vp == null: + return + var cam := vp.get_camera_3d() + if cam: + global_position = cam.global_position + + +func set_rain_intensity(intensity: float) -> void: + _target_rain = clampf(intensity, 0.0, 1.0) + + +func set_snow_intensity(intensity: float) -> void: + _target_snow = clampf(intensity, 0.0, 1.0) + + +func set_lightning(active: bool, frequency: float = 0.1) -> void: + _lightning_active = active + _lightning_frequency = frequency + if not active and _lightning_light: + _lightning_light.visible = false + + +func _create_rain_system() -> void: + _rain_particles = GPUParticles3D.new() + _rain_particles.name = "RainParticles" + _rain_particles.amount = rain_amount + _rain_particles.lifetime = rain_area.y / rain_speed + _rain_particles.emitting = false + _rain_particles.visibility_aabb = AABB(-rain_area / 2.0, rain_area) + + var mat := ParticleProcessMaterial.new() + mat.direction = Vector3(0.0, -1.0, 0.0) + mat.spread = 5.0 + mat.initial_velocity_min = rain_speed * 0.8 + mat.initial_velocity_max = rain_speed * 1.2 + mat.gravity = Vector3(0.0, -9.8, 0.0) + mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX + mat.emission_box_extents = Vector3(rain_area.x / 2.0, 0.5, rain_area.z / 2.0) + _rain_particles.process_material = mat + _rain_particles.transform.origin.y = rain_area.y / 2.0 + + _rain_material = ShaderMaterial.new() + _rain_material.shader = _get_rain_shader() + _rain_material.set_shader_parameter("rain_color", rain_color) + _rain_material.set_shader_parameter("alpha_mult", 0.0) + + var mesh := QuadMesh.new() + mesh.size = Vector2(0.02, 0.6) + mesh.material = _rain_material + _rain_particles.draw_pass_1 = mesh + + add_child(_rain_particles) + + +func _create_snow_system() -> void: + _snow_particles = GPUParticles3D.new() + _snow_particles.name = "SnowParticles" + _snow_particles.amount = snow_amount + _snow_particles.lifetime = snow_area.y / snow_speed * 2.5 + _snow_particles.emitting = false + _snow_particles.visibility_aabb = AABB(-snow_area / 2.0, snow_area) + + var mat := ParticleProcessMaterial.new() + mat.direction = Vector3(0.0, -1.0, 0.0) + mat.spread = 15.0 + mat.initial_velocity_min = snow_speed * 0.5 + mat.initial_velocity_max = snow_speed * 1.0 + mat.gravity = Vector3(0.0, -1.0, 0.0) + mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX + mat.emission_box_extents = Vector3(snow_area.x / 2.0, 0.5, snow_area.z / 2.0) + mat.turbulence_enabled = true + mat.turbulence_noise_strength = 2.0 + mat.turbulence_noise_speed_random = 0.5 + mat.turbulence_noise_scale = 4.0 + _snow_particles.process_material = mat + _snow_particles.transform.origin.y = snow_area.y / 2.0 + + _snow_material = ShaderMaterial.new() + _snow_material.shader = _get_snow_shader() + _snow_material.set_shader_parameter("snow_color", snow_color) + _snow_material.set_shader_parameter("alpha_mult", 0.0) + + var mesh := QuadMesh.new() + mesh.size = Vector2(0.08, 0.08) + mesh.material = _snow_material + _snow_particles.draw_pass_1 = mesh + + add_child(_snow_particles) + + +func _get_rain_shader() -> Shader: + var shader := Shader.new() + shader.code = """ +shader_type spatial; +render_mode unshaded, cull_disabled, depth_draw_opaque, blend_mix; + +uniform vec4 rain_color : source_color = vec4(0.7, 0.75, 0.85, 0.4); +uniform float alpha_mult = 1.0; + +void vertex() { + MODELVIEW_MATRIX = VIEW_MATRIX * mat4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + MODEL_MATRIX[3] + ); +} + +void fragment() { + float fade = smoothstep(0.0, 0.3, UV.y) * smoothstep(1.0, 0.7, UV.y); + ALBEDO = rain_color.rgb; + ALPHA = rain_color.a * fade * alpha_mult; +} +""" + return shader + + +func _get_snow_shader() -> Shader: + var shader := Shader.new() + shader.code = """ +shader_type spatial; +render_mode unshaded, cull_disabled, depth_draw_opaque, blend_mix; + +uniform vec4 snow_color : source_color = vec4(0.95, 0.95, 1.0, 0.8); +uniform float alpha_mult = 1.0; + +void vertex() { + MODELVIEW_MATRIX = VIEW_MATRIX * mat4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + MODEL_MATRIX[3] + ); +} + +void fragment() { + float dist = length(UV - vec2(0.5)); + float circle = smoothstep(0.5, 0.3, dist); + ALBEDO = snow_color.rgb; + ALPHA = snow_color.a * circle * alpha_mult; +} +""" + return shader diff --git a/dynamic-sky/scripts/weather_vfx.gd.uid b/dynamic-sky/scripts/weather_vfx.gd.uid new file mode 100644 index 0000000..6c8976b --- /dev/null +++ b/dynamic-sky/scripts/weather_vfx.gd.uid @@ -0,0 +1 @@ +uid://dgyy8274ji8vd diff --git a/scenes/test.tscn b/scenes/test.tscn index 0746cd8..a602970 100644 --- a/scenes/test.tscn +++ b/scenes/test.tscn @@ -1,10 +1,13 @@ [gd_scene format=3 uid="uid://leuf8p24te7a"] [ext_resource type="PackedScene" uid="uid://doiojb8xxlp2s" path="res://dynamic-sky/dynamic_sky.tscn" id="1_errlg"] +[ext_resource type="Script" path="res://scripts/test_camera.gd" id="2_testcam"] [node name="Node3D" type="Node3D" unique_id=973524329] [node name="DynamicSky" parent="." unique_id=516593260 instance=ExtResource("1_errlg")] +deterministic_seed = 1 [node name="Camera3D" type="Camera3D" parent="." unique_id=2106785413] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 74.42207, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 77.45268, 138.60399) +script = ExtResource("2_testcam") diff --git a/scripts/test_camera.gd b/scripts/test_camera.gd new file mode 100644 index 0000000..a31ca94 --- /dev/null +++ b/scripts/test_camera.gd @@ -0,0 +1,69 @@ +## Free-look camera controller for the test scene. +## WASD to move, mouse drag (right-click) to look around, scroll to change speed. +extends Camera3D + +@export_group("Movement") +@export var move_speed: float = 20.0 +@export var fast_multiplier: float = 3.0 +@export var smooth_factor: float = 10.0 + +@export_group("Mouse Look") +@export var mouse_sensitivity: float = 0.003 +@export var pitch_limit: float = 89.0 + +var _yaw: float = 0.0 +var _pitch: float = 0.0 +var _looking: bool = false +var _velocity: Vector3 = Vector3.ZERO + + +func _ready() -> void: + var euler := rotation + _yaw = euler.y + _pitch = euler.x + + +func _unhandled_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mb := event as InputEventMouseButton + if mb.button_index == MOUSE_BUTTON_RIGHT: + _looking = mb.pressed + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if _looking else Input.MOUSE_MODE_VISIBLE + if mb.button_index == MOUSE_BUTTON_WHEEL_UP: + move_speed = minf(move_speed * 1.2, 500.0) + if mb.button_index == MOUSE_BUTTON_WHEEL_DOWN: + move_speed = maxf(move_speed / 1.2, 1.0) + + if event is InputEventMouseMotion and _looking: + var motion := event as InputEventMouseMotion + _yaw -= motion.relative.x * mouse_sensitivity + _pitch -= motion.relative.y * mouse_sensitivity + _pitch = clampf(_pitch, deg_to_rad(-pitch_limit), deg_to_rad(pitch_limit)) + + +func _process(delta: float) -> void: + rotation = Vector3(_pitch, _yaw, 0.0) + + var input_dir := Vector3.ZERO + if Input.is_physical_key_pressed(KEY_W): + input_dir.z -= 1.0 + if Input.is_physical_key_pressed(KEY_S): + input_dir.z += 1.0 + if Input.is_physical_key_pressed(KEY_A): + input_dir.x -= 1.0 + if Input.is_physical_key_pressed(KEY_D): + input_dir.x += 1.0 + if Input.is_physical_key_pressed(KEY_E) or Input.is_physical_key_pressed(KEY_SPACE): + input_dir.y += 1.0 + if Input.is_physical_key_pressed(KEY_Q) or Input.is_physical_key_pressed(KEY_CTRL): + input_dir.y -= 1.0 + + input_dir = input_dir.normalized() + + var speed := move_speed + if Input.is_physical_key_pressed(KEY_SHIFT): + speed *= fast_multiplier + + var target_velocity := (global_transform.basis * input_dir) * speed + _velocity = _velocity.lerp(target_velocity, clampf(smooth_factor * delta, 0.0, 1.0)) + global_position += _velocity * delta diff --git a/scripts/test_camera.gd.uid b/scripts/test_camera.gd.uid new file mode 100644 index 0000000..b6dfab2 --- /dev/null +++ b/scripts/test_camera.gd.uid @@ -0,0 +1 @@ +uid://dmukn37wykx0