172 lines
6.1 KiB
GDScript
172 lines
6.1 KiB
GDScript
extends Node
|
|
|
|
@export var cycle_duration_seconds := 240.0
|
|
@export_range(0.0, 1.0, 0.01) var initial_cycle := 0.25
|
|
@export var directional_light: DirectionalLight3D
|
|
@export var world_environment: WorldEnvironment
|
|
|
|
const TO_RAD := PI / 180.0
|
|
|
|
const DAY_SKY_TOP := Color(0.12, 0.49, 0.69, 1)
|
|
const DAY_SKY_HORIZON := Color(0.65, 0.84, 0.84, 1)
|
|
const DAY_GROUND_BOTTOM := Color(0.12, 0.17, 0.12, 1)
|
|
const DAY_GROUND_HORIZON := DAY_SKY_HORIZON
|
|
const DAY_AMBIENT := Color(0.65, 0.80, 0.87, 1)
|
|
const DAY_AMBIENT_ENERGY := 0.65
|
|
const DAY_FOG_LIGHT := Color(0.63, 0.80, 0.82, 1)
|
|
const DAY_FOG_LIGHT_ENERGY := 0.8
|
|
const DAY_FOG_DENSITY := 0.0018
|
|
const DAY_LIGHT_COLOR := Color(1, 0.94, 0.79, 1)
|
|
const DAY_LIGHT_ENERGY := 1.35
|
|
|
|
const NIGHT_SKY_TOP := Color(0.04, 0.04, 0.12, 1)
|
|
const NIGHT_SKY_HORIZON := Color(0.08, 0.06, 0.14, 1)
|
|
const NIGHT_GROUND_BOTTOM := Color(0.02, 0.02, 0.04, 1)
|
|
const NIGHT_GROUND_HORIZON := Color(0.04, 0.03, 0.06, 1)
|
|
const NIGHT_AMBIENT := Color(0.12, 0.14, 0.28, 1)
|
|
const NIGHT_AMBIENT_ENERGY := 0.25
|
|
const NIGHT_FOG_LIGHT := Color(0.1, 0.12, 0.24, 1)
|
|
const NIGHT_FOG_LIGHT_ENERGY := 0.3
|
|
const NIGHT_FOG_DENSITY := 0.005
|
|
const NIGHT_LIGHT_COLOR := Color(0.22, 0.28, 0.5, 1)
|
|
const NIGHT_LIGHT_ENERGY := 0.3
|
|
|
|
const SUNSET_LIGHT_COLOR := Color(1, 0.55, 0.3, 1)
|
|
const SUNRISE_LIGHT_COLOR := Color(1, 0.6, 0.45, 1)
|
|
const SUNRISE_SUNSET_ENERGY := 0.8
|
|
const PRESENTATION_UPDATE_INTERVAL_SECONDS := 0.1
|
|
|
|
var elapsed := 0.0
|
|
var _simulation_node: Node
|
|
var _presentation_elapsed := 0.0
|
|
var _last_applied_cycle := -1.0
|
|
|
|
|
|
func _ready() -> void:
|
|
_find_simulation_node()
|
|
if _simulation_node == null:
|
|
elapsed = cycle_duration_seconds * initial_cycle
|
|
_apply_light_rotation(initial_cycle)
|
|
_apply_environment(initial_cycle)
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
_presentation_elapsed += delta
|
|
if _presentation_elapsed < PRESENTATION_UPDATE_INTERVAL_SECONDS:
|
|
return
|
|
var update_delta := _presentation_elapsed
|
|
_presentation_elapsed = 0.0
|
|
if _simulation_node == null or not is_instance_valid(_simulation_node):
|
|
_find_simulation_node()
|
|
var cycle: float
|
|
if _simulation_node != null and "clock" in _simulation_node and _simulation_node.clock != null:
|
|
cycle = _simulation_node.clock.time_of_day()
|
|
else:
|
|
elapsed += update_delta
|
|
if elapsed >= cycle_duration_seconds:
|
|
elapsed = fmod(elapsed, cycle_duration_seconds)
|
|
cycle = elapsed / cycle_duration_seconds
|
|
if is_equal_approx(cycle, _last_applied_cycle):
|
|
return
|
|
_last_applied_cycle = cycle
|
|
_apply_light_rotation(cycle)
|
|
_apply_environment(cycle)
|
|
|
|
|
|
func _apply_light_rotation(cycle: float) -> void:
|
|
if directional_light == null:
|
|
return
|
|
# The bright interval and the sun's arc share dawn/noon/dusk. The old
|
|
# quarter-cycle offset put the brightest morning sun on the horizon.
|
|
var solar_progress := clampf((cycle - 0.15) / 0.7, 0.0, 1.0)
|
|
var elevation := 8.0 + sin(solar_progress * PI) * 52.0
|
|
if cycle < 0.15 or cycle > 0.85:
|
|
elevation = 32.0 # One cool moon fill during the night interval.
|
|
directional_light.rotation = Vector3(-elevation * TO_RAD, cycle * TAU, 0)
|
|
|
|
|
|
func _apply_environment(cycle: float) -> void:
|
|
if directional_light == null:
|
|
return
|
|
|
|
var day_factor := _day_factor(cycle)
|
|
|
|
directional_light.light_color = _lerp_color(NIGHT_LIGHT_COLOR, DAY_LIGHT_COLOR, day_factor)
|
|
directional_light.light_energy = lerpf(NIGHT_LIGHT_ENERGY, DAY_LIGHT_ENERGY, day_factor)
|
|
|
|
var transition_peak := _sunrise_sunset_weight(cycle)
|
|
if transition_peak > 0.0:
|
|
var transition_color: Color
|
|
if cycle < 0.5:
|
|
transition_color = SUNRISE_LIGHT_COLOR
|
|
else:
|
|
transition_color = SUNSET_LIGHT_COLOR
|
|
directional_light.light_color = directional_light.light_color.lerp(
|
|
transition_color, transition_peak * 0.85
|
|
)
|
|
directional_light.light_energy = maxf(
|
|
directional_light.light_energy, SUNRISE_SUNSET_ENERGY * transition_peak
|
|
)
|
|
|
|
if world_environment == null or world_environment.environment == null:
|
|
return
|
|
|
|
var env: Environment = world_environment.environment
|
|
|
|
env.ambient_light_color = _lerp_color(NIGHT_AMBIENT, DAY_AMBIENT, day_factor)
|
|
env.ambient_light_energy = lerpf(NIGHT_AMBIENT_ENERGY, DAY_AMBIENT_ENERGY, day_factor)
|
|
|
|
env.fog_light_color = _lerp_color(NIGHT_FOG_LIGHT, DAY_FOG_LIGHT, day_factor)
|
|
env.fog_light_energy = lerpf(NIGHT_FOG_LIGHT_ENERGY, DAY_FOG_LIGHT_ENERGY, day_factor)
|
|
env.fog_density = lerpf(NIGHT_FOG_DENSITY, DAY_FOG_DENSITY, day_factor)
|
|
|
|
var sky: Sky = env.sky
|
|
if sky != null and sky.sky_material is ProceduralSkyMaterial:
|
|
var mat: ProceduralSkyMaterial = sky.sky_material
|
|
mat.sky_top_color = _lerp_color(NIGHT_SKY_TOP, DAY_SKY_TOP, day_factor)
|
|
mat.sky_horizon_color = _lerp_color(NIGHT_SKY_HORIZON, DAY_SKY_HORIZON, day_factor)
|
|
mat.ground_bottom_color = _lerp_color(NIGHT_GROUND_BOTTOM, DAY_GROUND_BOTTOM, day_factor)
|
|
mat.ground_horizon_color = _lerp_color(NIGHT_GROUND_HORIZON, DAY_GROUND_HORIZON, day_factor)
|
|
elif sky != null and sky.sky_material is ShaderMaterial:
|
|
var mat := sky.sky_material as ShaderMaterial
|
|
mat.set_shader_parameter("sky_top_color", NIGHT_SKY_TOP.lerp(DAY_SKY_TOP, day_factor))
|
|
mat.set_shader_parameter(
|
|
"sky_horizon_color", NIGHT_SKY_HORIZON.lerp(DAY_SKY_HORIZON, day_factor)
|
|
)
|
|
mat.set_shader_parameter(
|
|
"ground_bottom_color", NIGHT_GROUND_BOTTOM.lerp(DAY_GROUND_BOTTOM, day_factor)
|
|
)
|
|
mat.set_shader_parameter(
|
|
"ground_horizon_color", NIGHT_GROUND_HORIZON.lerp(DAY_GROUND_HORIZON, day_factor)
|
|
)
|
|
mat.set_shader_parameter("daylight", day_factor)
|
|
mat.set_shader_parameter("golden_hour", transition_peak)
|
|
|
|
|
|
func _find_simulation_node() -> void:
|
|
_simulation_node = get_tree().get_first_node_in_group("simulation_manager")
|
|
|
|
|
|
func _day_factor(cycle: float) -> float:
|
|
if cycle < 0.15:
|
|
return 0.0
|
|
if cycle < 0.3:
|
|
return smoothstep(0.15, 0.3, cycle)
|
|
if cycle < 0.7:
|
|
return 1.0
|
|
if cycle < 0.85:
|
|
return 1.0 - smoothstep(0.7, 0.85, cycle)
|
|
return 0.0
|
|
|
|
|
|
func _sunrise_sunset_weight(cycle: float) -> float:
|
|
if cycle < 0.3:
|
|
return smoothstep(0.15, 0.21, cycle) * (1.0 - smoothstep(0.23, 0.3, cycle))
|
|
if cycle > 0.7:
|
|
return smoothstep(0.7, 0.77, cycle) * (1.0 - smoothstep(0.79, 0.85, cycle))
|
|
return 0.0
|
|
|
|
|
|
func _lerp_color(from: Color, to: Color, weight: float) -> Color:
|
|
return from.lerp(to, clampf(weight, 0.0, 1.0))
|