// Copyright Pacificao. All Rights Reserved. #include "AgrarianSkyLightingController.h" #include "AgrarianGameState.h" #include "AgrarianPerformanceStats.h" #include "Components/DirectionalLightComponent.h" #include "Components/ExponentialHeightFogComponent.h" #include "Components/SceneComponent.h" #include "Components/SkyLightComponent.h" #include "Engine/World.h" #include "ProfilingDebugging/CpuProfilerTrace.h" AAgrarianSkyLightingController::AAgrarianSkyLightingController() { PrimaryActorTick.bCanEverTick = true; bReplicates = false; SceneRoot = CreateDefaultSubobject(TEXT("SceneRoot")); RootComponent = SceneRoot; SunLight = CreateDefaultSubobject(TEXT("SunLight")); SunLight->SetupAttachment(SceneRoot); SunLight->SetIntensity(NoonSunIntensity); SunLight->SetLightColor(FLinearColor(1.0f, 0.96f, 0.86f)); SunLight->SetMobility(EComponentMobility::Movable); SkyLight = CreateDefaultSubobject(TEXT("SkyLight")); SkyLight->SetupAttachment(SceneRoot); SkyLight->SetIntensity(ClearSkyLightIntensity); SkyLight->SetMobility(EComponentMobility::Movable); HeightFog = CreateDefaultSubobject(TEXT("HeightFog")); HeightFog->SetupAttachment(SceneRoot); HeightFog->SetFogDensity(ClearFogDensity); HeightFog->SetMobility(EComponentMobility::Movable); } void AAgrarianSkyLightingController::BeginPlay() { Super::BeginPlay(); RefreshSkyLighting(); } void AAgrarianSkyLightingController::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); RefreshSkyLighting(); } void AAgrarianSkyLightingController::RefreshSkyLighting() { SCOPE_CYCLE_COUNTER(STAT_AgrarianSkyLightingRefresh); TRACE_CPUPROFILER_EVENT_SCOPE(AgrarianSkyLightingRefresh); const UWorld* World = GetWorld(); const AAgrarianGameState* GameState = World ? World->GetGameState() : nullptr; if (!GameState) { return; } const float SunriseHour = GameState->bHasActiveTileSolarData ? GameState->SunriseHourLocal : 6.0f; const float SunsetHour = GameState->bHasActiveTileSolarData ? GameState->SunsetHourLocal : 20.0f; CurrentSunAlpha = CalculateSunAlpha(GameState->WorldHours, SunriseHour, SunsetHour); CurrentWeather = GameState->Weather; CurrentCloudAlpha = CalculateWeatherCloudAlpha( GameState->Weather, GameState->ActiveWeatherInputs.CloudCoverPercent, GameState->ActiveWeatherInputs.bHasProviderData); const float WeatherLightMultiplier = FMath::Lerp(1.0f, 0.35f, CurrentCloudAlpha); const float SunIntensity = FMath::Lerp(NightSunIntensity, NoonSunIntensity, CurrentSunAlpha) * WeatherLightMultiplier; const float SkyIntensity = FMath::Lerp(NightSkyLightIntensity, ClearSkyLightIntensity, CurrentSunAlpha) * FMath::Lerp(1.0f, 0.55f, CurrentCloudAlpha); const float FogDensity = FMath::Lerp(ClearFogDensity, StormFogDensity, CurrentCloudAlpha); const float SunPitch = FMath::Lerp(-8.0f, -72.0f, CurrentSunAlpha); if (SunLight) { SunLight->SetWorldRotation(FRotator(SunPitch, NorthYawDegrees, 0.0f)); SunLight->SetIntensity(SunIntensity); SunLight->SetLightColor(CalculateSunColor(CurrentSunAlpha, CurrentCloudAlpha)); } if (SkyLight) { SkyLight->SetIntensity(SkyIntensity); } if (HeightFog) { HeightFog->SetFogDensity(FogDensity); } } float AAgrarianSkyLightingController::CalculateSunAlpha(float HourOfDay, float SunriseHour, float SunsetHour) const { const float NormalizedHour = FMath::Fmod(HourOfDay + 24.0f, 24.0f); const float SafeSunrise = FMath::Fmod(SunriseHour + 24.0f, 24.0f); const float SafeSunset = FMath::Fmod(SunsetHour + 24.0f, 24.0f); const float DayLength = FMath::Max(0.1f, SafeSunset >= SafeSunrise ? SafeSunset - SafeSunrise : (24.0f - SafeSunrise) + SafeSunset); float HoursSinceSunrise = NormalizedHour - SafeSunrise; if (HoursSinceSunrise < 0.0f) { HoursSinceSunrise += 24.0f; } if (HoursSinceSunrise > DayLength) { return 0.0f; } const float DayProgress = FMath::Clamp(HoursSinceSunrise / DayLength, 0.0f, 1.0f); return FMath::Clamp(FMath::Sin(PI * DayProgress), 0.0f, 1.0f); } float AAgrarianSkyLightingController::CalculateWeatherCloudAlpha(EAgrarianWeatherType Weather, float ProviderCloudCoverPercent, bool bHasProviderCloudCover) const { float WeatherAlpha = 0.0f; switch (Weather) { case EAgrarianWeatherType::Rain: WeatherAlpha = 0.65f; break; case EAgrarianWeatherType::ColdWind: WeatherAlpha = 0.45f; break; case EAgrarianWeatherType::Storm: WeatherAlpha = 1.0f; break; default: WeatherAlpha = 0.0f; break; } if (bHasProviderCloudCover) { WeatherAlpha = FMath::Max(WeatherAlpha, FMath::Clamp(ProviderCloudCoverPercent / 100.0f, 0.0f, 1.0f)); } return WeatherAlpha; } FLinearColor AAgrarianSkyLightingController::CalculateSunColor(float SunAlpha, float CloudAlpha) const { const FLinearColor DawnColor(1.0f, 0.62f, 0.38f); const FLinearColor NoonColor(1.0f, 0.96f, 0.86f); const FLinearColor StormColor(0.52f, 0.58f, 0.66f); const FLinearColor TimeColor = FLinearColor::LerpUsingHSV(DawnColor, NoonColor, FMath::Clamp(SunAlpha, 0.0f, 1.0f)); return FLinearColor::LerpUsingHSV(TimeColor, StormColor, FMath::Clamp(CloudAlpha, 0.0f, 1.0f)); }