Add default light profile for PortMaster

This commit is contained in:
Alexander Sellite 2026-08-14 22:12:02 -04:00
parent a62cce6404
commit a3e0830bd1
16 changed files with 454 additions and 42 deletions

View file

@ -13,6 +13,9 @@ const DISTANCE_FADE_START: float = 240.0
const DISTANCE_FADE_END: float = 300.0
const WRAP_RADIUS: float = FIELD_HALF_EXTENT
const MAXIMUM_OPACITY: float = 1.0
const LIGHT_CEILING_SIZE: float = 520.0
const LIGHT_CEILING_ALTITUDE: float = 20.0
const LIGHT_MAXIMUM_OPACITY: float = 0.72
const BASE_VELOCITY := Vector2(0.825, -0.45)
const BODY_OUTLINE: Array[Vector2] = [
Vector2(-6.9, -0.8),
@ -50,14 +53,25 @@ var _target: Node3D
var _material: ShaderMaterial
var _multimesh: MultiMesh
var _cloud_mesh: MultiMeshInstance3D
var _light_cloud_mesh: MeshInstance3D
var _light_material: StandardMaterial3D
var _drift_offset := Vector2.ZERO
var _storm_amount: float = 0.0
var _light_performance_profile: bool = false
func setup(target: Node3D) -> void:
func setup(
target: Node3D,
light_performance_profile: bool = false,
) -> void:
_target = target
_build_cloud_field()
_update_cloud_transforms()
_light_performance_profile = light_performance_profile
if _light_performance_profile:
_build_light_cloud_ceiling()
_update_light_position()
else:
_build_cloud_field()
_update_cloud_transforms()
set_process(true)
@ -65,8 +79,19 @@ func set_storm_amount(amount: float, color: Color) -> void:
_storm_amount = clampf(amount, 0.0, 1.0)
var should_be_visible: bool = _storm_amount > 0.001
if should_be_visible and not visible:
_update_cloud_transforms()
if _light_performance_profile:
_update_light_position()
else:
_update_cloud_transforms()
visible = should_be_visible
if _light_performance_profile:
if _light_material != null:
var light_color: Color = color
light_color.a = (
_storm_amount * LIGHT_MAXIMUM_OPACITY
)
_light_material.albedo_color = light_color
return
if _material == null:
return
_material.set_shader_parameter("weather_opacity", _storm_amount)
@ -78,12 +103,19 @@ func get_storm_amount() -> float:
func get_patch_count() -> int:
return GRID_AXIS_COUNT * GRID_AXIS_COUNT
return (
1
if _light_performance_profile
else GRID_AXIS_COUNT * GRID_AXIS_COUNT
)
func _process(delta: float) -> void:
if not visible or _target == null or not is_instance_valid(_target):
return
if _light_performance_profile:
_update_light_position()
return
_drift_offset += BASE_VELOCITY * delta
_drift_offset = Vector2(
_wrap_axis(_drift_offset.x),
@ -128,6 +160,37 @@ func _build_cloud_field() -> void:
visible = false
func _build_light_cloud_ceiling() -> void:
_light_material = StandardMaterial3D.new()
_light_material.resource_name = "light_cloud_ceiling"
_light_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
_light_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
_light_material.cull_mode = BaseMaterial3D.CULL_DISABLED
_light_material.albedo_color = Color(0.22, 0.25, 0.26, 0.0)
var ceiling_mesh := PlaneMesh.new()
ceiling_mesh.size = Vector2.ONE * LIGHT_CEILING_SIZE
ceiling_mesh.material = _light_material
_light_cloud_mesh = MeshInstance3D.new()
_light_cloud_mesh.name = "CloudCeiling"
_light_cloud_mesh.position.y = LIGHT_CEILING_ALTITUDE
_light_cloud_mesh.mesh = ceiling_mesh
_light_cloud_mesh.cast_shadow = (
GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
)
add_child(_light_cloud_mesh)
visible = false
func _update_light_position() -> void:
if _target == null or not is_instance_valid(_target):
return
global_position = Vector3(
_target.global_position.x,
0.0,
_target.global_position.z,
)
func _build_cloud_body_mesh() -> ArrayMesh:
var outline := PackedVector2Array(BODY_OUTLINE)
var surface_tool := SurfaceTool.new()

View file

@ -13,6 +13,14 @@ const FOLIAGE_MATERIAL_NAMES: Array[StringName] = [
&"leaf_light",
&"leaf_dark",
]
const NORMAL_SALT_WATER_MATERIAL: Material = preload(
"res://world/materials/stylized_water.tres"
)
const NORMAL_FRESH_WATER_MATERIAL: Material = preload(
"res://world/materials/stylized_water_fresh.tres"
)
const LIGHT_SALT_WATER_COLOR := Color(0.11, 0.345, 0.435)
const LIGHT_FRESH_WATER_COLOR := Color(0.18, 0.46, 0.50)
@export_group("Owned Nodes")
@export_node_path("MeshInstance3D")
@ -60,6 +68,48 @@ func get_saltwater_shoreline_mesh() -> MeshInstance3D:
return get_node_or_null(^"ShorelineRibbons/Ocean") as MeshInstance3D
func set_light_performance_profile(enabled: bool) -> void:
var pond := get_node_or_null(
^"WaterBodies/Pond/VisualWater"
) as MeshInstance3D
var ocean := get_node_or_null(
^"WaterBodies/Ocean/VisualWater"
) as MeshInstance3D
if pond != null:
pond.material_override = (
_create_light_water_material(
LIGHT_FRESH_WATER_COLOR,
&"light_fresh_water",
)
if enabled
else NORMAL_FRESH_WATER_MATERIAL
)
if ocean != null:
ocean.material_override = (
_create_light_water_material(
LIGHT_SALT_WATER_COLOR,
&"light_salt_water",
)
if enabled
else NORMAL_SALT_WATER_MATERIAL
)
var surface_motion := ocean as WaterSurfaceMotion
if surface_motion != null:
surface_motion.set_motion_enabled(not enabled)
func _create_light_water_material(
color: Color,
material_name: StringName,
) -> StandardMaterial3D:
var material := StandardMaterial3D.new()
material.resource_name = str(material_name)
material.albedo_color = color
material.roughness = 1.0
material.shading_mode = BaseMaterial3D.SHADING_MODE_PER_VERTEX
return material
func get_spawn_surface_triangles(
material_names: Array[StringName],
minimum_global_y: float,

View file

@ -50,6 +50,10 @@ func get_sun() -> DirectionalLight3D:
return _sun
func set_light_performance_profile(enabled: bool) -> void:
_starter_island.set_light_performance_profile(enabled)
func get_fishable_water_regions() -> Array[FishableWaterRegion]:
var waters: Array[FishableWaterRegion] = []
for region: WorldRegion in _get_regions():

View file

@ -35,6 +35,12 @@ func _process(_delta: float) -> void:
)
func set_motion_enabled(enabled: bool) -> void:
set_process(enabled and amplitude > 0.0)
if not enabled:
position.y = _base_height
static func get_default_height_offset() -> float:
return calculate_height_offset(
_current_time_seconds(),

View file

@ -2,6 +2,7 @@ class_name WorldTimeVisualController
extends Node
const UPDATE_INTERVAL_SECONDS: float = 0.1
const LIGHT_UPDATE_INTERVAL_SECONDS: float = 0.25
const SUN_YAW_DEGREES: float = -32.0
const WEATHER_TRANSITION_SECONDS: float = 10.0
const RAIN_EMITTER_OFFSET := Vector3(0.0, 7.0, 0.0)
@ -11,6 +12,8 @@ const RAIN_VISIBILITY_AABB := AABB(
Vector3(27.0, 12.0, 27.0),
)
const RAIN_PARTICLE_AMOUNT: int = 2240
const LIGHT_RAIN_PARTICLE_AMOUNT: int = 256
const LIGHT_RAIN_FIXED_FPS: int = 15
const RAIN_VELOCITY_MIN: float = 16.0
const RAIN_VELOCITY_MAX: float = 20.0
const RAIN_DROP_SIZE := Vector3(0.014, 0.34, 0.014)
@ -92,6 +95,7 @@ var _weather_to: WorldWeatherService.Weather = (
WorldWeatherService.Weather.SUNNY
)
var _weather_transition: float = 1.0
var _light_performance_profile: bool = false
func setup(
@ -101,6 +105,7 @@ func setup(
weather_service: WorldWeatherService = null,
rain_target: Node3D = null,
rain_camera_provider: Callable = Callable(),
light_performance_profile: bool = false,
) -> void:
_time_service = time_service
_weather_service = weather_service
@ -108,6 +113,7 @@ func setup(
_sun = sun
_rain_target = rain_target
_rain_camera_provider = rain_camera_provider
_light_performance_profile = light_performance_profile
if not _prepare_runtime_environment():
set_process(false)
return
@ -135,7 +141,12 @@ func _process(delta: float) -> void:
1.0,
)
_elapsed += delta
if _elapsed < UPDATE_INTERVAL_SECONDS:
var update_interval: float = (
LIGHT_UPDATE_INTERVAL_SECONDS
if _light_performance_profile
else UPDATE_INTERVAL_SECONDS
)
if _elapsed < update_interval:
return
_elapsed = 0.0
_apply_time(_time_service.get_time_hours())
@ -187,10 +198,16 @@ func _prepare_runtime_environment() -> bool:
func _prepare_rain() -> void:
_rain = GPUParticles3D.new()
_rain.name = "LocalRain"
_rain.amount = RAIN_PARTICLE_AMOUNT
_rain.amount = (
LIGHT_RAIN_PARTICLE_AMOUNT
if _light_performance_profile
else RAIN_PARTICLE_AMOUNT
)
_rain.amount_ratio = 0.0
_rain.lifetime = 1.25
_rain.fixed_fps = 30
_rain.fixed_fps = (
LIGHT_RAIN_FIXED_FPS if _light_performance_profile else 30
)
_rain.local_coords = false
_rain.visibility_aabb = RAIN_VISIBILITY_AABB
var process_material := ParticleProcessMaterial.new()
@ -220,7 +237,7 @@ func _prepare_storm_clouds() -> void:
_storm_clouds = LocalStormCloudLayerType.new()
_storm_clouds.name = "LocalStormClouds"
add_child(_storm_clouds)
_storm_clouds.setup(_rain_target)
_storm_clouds.setup(_rain_target, _light_performance_profile)
func _apply_time(time_hours: float) -> void:
@ -259,6 +276,33 @@ func _apply_time(time_hours: float) -> void:
var fog_color: Color = _blended_color(
NIGHT_FOG, DAY_FOG, WARM_FOG, daylight, warmth
)
if _light_performance_profile:
var simple_cloud_color := _blended_color(
NIGHT_CLOUD_SHADOW,
DAY_CLOUD_SHADOW,
WARM_CLOUD_SHADOW,
daylight,
warmth,
)
var simple_overcast: float = _weather_value(
0.0,
0.55,
0.82,
0.0,
)
sky_top = sky_top.lerp(simple_cloud_color, simple_overcast)
sky_horizon = sky_horizon.lerp(
simple_cloud_color.lightened(0.08),
simple_overcast,
)
ground_bottom = ground_bottom.lerp(
simple_cloud_color.darkened(0.12),
simple_overcast * 0.65,
)
ground_horizon = ground_horizon.lerp(
simple_cloud_color,
simple_overcast * 0.75,
)
var foggy_fog_amount: float = _weather_value(0.0, 0.0, 0.0, 1.0)
var rain_fog_amount: float = _weather_value(0.0, 0.0, 1.0, 0.0)
var fog_daylight_amount: float = daylight * foggy_fog_amount
@ -399,6 +443,8 @@ func _apply_water_environment(
foggy_horizon_occlusion: float,
fog_render_color: Color,
) -> void:
if _light_performance_profile:
return
var water_tint := _blended_color(
NIGHT_WATER_TINT,
Color.WHITE,
@ -561,6 +607,15 @@ func _apply_sky_clouds(daylight: float, warmth: float) -> void:
daylight,
warmth,
)
if _light_performance_profile:
_sky_material.set_shader_parameter("cloud_coverage", 0.0)
_sky_material.set_shader_parameter("cloud_opacity", 0.0)
if _storm_clouds != null:
_storm_clouds.set_storm_amount(
_weather_value(0.0, 0.42, 0.88, 0.0),
lower_cloud_color,
)
return
_sky_material.set_shader_parameter(
"cloud_coverage",
_weather_value(0.0, 0.58, 0.985, 0.0),