class_name WeatherController extends Node var thunder_audio_player: AudioStreamPlayer var thunder_sounds: Array[AudioStream] var rain_sounds: Array[AudioStream] var rain_audio_player: AudioStreamPlayer var particles_wind: GPUParticles3D var particles_snow: GPUParticles3D var particles_fireflies: GPUParticles3D var particles_rain: GPUParticles3D var godray: PackedScene var environment_dust: ColorRect var blur: ColorRect var environment_shadows: MeshInstance3D var sun: DirectionalLight3D var environment: WorldEnvironment var environment_config: EnvironmentConfig var camera: Camera3D var day_time: float = 0.0 var lightning_sprite: Sprite3D var lightning_light = DirectionalLight3D.new(); var timer_next_lightning: float = 0.0 var is_raining: bool = false var rain_intensity: float = 0.0 var rain_tween: Tween var rain_audio_tween: Tween var puddle_tween: Tween var puddle_amount: float = 0.0 var snow_tween: Tween var snow_particles_tween: Tween var is_snowing: bool = false var is_snow_accumulated: bool = false var actual_snow_amount: float = 0.0 var is_storm: bool = false var cold_tween: Tween var is_windy: bool = false var thereare_fireflies: bool = false func _init(wind: GPUParticles3D, snow: GPUParticles3D, fireflies: GPUParticles3D, rain: GPUParticles3D, ray: PackedScene, dust: ColorRect, blurr: ColorRect, envshadows: MeshInstance3D, thesun: DirectionalLight3D, theenvironment: WorldEnvironment, environmentconfig: EnvironmentConfig, thecamera: Camera3D, thundersounds: Array[AudioStream], rainsounds: Array[AudioStream]) -> void: particles_wind = wind particles_snow = snow particles_fireflies = fireflies particles_rain = rain godray = ray sun = thesun environment_dust = dust blur = blurr environment_shadows = envshadows environment = theenvironment environment_config = environmentconfig camera = thecamera thunder_sounds = thundersounds rain_sounds = rainsounds set_rain() set_fireflies() set_wind() set_snow() set_lightning() create_sound_players() func _ready() -> void: #connect events UIEvents.toggle_rain.connect(toggle_rain) UIEvents.toggle_snow.connect(toggle_snow) UIEvents.toggle_wind.connect(toggle_wind) UIEvents.toggle_fireflies.connect(toggle_fireflies) UIEvents.toggle_storm.connect(toggle_storm) UIEvents.toggle_dust.connect(toggle_dust) UIEvents.toggle_blur.connect(toggle_blur) UIEvents.toggle_shadows.connect(toggle_shadows) func _process(delta: float) -> void: _follow_camera() var is_night = day_time >= 2.5 if particles_fireflies: particles_fireflies.emitting = thereare_fireflies and is_night var base_tint: Color var base_sky_top: Color var base_sky_horizon: Color var base_fog_color: Color var base_fog_density: float var base_bloom: float var base_exposure: float var base_grad_top: Color var base_grad_bot: Color var base_grad_intensity: float var base_rotation_sun: Vector3 if is_raining: var target_rain = environment_config.storm_rain_intensity_multiplier if is_storm else 1.0 rain_intensity = move_toward(rain_intensity, target_rain, delta * 0.5) if is_storm: timer_next_lightning -= delta if timer_next_lightning <= 0.0: start_lightning() set_lightning_timer() else: rain_intensity = move_toward(rain_intensity, 0.0, delta * 0.5) RenderingServer.global_shader_parameter_set("global_rain_intensity", rain_intensity) if day_time <= 2.0: var t = day_time - 1.0 base_tint = environment_config.morning_color.lerp(environment_config.afternoon_color, t) base_sky_top = environment_config.sky_top_morning.lerp(environment_config.sky_top_afternoon, t) base_sky_horizon = environment_config.sky_horizon_morning.lerp(environment_config.sky_horizon_afternoon, t) base_fog_color = environment_config.fog_color_morning.lerp(environment_config.fog_color_afternoon, t) base_fog_density = lerp(environment_config.fog_density_morning, environment_config.fog_density_afternoon, t) base_bloom = lerp(environment_config.glow_morning, environment_config.glow_afternoon, t) base_exposure = lerp(environment_config.exposure_morning, environment_config.exposure_afternoon, t) base_grad_top = environment_config.grad_top_morning.lerp(environment_config.grad_top_afternoon, t) base_grad_bot = environment_config.grad_top_morning.lerp(environment_config.grad_bot_afternoon, t) base_grad_intensity = lerp(environment_config.grad_intensity_morning, environment_config.grad_intensity_afternoon, t) base_rotation_sun = environment_config.sun_rotation_morning.lerp(environment_config.sun_rotation_afternoon, t) else: var t = day_time - 2.0 base_tint = environment_config.afternoon_color.lerp(environment_config.night_color, t) base_sky_top = environment_config.sky_top_afternoon.lerp(environment_config.sky_top_night, t) base_sky_horizon = environment_config.sky_horizon_afternoon.lerp(environment_config.sky_horizon_night, t) base_fog_color = environment_config.fog_color_afternoon.lerp(environment_config.fog_color_night, t) base_fog_density = lerp(environment_config.fog_density_afternoon, environment_config.fog_density_night, t) base_bloom = lerp(environment_config.glow_afternoon, environment_config.glow_night, t) base_exposure = lerp(environment_config.exposure_afternoon, environment_config.exposure_night, t) base_grad_top = environment_config.grad_top_afternoon.lerp(environment_config.grad_top_night, t) base_grad_bot = environment_config.grad_bot_afternoon.lerp(environment_config.grad_bot_night, t) base_grad_intensity = lerp(environment_config.grad_intensity_afternoon, environment_config.grad_intensity_night, t) base_rotation_sun = environment_config.sun_rotation_afternoon.lerp(environment_config.sun_rotation_night, t) var weather_color = environment_config.rain_mode_color if is_storm: var storm_blend = clamp((rain_intensity - 1.0) / (environment_config.storm_rain_intensity_multiplier - 1.0), 0.0, 1.0) weather_color = weather_color.lerp(environment_config.storm_mode_color, storm_blend) var final_tint = base_tint.lerp(base_tint * weather_color, clamp(rain_intensity, 0.0, 1.0)) var final_sky_top = base_sky_top.lerp(base_sky_top * weather_color, clamp(rain_intensity, 0.0, 1.0)) var final_sky_horizon = base_sky_horizon.lerp(base_sky_horizon * weather_color, clamp(rain_intensity, 0.0, 1.0)) var final_fog_color = base_fog_color.lerp(base_fog_color * weather_color, clamp(rain_intensity, 0.0, 1.0)) var final_fog_density = lerp(base_fog_density, base_fog_density * 4.0, clamp(rain_intensity, 0.0, 1.0)) final_tint = final_tint.lerp(final_tint * environment_config.snow_mode_color, actual_snow_amount) final_sky_top = final_sky_top.lerp(final_sky_top * environment_config.snow_mode_color, actual_snow_amount) final_sky_horizon = final_sky_horizon.lerp(final_sky_horizon * environment_config.snow_mode_color, actual_snow_amount) final_fog_color = final_fog_color.lerp(final_fog_color * environment_config.snow_mode_color, actual_snow_amount) #Shader parameters for global trunk_shader var final_grad_top = base_grad_top.lerp(base_grad_top * weather_color, clamp(rain_intensity, 0.0, 1.0)) var final_grad_bot = base_grad_bot.lerp(base_grad_bot * weather_color, clamp(rain_intensity, 0.0, 1.0)) RenderingServer.global_shader_parameter_set("global_gradient_color_top", final_grad_top) RenderingServer.global_shader_parameter_set("global_gradient_color_bot", final_grad_bot) RenderingServer.global_shader_parameter_set("global_gradient_intensity", base_grad_intensity) var night_val = clamp(day_time - 2.0, 0.0, 1.0) #Snow exposure compensation var snow_light_attenuation = lerp(1.0, 0.55, actual_snow_amount * (1.0 - night_val)) var snow_glow_attenuation = lerp(1.0, 0.5, actual_snow_amount) var final_bloom = lerp(base_bloom, base_bloom * 0.5, rain_intensity) * snow_glow_attenuation # We calculate the final exposure by applying snow damping directly to the camera exposure var final_exposure = base_exposure * snow_light_attenuation var reflected_color = Color(0.4, 0.5, 0.6, 1.0) var drops_color = final_sky_horizon.lerp(reflected_color, 0.15) drops_color = drops_color.lerp(drops_color * 0.4, night_val) if environment_config.material_drops: environment_config.material_drops.albedo_color = drops_color if sun: sun.light_color = final_tint sun.rotation_degrees = base_rotation_sun var target_energy = lerp(1.0, environment_config.night_light_energy, night_val) # Sunlight scales based on weather/night sun.light_energy = lerp(target_energy, target_energy * 0.4, rain_intensity) if environment and environment.environment: environment.environment.ambient_light_color = final_tint # Apply fog environment.environment.fog_enabled = true environment.environment.fog_light_color = final_fog_color environment.environment.fog_density = final_fog_density # Apply glow environment.environment.glow_enabled = true environment.environment.glow_intensity = final_bloom environment.environment.tonemap_exposure = final_exposure var sky_mat = environment.environment.sky.sky_material as ShaderMaterial if sky_mat: sky_mat.set_shader_parameter("sky_top_color", final_sky_top) sky_mat.set_shader_parameter("sky_horizon_color", final_sky_horizon) sky_mat.set_shader_parameter("sun_color", final_tint) sky_mat.set_shader_parameter("night_intensity", night_val) if environment_config.material_clouds: var current_density = lerp(0.4, 1.0, rain_intensity) environment_config.material_clouds.set_shader_parameter("cloud_density", current_density) var current_sharpness = lerp(0.14, 0.0, rain_intensity) environment_config.material_clouds.set_shader_parameter("center_sharpness", current_sharpness) if environment_config.material_fog: environment_config.material_fog.set_shader_parameter("night_intensity", night_val) environment_config.material_fog.set_shader_parameter("sun_color", final_tint) func _follow_camera() -> void: if not camera or not is_instance_valid(camera): return var cam_pos: Vector3 = camera.global_position if particles_rain: particles_rain.global_position = Vector3(cam_pos.x, particles_rain.position.y, cam_pos.z) if particles_snow: particles_snow.global_position = Vector3(cam_pos.x, particles_snow.position.y, cam_pos.z) if particles_wind: particles_wind.global_position = Vector3(cam_pos.x, cam_pos.y, cam_pos.z) if particles_fireflies: particles_fireflies.global_position = Vector3(cam_pos.x, particles_fireflies.position.y, cam_pos.z) func create_sound_players(): rain_audio_player = AudioStreamPlayer.new() rain_audio_player.name = "RainAudioPlayer" rain_audio_player.volume_db = environment_config.rain_audio_volume_db rain_audio_player.finished.connect(_on_rain_audio_player_finished) add_child(rain_audio_player) thunder_audio_player = AudioStreamPlayer.new() thunder_audio_player.name = "ThunderAudioPlayer" thunder_audio_player.volume_db = environment_config.thunder_audio_volume_db add_child(thunder_audio_player) #region Fireflies func toggle_fireflies(value: bool): thereare_fireflies = value var is_night = day_time >= 2.5 if particles_fireflies: particles_fireflies.emitting = thereare_fireflies and is_night #disable fireflies and set default values and materials func set_fireflies(): if particles_fireflies: particles_fireflies.visible = true particles_fireflies.emitting = false particles_fireflies.amount = environment_config.fireflies_amount var proc_mat = particles_fireflies.process_material as ParticleProcessMaterial if proc_mat: proc_mat.emission_box_extents = Vector3(environment_config.fireflies_spawn_ray, environment_config.fireflies_spawn_height, environment_config.fireflies_spawn_ray) #endregion #region Wind func toggle_wind(value: bool): is_windy = value if particles_wind: particles_wind.emitting = is_windy #disable wind and set default values and materials func set_wind(): if particles_wind: particles_wind.visible = true particles_wind.emitting = false particles_wind.amount = environment_config.wind_amount var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial if proc_mat_wind: proc_mat_wind.emission_box_extents = Vector3(environment_config.wind_spawn_ray, environment_config.wind_spawn_height, environment_config.wind_spawn_ray) if environment_config: _apply_cloud_config() _apply_wind_config() func _apply_wind_config() -> void: RenderingServer.global_shader_parameter_set("global_wind_speed", environment_config.wind_speed) RenderingServer.global_shader_parameter_set("global_wind_direction", environment_config.wind_direction) RenderingServer.global_shader_parameter_set("global_wind_scale", environment_config.wind_scale) RenderingServer.global_shader_parameter_set("global_wind_strength", environment_config.wind_strength) func _apply_cloud_config() -> void: var dir = environment_config.wind_direction var cloud_scale = environment_config.cloud_scale var cloud_speed = environment_config.cloud_speed var envshadows_scale_rate = environment_config.cloud_scale_envshadows_rate var envshadows_speed_rate = environment_config.cloud_speed_envshadows_rate if environment and environment.environment and environment.environment.sky: var sky_mat = environment.environment.sky.sky_material as ShaderMaterial if sky_mat: sky_mat.set_shader_parameter("cloud_direction", dir) sky_mat.set_shader_parameter("cloud_scale", cloud_scale) sky_mat.set_shader_parameter("cloud_speed", cloud_speed) var env_shadow_mat = environment_shadows.get_surface_override_material(0) if environment_shadows else null if env_shadow_mat: env_shadow_mat.set_shader_parameter("direction", dir) env_shadow_mat.set_shader_parameter("cloud_scale", cloud_scale * envshadows_scale_rate) env_shadow_mat.set_shader_parameter("cloud_speed", cloud_speed * envshadows_speed_rate) if environment_config.material_clouds: environment_config.material_clouds.set_shader_parameter("direction", dir) environment_config.material_clouds.set_shader_parameter("cloud_scale", cloud_scale * envshadows_scale_rate) environment_config.material_clouds.set_shader_parameter("cloud_speed", cloud_speed * envshadows_speed_rate) #endregion #region Lightning func toggle_storm(value: bool): is_storm = value #set lightning func set_lightning(): lightning_light.name = "Lightning Light" lightning_light.transform = Transform3D( Vector3(-0.6238797, -0.342596, 0.7024259), # asse X Vector3(0.0, 0.89879405, 0.43837115), # asse Y Vector3(-0.7815204, 0.27349085, -0.56073934), # asse Z Vector3(0.0, 0.0, 30.411049) # origine ) lightning_light.light_color = Color(0.8, 1.0, 1.0, 1.0) lightning_light.light_energy = 0.0 lightning_light.shadow_enabled = true add_child(lightning_light) create_lightning_sprite() set_lightning_timer() func create_lightning_sprite(): lightning_sprite = Sprite3D.new() lightning_sprite.name = "Lightning Sprite" lightning_sprite.transform = Transform3D( Basis( Vector3(4, 0, 0), Vector3(0, 7.32, 0), Vector3(0, 0, 7.3) ), Vector3(0, 17.324, 30.411049) ) lightning_sprite.modulate = Color(1, 1, 1, 0) lightning_sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED lightning_sprite.alpha_cut = SpriteBase3D.ALPHA_CUT_DISCARD #Add Texture lightning_sprite.texture = load(environment_config.lightning_texture) add_child(lightning_sprite) #set next lightning timer func set_lightning_timer(): timer_next_lightning = randf_range(environment_config.lightning_min_time, environment_config.lightning_max_time) func start_lightning(): if lightning_light: var tween_lighting = create_tween() lightning_light.light_energy = 5.0 tween_lighting.tween_interval(0.05) tween_lighting.tween_callback(func(): lightning_light.light_energy = 0.0) tween_lighting.tween_interval(0.05) tween_lighting.tween_callback(func(): lightning_light.light_energy = 2.0) tween_lighting.tween_method(func(val): lightning_light.light_energy = val, 2.0, 0.0, 0.2) var lightning_number = randi_range(environment_config.lightning_min, environment_config.lightning_max) for i in lightning_number: if lightning_sprite: var random_angle = randf() * TAU var distance = randf_range(10.0, 40.0) lightning_sprite.position = Vector3(cos(random_angle) * distance, 10.0, sin(random_angle) * distance) lightning_sprite.modulate = environment_config.lightning_color lightning_sprite.modulate.a = 1.0 var s = randf_range(environment_config.lightning_scale_min, environment_config.lightning_scale_max) lightning_sprite.scale = Vector3(s, s, s) var tween_sprite = create_tween() tween_sprite.tween_interval(0.2) tween_sprite.tween_property(lightning_sprite, "modulate:a", 0.0, 0.4) #Thunder sound; separated routine to do not stop the loop _play_thunder_delayed(randf_range(0.5, 2.5)) if lightning_number > 1: await get_tree().create_timer(randf_range(0.05, 0.15)).timeout func _play_thunder_delayed(delay: float): await get_tree().create_timer(delay).timeout if thunder_sounds.is_empty() or not thunder_audio_player: return thunder_audio_player.stream = thunder_sounds[randi() % thunder_sounds.size()] thunder_audio_player.pitch_scale = randf_range(0.9, 1.1) thunder_audio_player.play() #endregion #region Rain #disable rain func set_rain(): if particles_rain: particles_rain.visible = true particles_rain.emitting = false if rain_audio_player: rain_audio_player.volume_db = environment_config.rain_audio_volume_db rain_audio_player.stop() RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", 0.0) func set_puddle_amount(value: float): puddle_amount = value RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", value) func toggle_rain(value: bool): is_raining = value if not particles_rain: return if is_raining and is_snowing: toggle_snow(false) if rain_tween and rain_tween.is_valid(): rain_tween.kill() if puddle_tween and puddle_tween.is_valid(): puddle_tween.kill() if is_raining: particles_rain.amount_ratio = 0.0 particles_rain.emitting = true if rain_audio_player: if not rain_audio_player.playing: rain_audio_player.volume_db = -80.0 _play_random_rain_sound() _fade_rain_audio(environment_config.rain_audio_volume_db, false) rain_tween = create_tween() rain_tween.tween_property(particles_rain, "amount_ratio", 1.0, environment_config.rain_fade_time) puddle_tween = create_tween() puddle_tween.tween_method(set_puddle_amount, puddle_amount, 1.0, environment_config.puddle_form_time) else: if rain_audio_player: _fade_rain_audio(-80.0, true) rain_tween = create_tween() rain_tween.tween_property(particles_rain, "amount_ratio", 0.0, environment_config.rain_fade_time) rain_tween.tween_callback(func(): particles_rain.emitting = false trigger_after_rain()) puddle_tween = create_tween() puddle_tween.tween_method(set_puddle_amount, puddle_amount, 0.0, environment_config.puddle_dry_time) func trigger_morning() -> void: spawn_single_godray() func trigger_after_rain() -> void: if day_time < 2.5: var count: int = randi_range(1, environment_config.godray_max_rain) for i in count: spawn_single_godray() func spawn_single_godray() -> void: var ray: Node3D = godray.instantiate() get_tree().current_scene.add_child(ray) var random_x: float = randf_range(-environment_config.godray_spawn_radius, environment_config.godray_spawn_radius) var random_z: float = randf_range(-environment_config.godray_spawn_radius, environment_config.godray_spawn_radius) var center := Vector3.ZERO if camera and is_instance_valid(camera): center = camera.global_position ray.global_position = Vector3(center.x + random_x, environment_config.godray_spawn_height, center.z + random_z) if ray.has_method("set_visual_rotation_degrees"): ray.call("set_visual_rotation_degrees", environment_config.godray_rotation_degrees) else: ray.rotation_degrees = environment_config.godray_rotation_degrees if ray.has_method("set_visual_scale"): ray.call("set_visual_scale", environment_config.godray_scale) else: ray.scale = environment_config.godray_scale if ray.has_method("orient_to_camera"): ray.call("orient_to_camera") var godray_tween: Tween = create_tween() godray_tween.tween_interval(2.0) godray_tween.tween_property(ray, "scale", Vector3.ZERO, 2.0).set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_SINE) godray_tween.tween_callback(ray.queue_free) func _play_random_rain_sound() -> void: if rain_sounds.is_empty() or rain_audio_player == null: return rain_audio_player.stream = rain_sounds[randi() % rain_sounds.size()] rain_audio_player.pitch_scale = 1.0 rain_audio_player.volume_db = minf(rain_audio_player.volume_db, environment_config.rain_audio_volume_db) rain_audio_player.play() func _on_rain_audio_player_finished() -> void: if is_raining: _play_random_rain_sound() func _fade_rain_audio(target_volume_db: float, stop_after_fade: bool) -> void: if rain_audio_player == null: return if rain_audio_tween and rain_audio_tween.is_valid(): rain_audio_tween.kill() rain_audio_tween = create_tween() rain_audio_tween.tween_property( rain_audio_player, "volume_db", target_volume_db, environment_config.rain_fade_time #same value of the rain particles ) if stop_after_fade: rain_audio_tween.tween_callback(func(): rain_audio_player.stop()) #endregion #region Snow func toggle_snow(value: bool): is_snowing = value if is_snowing and is_raining: toggle_rain(false) # Fade particelle neve if particles_snow: if snow_particles_tween and snow_particles_tween.is_valid(): snow_particles_tween.kill() if is_snowing: particles_snow.amount_ratio = 0.0 particles_snow.visible = true particles_snow.emitting = true snow_particles_tween = create_tween() snow_particles_tween.tween_property(particles_snow, "amount_ratio", 1.0, environment_config.snow_fade_time) start_snow_accumulation() else: snow_particles_tween = create_tween() snow_particles_tween.tween_property(particles_snow, "amount_ratio", 0.0, environment_config.snow_fade_time) snow_particles_tween.tween_callback(func(): particles_snow.emitting = false) start_snow_melt() var target_snow_amount: float = 1.0 if is_snowing else 0.0 if snow_tween and snow_tween.is_valid(): snow_tween.kill() snow_tween = create_tween() snow_tween.tween_method(set_snow_amount, actual_snow_amount, target_snow_amount, environment_config.snow_transaction_time) #disable snow and set default values and shader func set_snow(value: float = 0.0): actual_snow_amount = value RenderingServer.global_shader_parameter_set("global_snow_amount", value) if particles_snow: particles_snow.emitting = false if environment_config: RenderingServer.global_shader_parameter_set("global_snow_threshold", environment_config.snow_threshold) RenderingServer.global_shader_parameter_set("global_snow_color", environment_config.snow_color) RenderingServer.global_shader_parameter_set("global_snow_accumulation_speed", environment_config.snow_accumulation_speed) RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed) RenderingServer.global_shader_parameter_set("global_snow_start_time", -1.0) RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0) func set_snow_amount(value: float): actual_snow_amount = value RenderingServer.global_shader_parameter_set("global_snow_amount", value) func start_snow_accumulation() -> void: RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0) RenderingServer.global_shader_parameter_set("global_snow_start_time", Time.get_ticks_msec() / 1000.0) func start_snow_melt() -> void: RenderingServer.global_shader_parameter_set("global_snow_melt_time", Time.get_ticks_msec() / 1000.0) RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed) #endregion #region Post-Process func toggle_dust(value: bool) -> void: if environment_dust: environment_dust.visible = value func toggle_blur(value: bool) -> void: if blur: blur.visible = value ApplyPostProcessBlurConfig() func toggle_shadows(value: bool) -> void: if environment_shadows: environment_shadows.visible = value func ApplyPostProcessBlurConfig() -> void: if blur == null: return var blur_material := blur.material as ShaderMaterial if blur_material: blur_material.set_shader_parameter("blur_amount", environment_config.blur_amount) #endregion