Add stylized sun and moon cycle

This commit is contained in:
Alexander Sellite 2026-08-02 13:23:12 -04:00
parent 58fb3d6605
commit cae120c532
8 changed files with 227 additions and 30 deletions

View file

@ -14,6 +14,7 @@ const FishingSpotType = preload("res://fishing/fishing_spot.gd")
const FishableWaterRegionType = preload( const FishableWaterRegionType = preload(
"res://world/fishable_water_region.gd" "res://world/fishable_water_region.gd"
) )
const WeatherIconType = preload("res://ui/weather_icon.gd")
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres") const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
@ -28,6 +29,7 @@ func _run() -> void:
_validate_fishing_spot_context() _validate_fishing_spot_context()
_validate_network_snapshot_bounds() _validate_network_snapshot_bounds()
_validate_environment_presentation() _validate_environment_presentation()
_validate_weather_clock_icon()
print("World time validation: PASS") print("World time validation: PASS")
quit() quit()
@ -154,7 +156,11 @@ func _validate_environment_presentation() -> void:
var world_environment := WorldEnvironment.new() var world_environment := WorldEnvironment.new()
var environment := Environment.new() var environment := Environment.new()
var sky := Sky.new() var sky := Sky.new()
sky.sky_material = ProceduralSkyMaterial.new() var sky_material := ShaderMaterial.new()
sky_material.shader = preload(
"res://world/environment/netfishing_sky.gdshader"
)
sky.sky_material = sky_material
environment.sky = sky environment.sky = sky
environment.adjustment_enabled = true environment.adjustment_enabled = true
environment.fog_enabled = true environment.fog_enabled = true
@ -170,13 +176,63 @@ func _validate_environment_presentation() -> void:
visuals.apply_time_immediately(12.0) visuals.apply_time_immediately(12.0)
var runtime_environment: Environment = world_environment.environment var runtime_environment: Environment = world_environment.environment
var runtime_sky_material := ( var runtime_sky_material := (
runtime_environment.sky.sky_material as ProceduralSkyMaterial runtime_environment.sky.sky_material as ShaderMaterial
)
var day_horizon_value: Variant = runtime_sky_material.get_shader_parameter(
"sky_horizon_color"
)
assert(day_horizon_value is Color)
var day_horizon: Color = day_horizon_value as Color
var day_sun_visibility: float = float(
runtime_sky_material.get_shader_parameter("sun_visibility")
)
var day_sun_direction: Vector3 = (
runtime_sky_material.get_shader_parameter("sun_direction") as Vector3
)
assert(day_sun_visibility > 0.99)
assert(day_sun_direction.y > 0.85)
assert(
float(runtime_sky_material.get_shader_parameter("moon_visibility"))
< 0.01
) )
var day_horizon: Color = runtime_sky_material.sky_horizon_color
var day_ambient_energy: float = runtime_environment.ambient_light_energy var day_ambient_energy: float = runtime_environment.ambient_light_energy
visuals.apply_time_immediately(0.0) visuals.apply_time_immediately(0.0)
assert(runtime_sky_material.sky_horizon_color != day_horizon) var night_horizon: Color = runtime_sky_material.get_shader_parameter(
"sky_horizon_color"
) as Color
assert(night_horizon != day_horizon)
assert(
float(runtime_sky_material.get_shader_parameter("sun_visibility"))
< 0.01
)
var night_moon_visibility: float = float(
runtime_sky_material.get_shader_parameter("moon_visibility")
)
var night_moon_direction: Vector3 = (
runtime_sky_material.get_shader_parameter("moon_direction") as Vector3
)
var night_sun_direction: Vector3 = (
runtime_sky_material.get_shader_parameter("sun_direction") as Vector3
)
assert(night_moon_visibility > 0.99)
assert(night_moon_direction.y > 0.85)
assert(night_moon_direction.is_equal_approx(-night_sun_direction))
assert(runtime_environment.ambient_light_energy < day_ambient_energy) assert(runtime_environment.ambient_light_energy < day_ambient_energy)
visuals.apply_time_immediately(20.0) visuals.apply_time_immediately(20.0)
assert(runtime_sky_material.sky_horizon_color.r > day_horizon.r) var dusk_horizon: Color = runtime_sky_material.get_shader_parameter(
"sky_horizon_color"
) as Color
assert(dusk_horizon.r > day_horizon.r)
world_root.queue_free() world_root.queue_free()
func _validate_weather_clock_icon() -> void:
var weather_icon := WeatherIconType.new()
root.add_child(weather_icon)
assert(weather_icon.tooltip_text == "clear")
assert(not weather_icon.is_nighttime())
weather_icon.set_nighttime(true)
assert(weather_icon.is_nighttime())
weather_icon.set_weather(WorldWeatherService.Weather.CLOUDY)
assert(weather_icon.tooltip_text == "cloudy")
weather_icon.queue_free()

View file

@ -146,7 +146,11 @@ func _validate_weather_presentation() -> void:
var world_environment := WorldEnvironment.new() var world_environment := WorldEnvironment.new()
var environment := Environment.new() var environment := Environment.new()
var sky := Sky.new() var sky := Sky.new()
sky.sky_material = ProceduralSkyMaterial.new() var sky_material := ShaderMaterial.new()
sky_material.shader = preload(
"res://world/environment/netfishing_sky.gdshader"
)
sky.sky_material = sky_material
environment.sky = sky environment.sky = sky
environment.adjustment_enabled = true environment.adjustment_enabled = true
environment.fog_enabled = true environment.fog_enabled = true

View file

@ -958,6 +958,8 @@ func _on_world_time_changed(
) -> void: ) -> void:
if _clock_label != null and _world_time != null: if _clock_label != null and _world_time != null:
_clock_label.text = _world_time.get_clock_text() _clock_label.text = _world_time.get_clock_text()
if _weather_icon != null and _world_time != null:
_weather_icon.set_nighttime(_world_time.is_night_period())
func _on_world_weather_changed( func _on_world_weather_changed(

View file

@ -4,27 +4,41 @@ extends Control
const BUBBLE_COLOR := Color(0.025, 0.13, 0.19, 0.94) const BUBBLE_COLOR := Color(0.025, 0.13, 0.19, 0.94)
const ICON_COLOR := Color(0.78, 0.91, 0.95) const ICON_COLOR := Color(0.78, 0.91, 0.95)
const SUN_COLOR := Color(0.98, 0.82, 0.34) const SUN_COLOR := Color(0.98, 0.82, 0.34)
const MOON_COLOR := Color(0.78, 0.88, 1.0)
const RAIN_COLOR := Color(0.28, 0.73, 0.82) const RAIN_COLOR := Color(0.28, 0.73, 0.82)
var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY
var _is_nighttime: bool = false
func _ready() -> void: func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_PASS mouse_filter = Control.MOUSE_FILTER_PASS
focus_mode = Control.FOCUS_NONE focus_mode = Control.FOCUS_NONE
custom_minimum_size = Vector2(34.0, 34.0) custom_minimum_size = Vector2(34.0, 34.0)
tooltip_text = "sunny" _update_tooltip()
queue_redraw() queue_redraw()
func set_weather(weather: WorldWeatherService.Weather) -> void: func set_weather(weather: WorldWeatherService.Weather) -> void:
if _weather == weather: if _weather == weather:
_update_tooltip()
return return
_weather = weather _weather = weather
tooltip_text = WorldWeatherService.weather_name(_weather) _update_tooltip()
queue_redraw() queue_redraw()
func set_nighttime(is_nighttime: bool) -> void:
if _is_nighttime == is_nighttime:
return
_is_nighttime = is_nighttime
queue_redraw()
func is_nighttime() -> bool:
return _is_nighttime
func get_weather() -> WorldWeatherService.Weather: func get_weather() -> WorldWeatherService.Weather:
return _weather return _weather
@ -35,7 +49,10 @@ func _draw() -> void:
draw_circle(center, radius, BUBBLE_COLOR) draw_circle(center, radius, BUBBLE_COLOR)
match _weather: match _weather:
WorldWeatherService.Weather.SUNNY: WorldWeatherService.Weather.SUNNY:
_draw_sun(center) if _is_nighttime:
_draw_moon(center)
else:
_draw_sun(center)
WorldWeatherService.Weather.CLOUDY: WorldWeatherService.Weather.CLOUDY:
_draw_cloud(center + Vector2(0.0, 1.0)) _draw_cloud(center + Vector2(0.0, 1.0))
WorldWeatherService.Weather.RAINY: WorldWeatherService.Weather.RAINY:
@ -73,6 +90,19 @@ func _draw_sun(center: Vector2) -> void:
) )
func _draw_moon(center: Vector2) -> void:
draw_circle(center, 8.0, MOON_COLOR)
draw_circle(center + Vector2(4.0, -2.0), 7.0, BUBBLE_COLOR)
func _update_tooltip() -> void:
tooltip_text = (
"clear"
if _weather == WorldWeatherService.Weather.SUNNY
else WorldWeatherService.weather_name(_weather)
)
func _draw_cloud(center: Vector2) -> void: func _draw_cloud(center: Vector2) -> void:
draw_circle(center + Vector2(-5.0, 0.0), 5.0, ICON_COLOR) draw_circle(center + Vector2(-5.0, 0.0), 5.0, ICON_COLOR)
draw_circle(center + Vector2(0.0, -3.0), 6.0, ICON_COLOR) draw_circle(center + Vector2(0.0, -3.0), 6.0, ICON_COLOR)

View file

@ -0,0 +1,83 @@
shader_type sky;
uniform vec4 sky_top_color : source_color = vec4(0.204, 0.498, 0.643, 1.0);
uniform vec4 sky_horizon_color : source_color = vec4(0.663, 0.843, 0.847, 1.0);
uniform vec4 ground_bottom_color : source_color = vec4(0.157, 0.361, 0.439, 1.0);
uniform vec4 ground_horizon_color : source_color = vec4(0.549, 0.749, 0.765, 1.0);
uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0);
uniform vec4 sun_color : source_color = vec4(1.0, 0.86, 0.46, 1.0);
uniform float sun_visibility : hint_range(0.0, 1.0) = 1.0;
uniform float sun_radius : hint_range(0.005, 0.15) = 0.055;
uniform float sun_edge_softness : hint_range(0.001, 0.03) = 0.003;
uniform float sun_halo_radius : hint_range(0.02, 0.35) = 0.14;
uniform float sun_halo_strength : hint_range(0.0, 1.0) = 0.16;
uniform vec3 moon_direction = vec3(0.0, -1.0, 0.0);
uniform vec4 moon_color : source_color = vec4(0.78, 0.88, 1.0, 1.0);
uniform float moon_visibility : hint_range(0.0, 1.0) = 0.0;
uniform float moon_radius : hint_range(0.005, 0.15) = 0.045;
uniform float moon_edge_softness : hint_range(0.001, 0.03) = 0.003;
uniform float moon_halo_radius : hint_range(0.02, 0.35) = 0.11;
uniform float moon_halo_strength : hint_range(0.0, 1.0) = 0.12;
void sky() {
vec3 view_direction = normalize(EYEDIR);
float above_horizon = max(view_direction.y, 0.0);
float below_horizon = max(-view_direction.y, 0.0);
vec3 sky_color = mix(
sky_horizon_color.rgb,
sky_top_color.rgb,
pow(above_horizon, 0.72)
);
vec3 ground_color = mix(
ground_horizon_color.rgb,
ground_bottom_color.rgb,
pow(below_horizon, 0.16)
);
vec3 base_color = mix(
ground_color,
sky_color,
smoothstep(-0.01, 0.01, view_direction.y)
);
float alignment = dot(view_direction, normalize(sun_direction));
float disc_outer = cos(sun_radius + sun_edge_softness);
float disc_inner = cos(sun_radius);
float halo_outer = cos(sun_halo_radius);
float sun_disc = smoothstep(disc_outer, disc_inner, alignment);
float sun_halo = smoothstep(halo_outer, disc_outer, alignment);
float horizon_visibility = smoothstep(-0.035, 0.02, sun_direction.y);
float visible_strength = sun_visibility * horizon_visibility;
base_color += sun_color.rgb * sun_halo * sun_halo_strength * visible_strength;
base_color = mix(base_color, sun_color.rgb, sun_disc * visible_strength);
float moon_alignment = dot(view_direction, normalize(moon_direction));
float moon_disc_outer = cos(moon_radius + moon_edge_softness);
float moon_disc_inner = cos(moon_radius);
float moon_halo_outer = cos(moon_halo_radius);
float moon_disc = smoothstep(
moon_disc_outer, moon_disc_inner, moon_alignment
);
float moon_halo = smoothstep(
moon_halo_outer, moon_disc_outer, moon_alignment
);
float moon_horizon_visibility = smoothstep(
-0.035, 0.02, moon_direction.y
);
float moon_visible_strength = (
moon_visibility * moon_horizon_visibility
);
base_color += (
moon_color.rgb
* moon_halo
* moon_halo_strength
* moon_visible_strength
);
base_color = mix(
base_color, moon_color.rgb, moon_disc * moon_visible_strength
);
COLOR = base_color;
}

View file

@ -0,0 +1 @@
uid://chbcdlhui3ro4

View file

@ -1,13 +1,9 @@
[gd_resource type="Sky" format=3 uid="uid://dersyay71dyat"] [gd_resource type="Sky" load_steps=3 format=3 uid="uid://dersyay71dyat"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_netfishing"] [ext_resource type="Shader" path="res://world/environment/netfishing_sky.gdshader" id="1_sky_shader"]
sky_top_color = Color(0.203922, 0.498039, 0.643137, 1)
sky_horizon_color = Color(0.662745, 0.843137, 0.847059, 1) [sub_resource type="ShaderMaterial" id="ShaderMaterial_netfishing"]
ground_bottom_color = Color(0.156863, 0.360784, 0.439216, 1) shader = ExtResource("1_sky_shader")
ground_horizon_color = Color(0.54902, 0.74902, 0.764706, 1)
ground_curve = 0.08
sun_angle_max = 18.0
sun_curve = 0.22
[resource] [resource]
sky_material = SubResource("ProceduralSkyMaterial_netfishing") sky_material = SubResource("ShaderMaterial_netfishing")

View file

@ -13,6 +13,7 @@ const DAY_GROUND_HORIZON := Color(0.549, 0.749, 0.765)
const DAY_AMBIENT := Color(0.82, 0.90, 0.92) const DAY_AMBIENT := Color(0.82, 0.90, 0.92)
const DAY_FOG := Color(0.549, 0.749, 0.765) const DAY_FOG := Color(0.549, 0.749, 0.765)
const DAY_SUN := Color(0.88, 0.94, 0.96) const DAY_SUN := Color(0.88, 0.94, 0.96)
const DAY_SUN_DISC := Color(1.0, 0.86, 0.46)
const NIGHT_SKY_TOP := Color(0.026, 0.050, 0.105) const NIGHT_SKY_TOP := Color(0.026, 0.050, 0.105)
const NIGHT_SKY_HORIZON := Color(0.10, 0.17, 0.28) const NIGHT_SKY_HORIZON := Color(0.10, 0.17, 0.28)
@ -29,6 +30,8 @@ const WARM_GROUND_HORIZON := Color(0.82, 0.34, 0.18)
const WARM_AMBIENT := Color(0.92, 0.58, 0.40) const WARM_AMBIENT := Color(0.92, 0.58, 0.40)
const WARM_FOG := Color(0.74, 0.39, 0.27) const WARM_FOG := Color(0.74, 0.39, 0.27)
const WARM_SUN := Color(1.0, 0.58, 0.30) const WARM_SUN := Color(1.0, 0.58, 0.30)
const WARM_SUN_DISC := Color(1.0, 0.45, 0.16)
const MOON_DISC := Color(0.78, 0.88, 1.0)
const CLOUDY_TINT := Color(0.53, 0.61, 0.66) const CLOUDY_TINT := Color(0.53, 0.61, 0.66)
const RAINY_TINT := Color(0.31, 0.42, 0.50) const RAINY_TINT := Color(0.31, 0.42, 0.50)
@ -40,7 +43,7 @@ var _world_environment: WorldEnvironment
var _sun: DirectionalLight3D var _sun: DirectionalLight3D
var _rain_target: Node3D var _rain_target: Node3D
var _environment: Environment var _environment: Environment
var _sky_material: ProceduralSkyMaterial var _sky_material: ShaderMaterial
var _rain: GPUParticles3D var _rain: GPUParticles3D
var _elapsed: float = 0.0 var _elapsed: float = 0.0
var _weather_from: WorldWeatherService.Weather = ( var _weather_from: WorldWeatherService.Weather = (
@ -125,13 +128,13 @@ func _prepare_runtime_environment() -> bool:
return false return false
var runtime_sky: Sky = _environment.sky.duplicate(true) as Sky var runtime_sky: Sky = _environment.sky.duplicate(true) as Sky
if runtime_sky == null or runtime_sky.sky_material == null: if runtime_sky == null or runtime_sky.sky_material == null:
push_error("World time visuals require a procedural sky material.") push_error("World time visuals require a sky material.")
return false return false
_sky_material = ( _sky_material = (
runtime_sky.sky_material.duplicate(true) as ProceduralSkyMaterial runtime_sky.sky_material.duplicate(true) as ShaderMaterial
) )
if _sky_material == null: if _sky_material == null:
push_error("World time visuals require a ProceduralSkyMaterial.") push_error("World time visuals require the NETfishing sky material.")
return false return false
runtime_sky.sky_material = _sky_material runtime_sky.sky_material = _sky_material
_environment.sky = runtime_sky _environment.sky = runtime_sky
@ -213,15 +216,19 @@ func _apply_time(time_hours: float) -> void:
var tint_strength: float = _weather_value( var tint_strength: float = _weather_value(
0.0, 0.30, 0.48, 0.62 0.0, 0.30, 0.48, 0.62
) )
_sky_material.sky_top_color = sky_top.lerp(weather_tint, tint_strength) _sky_material.set_shader_parameter(
_sky_material.sky_horizon_color = sky_horizon.lerp( "sky_top_color", sky_top.lerp(weather_tint, tint_strength)
weather_tint, tint_strength
) )
_sky_material.ground_bottom_color = ground_bottom.lerp( _sky_material.set_shader_parameter(
weather_tint, tint_strength * 0.62 "sky_horizon_color", sky_horizon.lerp(weather_tint, tint_strength)
) )
_sky_material.ground_horizon_color = ground_horizon.lerp( _sky_material.set_shader_parameter(
weather_tint, tint_strength * 0.76 "ground_bottom_color",
ground_bottom.lerp(weather_tint, tint_strength * 0.62),
)
_sky_material.set_shader_parameter(
"ground_horizon_color",
ground_horizon.lerp(weather_tint, tint_strength * 0.76),
) )
_environment.background_energy_multiplier = ( _environment.background_energy_multiplier = (
lerpf(0.52, 0.85, daylight) lerpf(0.52, 0.85, daylight)
@ -269,6 +276,24 @@ func _apply_time(time_hours: float) -> void:
SUN_YAW_DEGREES, SUN_YAW_DEGREES,
0.0, 0.0,
) )
_sky_material.set_shader_parameter(
"sun_direction", _sun.global_transform.basis.z.normalized()
)
_sky_material.set_shader_parameter(
"sun_color", DAY_SUN_DISC.lerp(WARM_SUN_DISC, warmth)
)
_sky_material.set_shader_parameter(
"sun_visibility",
daylight * _weather_value(1.0, 0.65, 0.28, 0.15),
)
_sky_material.set_shader_parameter(
"moon_direction", -_sun.global_transform.basis.z.normalized()
)
_sky_material.set_shader_parameter("moon_color", MOON_DISC)
_sky_material.set_shader_parameter(
"moon_visibility",
(1.0 - daylight) * _weather_value(1.0, 0.72, 0.36, 0.20),
)
_sun.light_color = _blended_color( _sun.light_color = _blended_color(
NIGHT_SUN, DAY_SUN, WARM_SUN, daylight, warmth NIGHT_SUN, DAY_SUN, WARM_SUN, daylight, warmth
) )