From 4054e2c0ddb8cc267136e9f50241f16636a0810e Mon Sep 17 00:00:00 2001 From: Rijad Zuzo Date: Wed, 8 Jul 2026 17:17:34 +0200 Subject: [PATCH] feat: strengthen water and foreground silhouettes - Upgrade river shader with multi-layer UV flow, fresnel edges, foam highlights, and specular sparkle - Upgrade waterfall shader with dual-band scrolling, distinct foam contrast, and rim transparency - Widen river (8m) and waterfall (8m) for greater visual presence - Increase waterfall mist particles to 72 with wider spread and longer lifetime - Add fortress crenellations (10), three turrets, and prominent ridge banner for readable skyline silhouette - Remove duplicate banner nodes from JajceWorld instance (now owned by FortressBlockout scene) - Update BUILD_IN_PUBLIC_PLAN and LEARNING_ROADMAP to reflect completion --- docs/BUILD_IN_PUBLIC_PLAN.md | 15 +++- docs/LEARNING_ROADMAP.md | 4 ++ world/jajce/FortressBlockout.tscn | 90 ++++++++++++++++++++++-- world/jajce/JajceWorld.tscn | 8 --- world/jajce/WaterBody.tscn | 4 +- world/jajce/WaterfallBody.tscn | 4 +- world/jajce/WaterfallMist.tscn | 26 +++---- world/jajce/materials/water.gdshader | 52 +++++++++++--- world/jajce/materials/waterfall.gdshader | 49 ++++++++++--- 9 files changed, 200 insertions(+), 52 deletions(-) diff --git a/docs/BUILD_IN_PUBLIC_PLAN.md b/docs/BUILD_IN_PUBLIC_PLAN.md index ce25514..8fdc568 100644 --- a/docs/BUILD_IN_PUBLIC_PLAN.md +++ b/docs/BUILD_IN_PUBLIC_PLAN.md @@ -644,10 +644,21 @@ Completed: study desk, and rest bench now have compact presentation props that remain readable in the runtime cinematic frame. +Completed: + +15. Water and foreground silhouette strengthening: river/waterfall shaders + now include multi-layer UV flow, fresnel edges, foam highlights, sparkle + specular, and better color depth; the waterfall has distinct foam bands + and rim transparency; mist particle count and spread increased; the river + and waterfall widened; the fortress ridge landmark now has crenellations, + a dedicated banner pole with larger banner, and three turrets for a more + readable silhouette against the sky. + Next: -1. Strengthen water and foreground silhouettes without adding new simulation - mechanics. +1. Continue growing resource discovery with finite ResourceNode instances + placed in meaningful contexts (from LEARNING_ROADMAP). See the roadmap + for the next systems priority. Do not start with GIS data, a full city, a large asset pack, or more NPC mechanics. The next proof is a beautiful stage for the systems that already diff --git a/docs/LEARNING_ROADMAP.md b/docs/LEARNING_ROADMAP.md index f2aa6d1..9ecfe87 100644 --- a/docs/LEARNING_ROADMAP.md +++ b/docs/LEARNING_ROADMAP.md @@ -689,6 +689,10 @@ Recently completed: authored path strips that reveal the active village task loop. - Landmark and work-site silhouette props make the ridge landmark, pantry, guard, study, and rest sites easier to identify without debug labels. +- Water and foreground silhouettes strengthened: river/waterfall shaders with + multi-layer UV flow, fresnel edges, foam highlights, and sparkle; wider river + and waterfall; fortress with crenellations, three turrets, and prominent + ridge banner. This order strengthens the simulation while regularly producing visible progress suitable for public development updates. diff --git a/world/jajce/FortressBlockout.tscn b/world/jajce/FortressBlockout.tscn index ed8770d..20ac413 100644 --- a/world/jajce/FortressBlockout.tscn +++ b/world/jajce/FortressBlockout.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3] +[gd_scene load_steps=12 format=3] [sub_resource type="StandardMaterial3D" id="Material_keep"] albedo_color = Color(0.45, 0.42, 0.38, 1) @@ -6,15 +6,15 @@ roughness = 0.85 [sub_resource type="BoxMesh" id="Mesh_keep"] material = SubResource("Material_keep") -size = Vector3(6, 6, 8) +size = Vector3(7, 6, 9) [sub_resource type="StandardMaterial3D" id="Material_parapet"] -albedo_color = Color(0.45, 0.42, 0.38, 1) +albedo_color = Color(0.42, 0.39, 0.35, 1) roughness = 0.85 [sub_resource type="BoxMesh" id="Mesh_parapet"] material = SubResource("Material_parapet") -size = Vector3(5, 1.5, 7) +size = Vector3(6.6, 1.5, 7.8) [sub_resource type="StandardMaterial3D" id="Material_turret"] albedo_color = Color(0.45, 0.42, 0.38, 1) @@ -26,6 +26,28 @@ top_radius = 0.6 bottom_radius = 0.7 height = 4.0 +[sub_resource type="BoxMesh" id="Mesh_crenellation"] +material = SubResource("Material_parapet") +size = Vector3(0.55, 0.9, 0.7) + +[sub_resource type="StandardMaterial3D" id="Material_pole"] +albedo_color = Color(0.24, 0.13, 0.07, 1) +roughness = 0.82 + +[sub_resource type="CylinderMesh" id="Mesh_pole"] +material = SubResource("Material_pole") +top_radius = 0.08 +bottom_radius = 0.1 +height = 3.5 + +[sub_resource type="StandardMaterial3D" id="Material_banner"] +albedo_color = Color(0.68, 0.12, 0.1, 1) +roughness = 0.65 + +[sub_resource type="BoxMesh" id="Mesh_banner"] +material = SubResource("Material_banner") +size = Vector3(0.14, 2.0, 1.25) + [node name="FortressBlockout" type="Node3D"] [node name="Keep" type="MeshInstance3D" parent="."] @@ -36,6 +58,62 @@ mesh = SubResource("Mesh_keep") position = Vector3(0, 6.75, 0) mesh = SubResource("Mesh_parapet") -[node name="Turret" type="MeshInstance3D" parent="."] -position = Vector3(2.8, 2.0, 3.5) +[node name="Crenellation_01" type="MeshInstance3D" parent="."] +position = Vector3(-2.8, 7.7, -3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_02" type="MeshInstance3D" parent="."] +position = Vector3(-1.4, 7.7, -3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_03" type="MeshInstance3D" parent="."] +position = Vector3(0, 7.7, -3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_04" type="MeshInstance3D" parent="."] +position = Vector3(1.4, 7.7, -3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_05" type="MeshInstance3D" parent="."] +position = Vector3(2.8, 7.7, -3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_06" type="MeshInstance3D" parent="."] +position = Vector3(-2.8, 7.7, 3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_07" type="MeshInstance3D" parent="."] +position = Vector3(-1.4, 7.7, 3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_08" type="MeshInstance3D" parent="."] +position = Vector3(0, 7.7, 3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_09" type="MeshInstance3D" parent="."] +position = Vector3(1.4, 7.7, 3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Crenellation_10" type="MeshInstance3D" parent="."] +position = Vector3(2.8, 7.7, 3.1) +mesh = SubResource("Mesh_crenellation") + +[node name="Turret_A" type="MeshInstance3D" parent="."] +position = Vector3(-3.2, 2.0, -4) mesh = SubResource("Mesh_turret") + +[node name="Turret_B" type="MeshInstance3D" parent="."] +position = Vector3(3.2, 2.0, -4) +mesh = SubResource("Mesh_turret") + +[node name="Turret_C" type="MeshInstance3D" parent="."] +position = Vector3(3.2, 2.0, 4) +mesh = SubResource("Mesh_turret") + +[node name="RidgeBannerPole" type="MeshInstance3D" parent="."] +position = Vector3(0, 8.4, 3.8) +mesh = SubResource("Mesh_pole") + +[node name="RidgeBanner" type="MeshInstance3D" parent="."] +position = Vector3(0, 9.2, 4.45) +mesh = SubResource("Mesh_banner") diff --git a/world/jajce/JajceWorld.tscn b/world/jajce/JajceWorld.tscn index 5e84d2f..64c5a1b 100644 --- a/world/jajce/JajceWorld.tscn +++ b/world/jajce/JajceWorld.tscn @@ -186,14 +186,6 @@ position = Vector3(-25, 7, 9) [node name="Keep" parent="FortressBlockout" instance=ExtResource("5_fortress")] -[node name="RidgeBannerPole" type="MeshInstance3D" parent="FortressBlockout/Keep"] -position = Vector3(2.2, 8.6, 2.8) -mesh = SubResource("Mesh_site_pole") - -[node name="RidgeBanner" type="MeshInstance3D" parent="FortressBlockout/Keep"] -position = Vector3(2.2, 8.95, 3.35) -mesh = SubResource("Mesh_landmark_banner") - [node name="WaterRoot" type="Node3D" parent="."] [node name="RiverSurface" parent="WaterRoot" instance=ExtResource("9_water")] diff --git a/world/jajce/WaterBody.tscn b/world/jajce/WaterBody.tscn index 2c53cdc..0c097d0 100644 --- a/world/jajce/WaterBody.tscn +++ b/world/jajce/WaterBody.tscn @@ -7,10 +7,10 @@ shader = ExtResource("1_water_shader") [sub_resource type="BoxMesh" id="Mesh_water_body"] material = SubResource("ShaderMaterial_water") -size = Vector3(5, 0.08, 56) +size = Vector3(8, 0.08, 58) [node name="WaterBody" type="Node3D"] -position = Vector3(25, 0.12, 0) +position = Vector3(25, 0.08, 0) [node name="Mesh" type="MeshInstance3D" parent="."] mesh = SubResource("Mesh_water_body") diff --git a/world/jajce/WaterfallBody.tscn b/world/jajce/WaterfallBody.tscn index 7dfb0c8..4708fef 100644 --- a/world/jajce/WaterfallBody.tscn +++ b/world/jajce/WaterfallBody.tscn @@ -7,10 +7,10 @@ shader = ExtResource("1_waterfall_shader") [sub_resource type="BoxMesh" id="Mesh_waterfall_body"] material = SubResource("ShaderMaterial_waterfall") -size = Vector3(5, 10, 0.5) +size = Vector3(8, 12, 0.5) [node name="WaterfallBody" type="Node3D"] -position = Vector3(25, 5, -24) +position = Vector3(25, 6, -24) [node name="Mesh" type="MeshInstance3D" parent="."] mesh = SubResource("Mesh_waterfall_body") diff --git a/world/jajce/WaterfallMist.tscn b/world/jajce/WaterfallMist.tscn index 7bad3bf..970d906 100644 --- a/world/jajce/WaterfallMist.tscn +++ b/world/jajce/WaterfallMist.tscn @@ -1,19 +1,19 @@ [gd_scene load_steps=4 format=3] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"] -lifetime_randomness = 0.2 +lifetime_randomness = 0.35 spread = 180.0 -gravity = Vector3(0, -0.5, 0) -initial_velocity_min = 0.3 -initial_velocity_max = 0.8 -scale_min = 0.1 -scale_max = 1.5 -color = Color(0.85, 0.95, 0.95, 0.4) +gravity = Vector3(0, -0.35, 0) +initial_velocity_min = 0.25 +initial_velocity_max = 1.2 +scale_min = 0.08 +scale_max = 2.0 +color = Color(0.82, 0.94, 0.96, 0.35) emission_shape = 1 -emission_box_extents = Vector3(2.5, 1.0, 0.5) +emission_box_extents = Vector3(4.5, 1.5, 1.0) [sub_resource type="StandardMaterial3D" id="Material_mist"] -albedo_color = Color(0.85, 0.95, 0.95, 0.35) +albedo_color = Color(0.82, 0.94, 0.96, 0.28) transparency = 1 shading_mode = 0 billboard_mode = 1 @@ -21,14 +21,14 @@ cull_mode = 0 [sub_resource type="QuadMesh" id="Mesh_mist"] material = SubResource("Material_mist") -size = Vector2(1.5, 1.5) +size = Vector2(1.8, 1.8) [node name="WaterfallMist" type="GPUParticles3D"] emitting = true -amount = 48 -lifetime = 2.5 +amount = 72 +lifetime = 3.0 one_shot = false -preprocess = 1.0 +preprocess = 1.5 speed_scale = 1.0 local_coords = true draw_order = 0 diff --git a/world/jajce/materials/water.gdshader b/world/jajce/materials/water.gdshader index 3fa01c1..4f85114 100644 --- a/world/jajce/materials/water.gdshader +++ b/world/jajce/materials/water.gdshader @@ -1,20 +1,54 @@ shader_type spatial; render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; -uniform float wave_strength : hint_range(0.0, 1.0) = 0.15; -uniform float wave_speed : hint_range(0.0, 5.0) = 1.2; +uniform float wave_strength : hint_range(0.0, 0.5) = 0.08; +uniform float wave_speed : hint_range(0.0, 5.0) = 0.8; +uniform float flow_speed : hint_range(0.0, 3.0) = 0.6; +uniform vec3 deep_color : source_color = vec3(0.06, 0.24, 0.38); +uniform vec3 shallow_color : source_color = vec3(0.18, 0.44, 0.58); +uniform vec3 foam_color : source_color = vec3(0.82, 0.88, 0.86); +uniform float rim_power : hint_range(0.5, 5.0) = 2.5; +uniform float sparkle_strength : hint_range(0.0, 1.0) = 0.12; + +global uniform vec3 sun_direction = vec3(0.4, 0.8, 0.4); void vertex() { vec3 pos = VERTEX; - float wave = sin(pos.z * 0.4 + TIME * wave_speed) * wave_strength; - pos.y += wave; + float wave_a = sin(pos.x * 0.35 + pos.z * 0.25 + TIME * wave_speed) * wave_strength; + float wave_b = cos(pos.x * 0.45 - pos.z * 0.55 + TIME * wave_speed * 0.7) * wave_strength * 0.6; + float wave_c = sin(pos.z * 0.7 + TIME * wave_speed * 1.3) * wave_strength * 0.35; + pos.y += wave_a + wave_b + wave_c; VERTEX = pos; } void fragment() { - ALBEDO = vec3(0.12, 0.39, 0.55); - METALLIC = 0.15; - ROUGHNESS = 0.1; - EMISSION = vec3(0.05, 0.19, 0.24) * 0.45; - ALPHA = 0.72; + vec3 view_dir = VIEW; + vec3 normal = NORMAL; + vec3 view_norm = normalize(view_dir); + + float rim = 1.0 - abs(dot(normal, view_norm)); + rim = pow(rim, rim_power); + rim = smoothstep(0.15, 0.7, rim); + + vec2 uv = UV; + uv.x += TIME * flow_speed * 0.4; + uv.y += TIME * flow_speed * 0.3; + float flow_a = sin(uv.x * 8.0 + uv.y * 5.0) * 0.5 + 0.5; + float flow_b = cos(uv.y * 6.0 - uv.x * 4.0 + TIME * 0.25) * 0.5 + 0.5; + float flow = mix(flow_a, flow_b, 0.5); + + vec3 water_color = mix(deep_color, shallow_color, flow * 0.6 + 0.2); + water_color = mix(water_color, foam_color, rim * 0.65); + + vec3 half_vec = normalize(view_norm + normalize(sun_direction)); + float specular = pow(max(dot(normal, half_vec), 0.0), 120.0); + specular *= smoothstep(0.4, 0.85, rim); + float sparkle = specular * sparkle_strength; + + ALBEDO = water_color; + METALLIC = 0.08; + ROUGHNESS = 0.22; + EMISSION = water_color * 0.04 + foam_color * sparkle; + ALPHA = 0.76; + ALPHA_SCISSOR_THRESHOLD = 0.0; } diff --git a/world/jajce/materials/waterfall.gdshader b/world/jajce/materials/waterfall.gdshader index 9bcdd72..143ce97 100644 --- a/world/jajce/materials/waterfall.gdshader +++ b/world/jajce/materials/waterfall.gdshader @@ -1,7 +1,12 @@ shader_type spatial; render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; -uniform float scroll_speed : hint_range(0.0, 5.0) = 2.0; +uniform float scroll_speed : hint_range(0.0, 5.0) = 1.8; +uniform vec3 deep_color : source_color = vec3(0.1, 0.45, 0.55); +uniform vec3 mid_color : source_color = vec3(0.45, 0.78, 0.82); +uniform vec3 foam_color : source_color = vec3(0.9, 0.95, 0.95); +uniform float foam_contrast : hint_range(0.5, 4.0) = 2.2; +uniform float sparkle_strength : hint_range(0.0, 0.8) = 0.25; void vertex() { } @@ -9,13 +14,37 @@ void vertex() { void fragment() { vec2 uv = UV; uv.y += TIME * scroll_speed; - float pattern = fract(uv.y * 4.0); - pattern = smoothstep(0.3, 0.7, pattern); - pattern = mix(0.4, 1.0, pattern); - vec3 base_color = mix(vec3(0.6, 0.85, 0.9), vec3(1.0, 1.0, 1.0), pattern); - ALBEDO = base_color; - ALPHA = mix(0.3, 0.7, pattern); - EMISSION = vec3(0.36, 0.52, 0.54) * 0.35 * pattern; - METALLIC = 0.0; - ROUGHNESS = 0.2; + + float band_a = fract(uv.y * 5.0 + sin(uv.x * 2.0) * 0.15); + band_a = smoothstep(0.2, 0.55, band_a) * smoothstep(0.8, 0.45, band_a); + + float band_b = fract(uv.y * 3.5 + cos(uv.x * 2.5 + TIME * 0.3) * 0.12); + band_b = smoothstep(0.1, 0.5, band_b) * smoothstep(0.85, 0.4, band_b); + + float foam_a = fract(uv.y * 4.0); + foam_a = pow(smoothstep(0.0, 0.12, foam_a) * smoothstep(0.3, 0.15, foam_a), foam_contrast); + + float foam_b = fract(uv.y * 6.0 + uv.x * 1.5); + foam_b = pow(smoothstep(0.5, 0.58, foam_b) * smoothstep(0.7, 0.6, foam_b), foam_contrast * 0.8); + + float foam = foam_a * 0.7 + foam_b * 0.3; + + vec3 base = mix(deep_color, mid_color, band_a * 0.5 + band_b * 0.3); + vec3 color = mix(base, foam_color, foam); + + float sparkle = pow(foam_a, 3.0) * sparkle_strength; + float highlight = band_a * band_b * 0.35; + float emission_value = sparkle + highlight * 0.3; + + ALBEDO = color; + ALPHA = mix(0.25, 0.78, foam * 0.7 + 0.15); + EMISSION = color * emission_value; + METALLIC = 0.05; + ROUGHNESS = 0.15; + + vec3 normal = NORMAL; + vec3 view_dir = VIEW; + vec3 view_norm = normalize(view_dir); + float rim = 1.0 - abs(dot(normal, view_norm)); + ALPHA = mix(ALPHA, ALPHA * 0.5, rim * 0.6); }