Files
Project-M/Assets/_Project/Scripts/Client/Presentation/WorldAtmosphereSystem.cs
T
kronic 2fc5ae9a10 World: ground bundle — macro-blend ground materials, per-room dressing scatter, biome ground tint, base flora + worn paths
Phase 1.5b ground bundle (operator: "the ground is very plain"):
- Ground materials rebuilt: Grass/Sand base maps at real tiling (22x/26x, was a
  2x smear across 90-130u) + Synty normal maps + a generated 0.5-centered macro
  noise detail texture (LINEAR import — sRGB detail mul2x darkens 2.3x) with
  baked rim vignette. Meadow ground moved OFF the shared Synty asset onto
  project-owned Mat_Ground_Meadow.
- WorldAtmosphereSystem: per-room-biome ground tint on the expedition quads via
  MaterialPropertyBlock (Meadow green / Cavern blue / Blight purple; Arid sand
  default), lerped alongside the existing fog/ambient palettes.
- RoomDressingSystem + RoomDressingConfig (new, client-only observe-only):
  26 biome-flavoured cosmetic props per room, scattered inside the room's ACTUAL
  shape (same RoomLayoutMath authority as the server, distinct 0xD2E55 hash
  stream), deterministic from the replicated run seed, torn down with the room,
  colliders stripped. 47 curated prefab refs wired in Game.unity (build-safe).
- Base: 357 curated flora props re-enabled (bushes/wildflowers/grass clumps;
  buildings/pond/FX stay parked) + generated worn-dirt paths storage->warpgate
  and storage->base gate.

Live-verified: dressing 26/26 inside x[981,1023] z[-11,12] y=0, 11 varieties,
0 colliders; teardown on run end; Meadow tint converged to exact target; 470
EditMode green; console clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:47:44 -07:00

106 lines
6.0 KiB
C#

using Unity.Entities;
using UnityEngine;
namespace ProjectM.Client
{
/// <summary>
/// Client-only ATMOSPHERE cross-fade for the two-region world. A managed presentation system
/// (SystemBase, main thread, NO Burst) in <see cref="PresentationSystemGroup"/> that OBSERVES the
/// follow-camera's world-space X and lerps <see cref="RenderSettings"/> fog + Trilight sky-ambient between
/// the lush cyan meadow base (origin) and the hostile orange arid expedition (+1000 X) — mirroring
/// HudSystem's region split at camera X &gt; 500. The directional light + skybox + post-FX volume stay
/// GLOBAL; only fog colour/density + ambient sky colour shift per region. Pure presentation: never mutates
/// the simulation, no ECS query (camera + RenderSettings only). Knobs live in
/// <see cref="WorldAtmosphereConfig"/> (falls back to baked defaults when absent). Guarded to the gameplay
/// "Game" scene so it never restyles the menu. Fog enable/mode + equator/ground ambient are baked into the
/// scene; this only drives the per-region deltas. Phase 1.5b: also drives a per-room-biome tint on the
/// expedition ground quads (MaterialPropertyBlock — the material asset is never touched).
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
[UpdateInGroup(typeof(PresentationSystemGroup))]
public partial class WorldAtmosphereSystem : SystemBase
{
Camera _cam;
MeshRenderer[] _ground;
MaterialPropertyBlock _mpb;
Color _groundCur;
static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
protected override void OnUpdate()
{
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name != "Game") return;
if (_cam == null) _cam = Camera.main;
if (_cam == null) return;
var cfg = WorldAtmosphereConfig.Instance;
float boundary = cfg != null ? cfg.BoundaryX : 500f;
float half = cfg != null ? Mathf.Max(1f, cfg.BlendHalfWidth) : 80f;
Color baseFog = cfg != null ? cfg.BaseFogColor : new Color(0.09f, 0.13f, 0.14f, 1f);
float baseDen = cfg != null ? cfg.BaseFogDensity : 0.010f;
Color baseAmb = cfg != null ? cfg.BaseAmbientSky : new Color(0.32f, 0.38f, 0.48f, 1f);
Color expFog = cfg != null ? cfg.ExpeditionFogColor : new Color(0.24f, 0.13f, 0.06f, 1f);
float expDen = cfg != null ? cfg.ExpeditionFogDensity : 0.016f;
Color expAmb = cfg != null ? cfg.ExpeditionAmbientSky : new Color(0.45f, 0.31f, 0.20f, 1f);
// Step 14 (expedition redesign): the EXPEDITION-side palette keys on the replicated per-room biome
// (RunInfo.CurrentBiome) so every room re-themes — the camera-X blend still owns the base↔run fade.
// Palettes are code consts (cosmetic; per-biome config knobs can layer on later without churn).
// Phase 1.5: all palettes moved to the dark-ambient set (dynamic lights carry readability).
// Ground tints are ABSOLUTE _BaseColor values (the MPB replaces the authored arid sand tint).
Color groundTarget = new Color(0.60f, 0.56f, 0.50f, 1f); // authored arid sand (Mat_Ground_Arid_Dark)
bool haveRun = SystemAPI.TryGetSingleton<ProjectM.Simulation.RunInfo>(out var runInfo);
if (haveRun && runInfo.Lifecycle != ProjectM.Simulation.RunLifecycle.Staging)
{
switch (runInfo.CurrentBiome)
{
case ProjectM.Simulation.RoomBiomeId.Meadow:
expFog = new Color(0.10f, 0.16f, 0.10f, 1f); expDen = 0.012f;
expAmb = new Color(0.30f, 0.42f, 0.32f, 1f);
groundTarget = new Color(0.47f, 0.55f, 0.43f, 1f);
break;
case ProjectM.Simulation.RoomBiomeId.Cavern:
expFog = new Color(0.06f, 0.08f, 0.12f, 1f); expDen = 0.020f;
expAmb = new Color(0.24f, 0.28f, 0.38f, 1f);
groundTarget = new Color(0.42f, 0.46f, 0.55f, 1f);
break;
case ProjectM.Simulation.RoomBiomeId.Blight:
expFog = new Color(0.13f, 0.07f, 0.15f, 1f); expDen = 0.022f;
expAmb = new Color(0.32f, 0.24f, 0.38f, 1f);
groundTarget = new Color(0.52f, 0.42f, 0.58f, 1f);
break;
// Arid keeps the config/default burnt-dusk orange + sand ground above.
}
}
float x = _cam.transform.position.x;
float t = Mathf.Clamp01((x - (boundary - half)) / (2f * half));
t = t * t * (3f - 2f * t); // smoothstep
RenderSettings.fogColor = Color.Lerp(baseFog, expFog, t);
RenderSettings.fogDensity = Mathf.Lerp(baseDen, expDen, t);
RenderSettings.ambientSkyColor = Color.Lerp(baseAmb, expAmb, t);
// Per-room-biome ground tint (expedition quads only; the meadow base keeps its authored material).
if (_ground == null)
{
var found = new System.Collections.Generic.List<MeshRenderer>();
foreach (var mr in Object.FindObjectsByType<MeshRenderer>(FindObjectsSortMode.None))
if (mr.gameObject.name == "Ground_Arid") found.Add(mr);
if (found.Count > 0)
{
_ground = found.ToArray();
_mpb = new MaterialPropertyBlock();
_groundCur = groundTarget; // snap on first sight — no lerp from black
}
}
if (_ground != null)
{
_groundCur = Color.Lerp(_groundCur, groundTarget, 1f - Mathf.Exp(-2.5f * UnityEngine.Time.deltaTime));
_mpb.SetColor(BaseColorId, _groundCur);
for (int i = 0; i < _ground.Length; i++)
if (_ground[i] != null) _ground[i].SetPropertyBlock(_mpb);
}
}
}
}