From 8d4078989c23473552369b00410dfcbd10d9f15b Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Fri, 7 Aug 2026 22:26:02 +0100 Subject: [PATCH] feat(arena): add grounded lighting and asteroid dressing --- Game/scenes/arena_03.tscn | 32 ++++++++++++++++++- Game/scenes/arena_base.tscn | 55 ++++++++++++++++++++++++++++++++ Game/shaders/arena_deck.gdshader | 14 +++----- TODO.md | 6 ++-- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/Game/scenes/arena_03.tscn b/Game/scenes/arena_03.tscn index 56b858b6..036affeb 100644 --- a/Game/scenes/arena_03.tscn +++ b/Game/scenes/arena_03.tscn @@ -1,7 +1,10 @@ -[gd_scene load_steps=4 format=3] +[gd_scene load_steps=7 format=3] [ext_resource type="PackedScene" path="res://scenes/arena_base.tscn" id="1_base"] [ext_resource type="Shader" path="res://shaders/sky_asteroid_field.gdshader" id="7_asteroid"] +[ext_resource type="PackedScene" path="res://assets/models/nebula_station.glb" id="8_station"] +[ext_resource type="PackedScene" path="res://assets/models/nebula_debris.glb" id="9_debris"] +[ext_resource type="PackedScene" path="res://assets/models/nebula_planet.glb" id="10_planet"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_asteroid"] shader = ExtResource("7_asteroid") @@ -22,3 +25,30 @@ light_color = Color(1, 0.85, 0.65, 1) light_color = Color(0.55, 0.4, 0.3, 1) [node name="Decoration" type="Node3D" parent="."] + +[node name="MiningStationA" parent="Decoration" instance=ExtResource("8_station")] +transform = Transform3D(2.1, 0, 0, 0, 2.1, 0, 0, 0, 2.1, 44, 8, -12) +rotation = Vector3(0.08, -0.72, 0.02) + +[node name="MiningStationB" parent="Decoration" instance=ExtResource("8_station")] +transform = Transform3D(1.45, 0, 0, 0, 1.45, 0, 0, 0, 1.45, -48, 15, 22) +rotation = Vector3(-0.06, 1.2, 0.08) + +[node name="AsteroidClusterA" parent="Decoration" instance=ExtResource("9_debris")] +transform = Transform3D(2.4, 0, 0, 0, 2.4, 0, 0, 0, 2.4, -35, 12, -25) +rotation = Vector3(0.4, 0.9, -0.2) + +[node name="AsteroidClusterB" parent="Decoration" instance=ExtResource("9_debris")] +transform = Transform3D(1.7, 0, 0, 0, 1.7, 0, 0, 0, 1.7, 38, 20, 28) +rotation = Vector3(-0.25, 2.1, 0.35) + +[node name="AsteroidClusterC" parent="Decoration" instance=ExtResource("9_debris")] +transform = Transform3D(1.2, 0, 0, 0, 1.2, 0, 0, 0, 1.2, 18, 7, -48) +rotation = Vector3(0.7, -0.5, 0.1) + +[node name="AsteroidClusterD" parent="Decoration" instance=ExtResource("9_debris")] +transform = Transform3D(0.9, 0, 0, 0, 0.9, 0, 0, 0, 0.9, -20, 25, 50) +rotation = Vector3(-0.3, -1.1, -0.2) + +[node name="DistantPlanet" parent="Decoration" instance=ExtResource("10_planet")] +transform = Transform3D(24, 0, 0, 0, 24, 0, 0, 0, 24, 95, 48, -145) diff --git a/Game/scenes/arena_base.tscn b/Game/scenes/arena_base.tscn index c9a99b5e..1e3a8277 100644 --- a/Game/scenes/arena_base.tscn +++ b/Game/scenes/arena_base.tscn @@ -31,6 +31,23 @@ adjustment_enabled = true adjustment_brightness = 1.0 adjustment_contrast = 1.05 adjustment_saturation = 1.08 +ssao_enabled = true +ssao_radius = 2.5 +ssao_intensity = 2.0 +ssao_power = 1.5 +ssao_detail = 0.75 +ssao_horizon = 0.06 +ssao_sharpness = 0.98 +ssao_light_affect = 0.15 +ssil_enabled = true +ssil_radius = 4.0 +ssil_intensity = 1.25 +ssil_sharpness = 0.98 +ssil_normal_rejection = 1.0 +sdfgi_enabled = true +sdfgi_use_occlusion = true +sdfgi_bounce_feedback = 0.5 +sdfgi_read_sky_light = true [node name="Arena" type="Node3D"] script = ExtResource("1_iywne") @@ -49,6 +66,44 @@ light_color = Color(0.55, 0.65, 0.85, 1) light_energy = 0.4 shadow_enabled = false +[node name="PitchLights" type="Node3D" parent="."] + +[node name="Team0GoalLight" type="OmniLight3D" parent="PitchLights"] +position = Vector3(0, 4.5, 22) +light_color = Color(0.48, 0.18, 0.88, 1) +light_energy = 5.0 +light_bake_mode = 1 +omni_range = 16.0 +omni_attenuation = 1.35 +shadow_enabled = true + +[node name="Team1GoalLight" type="OmniLight3D" parent="PitchLights"] +position = Vector3(0, 4.5, -22) +light_color = Color(0.24, 0.86, 0.42, 1) +light_energy = 5.0 +light_bake_mode = 1 +omni_range = 16.0 +omni_attenuation = 1.35 +shadow_enabled = true + +[node name="MidfieldLightA" type="OmniLight3D" parent="PitchLights"] +position = Vector3(-10, 7, 0) +light_color = Color(0.38, 0.62, 1, 1) +light_energy = 3.5 +light_bake_mode = 1 +omni_range = 15.0 +omni_attenuation = 1.5 +shadow_enabled = true + +[node name="MidfieldLightB" type="OmniLight3D" parent="PitchLights"] +position = Vector3(10, 7, 0) +light_color = Color(0.38, 0.62, 1, 1) +light_energy = 3.5 +light_bake_mode = 1 +omni_range = 15.0 +omni_attenuation = 1.5 +shadow_enabled = true + [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_base") diff --git a/Game/shaders/arena_deck.gdshader b/Game/shaders/arena_deck.gdshader index 49fa6ef1..1755fe4a 100644 --- a/Game/shaders/arena_deck.gdshader +++ b/Game/shaders/arena_deck.gdshader @@ -14,11 +14,6 @@ uniform vec3 deck_color : source_color = vec3(0.085, 0.095, 0.122); // Kept close to deck_color on purpose: per-plate variation is meant to break // up a 24x36 m plane, not to reintroduce visible tiling. uniform vec3 panel_color : source_color = vec3(0.104, 0.116, 0.146); -// Stand-in for bounce light. The arena is lit by one dim directional plus low -// ambient, so hull facing away from it — the goal bulkheads, the fillet caps -// either side of each mouth — otherwise crushed to pure black and the whole -// end zone read as a void with a goal floating in it. -uniform float hull_fill : hint_range(0.0, 2.0) = 0.55; uniform float panel_variation : hint_range(0.0, 1.0) = 0.4; uniform vec3 line_color : source_color = vec3(0.62, 0.78, 1.0); // Must track TeamColors.TEAM_COLORS in scripts/team_colors.gd — shaders can't @@ -47,9 +42,9 @@ uniform float seam_width = 0.22; uniform float end_zone_strength : hint_range(0.0, 1.0) = 0.12; uniform float panel_size = 4.0; uniform float panel_line_width : hint_range(0.0, 0.5) = 0.05; -// Deliberately not a high metallic value: this arena is lit by one dim -// directional plus low ambient, and a near-metal deck has almost no diffuse -// response, which crushed the plating to black. +// Deliberately not a high metallic value: the local pitch lights and SDFGI +// need enough diffuse response to reveal the plating instead of turning it +// into a dark mirror at grazing angles. uniform float deck_metallic : hint_range(0.0, 1.0) = 0.3; uniform float deck_roughness : hint_range(0.0, 1.0) = 0.34; @@ -135,8 +130,7 @@ void fragment() { ALBEDO = mix(albedo, line_color, marks); METALLIC = deck_metallic * (1.0 - marks); ROUGHNESS = clamp(mix(rough, 0.5, marks), 0.05, 1.0); - EMISSION = albedo * hull_fill - + line_color * marks * line_emission + EMISSION = line_color * marks * line_emission + seam_color * seam * seam_emission + zone * 0.015 * flat_deck; } diff --git a/TODO.md b/TODO.md index d571524a..16384aeb 100644 --- a/TODO.md +++ b/TODO.md @@ -15,13 +15,13 @@ The training pipeline is built — see `TRAINING.md` (self-play PPO via the vend The largest gap between this and a AAA-feeling product is presentation, not code. Sequenced after the above for pragmatic reasons, but this is the highest impact per hour. - [ ] **Audio — there is none.** Zero sound files, zero `AudioStreamPlayer` nodes, no bus layout. Needs: engine hum pitched to throttle, turbo whoosh, ball impacts scaled by collision impulse, wall scrapes, goal explosion, crowd bed, UI clicks, countdown beeps, music. Can be driven off `Ship`'s existing telemetry signals. -- [ ] SSAO/SSIL in the arena Environments — cheapest single perceived-quality win available; grounds the ships against the deck and gives the fillets and goal recesses real depth. +- [x] SSAO/SSIL in the arena Environments — cheapest single perceived-quality win available; grounds the ships against the deck and gives the fillets and goal recesses real depth. - [ ] VFX on anything that moves: `EngineGlowL/R` are static meshes that don't respond to throttle. No thruster plume, turbo flame, ball trail, impact sparks or goal burst. `arena_02` is the only scene with any particles at all. - [ ] Impact feedback — screen shake, hit-stop, flash, controller rumble on ball contact. - [ ] Camera feel in `ship_camera.gd` — fixed distance/height/FOV today. Speed-based FOV widening, turbo kick, impact shake. - [ ] Goal celebration sequence: today it's a `print()` and a label scale-pop. Wants an explosion, team-tinted screen flash, camera cut, slow-mo, title card. `HUDController`'s `ResultOverlay` animation is a reasonable template. -- [ ] Local lighting / LightmapGI bake — two directional lights and low ambient, no local lights anywhere. `arena_deck.gdshader`'s `hull_fill` uniform is explicitly a "stand-in for bounce light", i.e. the shader is compensating for lighting that isn't there. The arena is fully static, so a bake is viable; retire `hull_fill` afterwards. -- [ ] Dress `arena_03` — its `Decoration` node is empty, while `arena_02` has stations, debris, a planet and volumetric dust. +- [x] Local lighting / dynamic GI — four local pitch lights plus SDFGI provide bounce and contact lighting for the runtime-generated arena shell; the shader's `hull_fill` stand-in is retired. (The shell is generated in `_ready`, so it cannot participate in an editor LightmapGI bake.) +- [x] Dress `arena_03` — its asteroid field now has mining stations, debris clusters and a distant planet. - [ ] Custom font + a real `Theme` resource for the HUD. The procedural instruments are well-engineered, but `ThemeDB.fallback_font` at 10-13 px reads as a debug overlay. - [ ] Post-processing beyond glow: DoF, motion blur, vignette, chromatic aberration on turbo.