From 097cfaeaca8e0475775c577cf243b7f19e8ca186 Mon Sep 17 00:00:00 2001 From: Rijad Zuzo Date: Mon, 6 Jul 2026 00:01:36 +0200 Subject: [PATCH] feat: add day/night cycle and visual workplace props MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DayNightCycle.gd smoothly rotates DirectionalLight3D and interpolates sky colors, ambient light, fog, and procedural sky material across a configurable 240-second cycle with warm sunset/sunrise peaks and deep night ambiance. No simulation dependency — purely visual. JajceWorld.tscn now includes visible workplace structures replacing the invisible Marker3Ds: wooden pantry crate, guard tower post, study desk, and rest bench. Each has a distinct material color matching the profession palette. The DayNightCycle node is added to JajceWorld.tscn with node_paths wired to DirectionalLight3D and WorldEnvironment. Script reference uses path-based ext_resource (uid generation deferred to editor first load). --- world/jajce/JajceWorld.tscn | 58 +++++++++++++++- world/jajce/day_night_cycle.gd | 122 +++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 world/jajce/day_night_cycle.gd diff --git a/world/jajce/JajceWorld.tscn b/world/jajce/JajceWorld.tscn index c8724fe..f6a6b81 100644 --- a/world/jajce/JajceWorld.tscn +++ b/world/jajce/JajceWorld.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=20 format=3] +[gd_scene load_steps=26 format=3] [ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"] [ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"] @@ -12,6 +12,7 @@ [ext_resource type="PackedScene" path="res://world/jajce/WaterfallBody.tscn" id="10_waterfall"] [ext_resource type="PackedScene" path="res://world/jajce/WaterfallMist.tscn" id="11_mist"] [ext_resource type="PackedScene" path="res://world/jajce/ChimneySmoke.tscn" id="12_smoke"] +[ext_resource type="Script" path="res://world/jajce/day_night_cycle.gd" id="13_daynight"] [sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_jajce"] _shader_parameters = { @@ -85,6 +86,40 @@ adjustment_brightness = 1.04 adjustment_contrast = 1.03 adjustment_saturation = 1.1 +[sub_resource type="StandardMaterial3D" id="Material_pantry"] +albedo_color = Color(0.5, 0.32, 0.2, 1) +roughness = 0.85 + +[sub_resource type="StandardMaterial3D" id="Material_guard"] +albedo_color = Color(0.55, 0.45, 0.35, 1) +roughness = 0.8 + +[sub_resource type="StandardMaterial3D" id="Material_study"] +albedo_color = Color(0.45, 0.35, 0.22, 1) +roughness = 0.82 + +[sub_resource type="StandardMaterial3D" id="Material_rest"] +albedo_color = Color(0.35, 0.28, 0.22, 1) +roughness = 0.88 + +[sub_resource type="BoxMesh" id="Mesh_pantry_base"] +material = SubResource("Material_pantry") +size = Vector3(1.6, 1.1, 1.6) + +[sub_resource type="CylinderMesh" id="Mesh_guard_post"] +material = SubResource("Material_guard") +top_radius = 0.35 +bottom_radius = 0.4 +height = 2.5 + +[sub_resource type="BoxMesh" id="Mesh_study_desk"] +material = SubResource("Material_study") +size = Vector3(2.0, 0.25, 1.2) + +[sub_resource type="BoxMesh" id="Mesh_rest_bench"] +material = SubResource("Material_rest") +size = Vector3(2.8, 0.3, 0.9) + [node name="JajceWorld" type="Node3D"] script = ExtResource("3_world") @@ -223,15 +258,31 @@ yield_per_action = 3.0 [node name="PantryMarker" type="Marker3D" parent="LegacyActivityMarkers"] position = Vector3(0, 0, -8) +[node name="PantryVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/PantryMarker"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0) +mesh = SubResource("Mesh_pantry_base") + [node name="GuardZone" type="Marker3D" parent="LegacyActivityMarkers"] position = Vector3(0, 0, 8) +[node name="GuardTowerVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/GuardZone"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0) +mesh = SubResource("Mesh_guard_post") + [node name="StudyZone" type="Marker3D" parent="LegacyActivityMarkers"] position = Vector3(-6, 0, 6) +[node name="StudyDeskVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/StudyZone"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.125, 0) +mesh = SubResource("Mesh_study_desk") + [node name="RestZone" type="Marker3D" parent="LegacyActivityMarkers"] position = Vector3(4, 0, 5) +[node name="RestBenchVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/RestZone"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0) +mesh = SubResource("Mesh_rest_bench") + [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] rotation_degrees = Vector3(-55, -35, 0) light_color = Color(1, 0.87, 0.72, 1) @@ -241,3 +292,8 @@ directional_shadow_max_distance = 180.0 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_greybox") + +[node name="DayNightCycle" type="Node" parent="." node_paths=PackedStringArray("directional_light", "world_environment")] +script = ExtResource("13_daynight") +directional_light = NodePath("../DirectionalLight3D") +world_environment = NodePath("../WorldEnvironment") diff --git a/world/jajce/day_night_cycle.gd b/world/jajce/day_night_cycle.gd new file mode 100644 index 0000000..c2ed4f0 --- /dev/null +++ b/world/jajce/day_night_cycle.gd @@ -0,0 +1,122 @@ +extends Node + +@export var cycle_duration_seconds := 240.0 +@export var directional_light: DirectionalLight3D +@export var world_environment: WorldEnvironment + +const TO_RAD := PI / 180.0 + +const DAY_SKY_TOP := Color(0.23, 0.43, 0.62, 1) +const DAY_SKY_HORIZON := Color(0.82, 0.68, 0.5, 1) +const DAY_GROUND_BOTTOM := Color(0.12, 0.17, 0.12, 1) +const DAY_GROUND_HORIZON := Color(0.62, 0.55, 0.42, 1) +const DAY_AMBIENT := Color(0.91, 0.85, 0.72, 1) +const DAY_AMBIENT_ENERGY := 0.7 +const DAY_FOG_LIGHT := Color(0.82, 0.78, 0.66, 1) +const DAY_FOG_LIGHT_ENERGY := 0.72 +const DAY_FOG_DENSITY := 0.002 +const DAY_LIGHT_COLOR := Color(1, 0.87, 0.72, 1) +const DAY_LIGHT_ENERGY := 1.45 + +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 + +var elapsed := 0.0 + + +func _process(delta: float) -> void: + elapsed += delta + if elapsed >= cycle_duration_seconds: + elapsed = fmod(elapsed, cycle_duration_seconds) + var cycle := elapsed / cycle_duration_seconds + _apply_light_rotation(cycle) + _apply_environment(cycle) + + +func _apply_light_rotation(cycle: float) -> void: + if directional_light == null: + return + var angle_rad: float = cycle * 2.0 * PI + directional_light.rotation = Vector3(-PI / 4.0 + sin(angle_rad) * PI / 4.0, angle_rad, 0) + + +func _apply_environment(cycle: float) -> void: + if directional_light == null: + return + + var day_factor := _day_factor(cycle) + var night_factor := 1.0 - day_factor + + 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 sunset_peak := _sunrise_sunset_weight(cycle) + if sunset_peak > 0.0: + var sunset_color: Color + if cycle < 0.5: + sunset_color = SUNSET_LIGHT_COLOR + else: + sunset_color = SUNRISE_LIGHT_COLOR + directional_light.light_color = directional_light.light_color.lerp( + sunset_color, sunset_peak * 0.85 + ) + directional_light.light_energy = maxf( + directional_light.light_energy, SUNRISE_SUNSET_ENERGY * sunset_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) + + +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.15: + return smoothstep(0.05, 0.15, cycle) * (1.0 - smoothstep(0.1, 0.15, cycle)) + if cycle > 0.85: + return smoothstep(0.85, 0.95, cycle) * (1.0 - smoothstep(0.9, 0.95, cycle)) + return 0.0 + + +func _lerp_color(from: Color, to: Color, weight: float) -> Color: + return from.lerp(to, clampf(weight, 0.0, 1.0))