class_name EnvironmentManagerRoot extends Node3D const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres") const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres") const WEATHER_PLAIN_SHADER: Material = preload("res://core/daynight/weather_plain_shader.tres") @export var environment_config: EnvironmentConfig @export_group("Camera") @export var camera: Camera3D @export var camera_pivot: Node3D @export_group("Particles") @export var particles_wind: GPUParticles3D @export var particles_snow: GPUParticles3D @export var particles_fireflies: GPUParticles3D @export var particles_rain: GPUParticles3D @export var godray: PackedScene @export var environment_dust: ColorRect @export var blur: ColorRect @export var environment_shadows: MeshInstance3D @export_group("Sound") @export var thunder_sounds: Array[AudioStream] @export var rain_sounds: Array[AudioStream] #environment nodes @onready var sun: DirectionalLight3D = $"../DirectionalLight3D" @onready var environment: WorldEnvironment = $"../WorldEnvironment" @onready var fog_node: Node3D = $Fog var day_night_controller: DayNightController var weather_controller: WeatherController var day_tween: Tween var day_time: float = 0.0 func _ready() -> void: #connect shader when new node is added get_tree().node_added.connect(_on_tree_node_added) UIEvents.toggle_fog.connect(toggle_fog) #set noise texture 2d to materials in special group ApplyWindNoiseToMaterials() #set weather overlay (snow + rain) to materials in special group ApplyWeatherShaderToMaterials() if day_night_controller == null: day_night_controller = DayNightController.new( environment_config ) day_night_controller.name = "DayNightController" day_night_controller.time_changed.connect(select_day_time) add_child(day_night_controller) if weather_controller == null: weather_controller = WeatherController.new( particles_wind, particles_snow, particles_fireflies, particles_rain, godray, environment_dust, blur, environment_shadows, sun, environment, environment_config, camera, camera_pivot, thunder_sounds, rain_sounds ) weather_controller.name = "WeatherController" add_child(weather_controller) func _on_tree_node_added(node: Node) -> void: if node.is_in_group("wind_node") or node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"): call_deferred("_apply_dynamic_environment_materials", node) func _apply_dynamic_environment_materials(node: Node) -> void: if not is_instance_valid(node): return if node.is_in_group("wind_node"): _apply_wind_noise_to_node(node, NOISE_TEXTURE) if node.is_in_group("weather_node"): _apply_weather_overlay_to_node(node, WEATHER_SHADER) if node.is_in_group("weather_vegetables_node"): _apply_weather_overlay_to_node(node, WEATHER_PLAIN_SHADER) func ApplyWindNoiseToMaterials(): for node in get_tree().get_nodes_in_group("wind_node"): _apply_wind_noise_to_node(node, NOISE_TEXTURE) func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void: if node is GeometryInstance3D: var material_override := node.material_override as ShaderMaterial if material_override: material_override.set_shader_parameter("wind_noise", noise_tex) if node is MeshInstance3D: for surface_index in node.get_surface_override_material_count(): var surface_material := node.get_surface_override_material(surface_index) as ShaderMaterial if surface_material: surface_material.set_shader_parameter("wind_noise", noise_tex) for child in node.get_children(): _apply_wind_noise_to_node(child, noise_tex) func ApplyWeatherShaderToMaterials(): for node in get_tree().get_nodes_in_group("weather_node"): _apply_weather_overlay_to_node(node, WEATHER_SHADER) for node in get_tree().get_nodes_in_group("weather_vegetables_node"): _apply_weather_overlay_to_node(node, WEATHER_PLAIN_SHADER) func _apply_weather_overlay_to_node(node: Node, material: Material) -> void: if node is GeometryInstance3D: node.material_overlay = material for child in node.get_children(): _apply_weather_overlay_to_node(child, material) func select_day_time(normalized_time: float) -> void: #set show_day_time_debug = true to show debug on screen #normalized_time is a value between 0 and 1; the time of the day is calculate as "normalized_time" * 1440; day_time is the step to pass from sunrise, to day, to sunset, to night #e.g. #Time normalized_time day_time #00:00 0.0000 3.0 #06:00 0.2500 1.0 #09:36 0.4000 1.75 #10:48 0.4500 2.0 #12:00 0.5000 2.0 #19:12 0.8000 2.0 #21:36 0.9000 2.5 #24:00 1.0000 3.0 if normalized_time < environment_config.sunrise: #Sunrise: night → morning day_time = 3.0 - (normalized_time / environment_config.sunrise) * 2.0 # 3.0 → 1.0 elif normalized_time < environment_config.day: #Morning: morning → afternoon var t: float = (normalized_time - environment_config.sunrise) / (environment_config.day - environment_config.sunrise) day_time = 1.0 + t # 1.0 → 2.0 elif normalized_time < environment_config.sunset: #Broad daylight day_time = 2.0 elif normalized_time < environment_config.night: #Sunset: afternoon → night var t: float = (normalized_time - environment_config.sunset) / (environment_config.night - environment_config.sunset) day_time = 2.0 + t # 2.0 → 3.0 else: #Night day_time = 3.0 if weather_controller: weather_controller.day_time = day_time func toggle_fog(value: bool) -> void: if fog_node: fog_node.visible = value