214 lines
6.4 KiB
C++
214 lines
6.4 KiB
C++
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
|
|
|
#include "Carla.h"
|
|
#include "DynamicWeather.h"
|
|
|
|
#include "Util/IniFile.h"
|
|
|
|
#include "Components/ArrowComponent.h"
|
|
|
|
static FString GetIniFileName(const FString &MapName = TEXT(""))
|
|
{
|
|
const FString BaseName = TEXT("CarlaWeather");
|
|
constexpr auto Sep = TEXT(".");
|
|
constexpr auto Ext = TEXT(".ini");
|
|
return (MapName.IsEmpty() ? (BaseName + Ext) : (BaseName + Sep + MapName + Ext));
|
|
}
|
|
|
|
static bool GetWeatherIniFilePath(const FString &FileName, FString &FilePath)
|
|
{
|
|
FilePath = FPaths::Combine(FPaths::GameConfigDir(), FileName);
|
|
const bool bFileExists = FPaths::FileExists(FilePath);
|
|
if (!bFileExists) {
|
|
UE_LOG(LogCarla, Warning, TEXT("\"%s\" not found"), *FilePath);
|
|
}
|
|
return bFileExists;
|
|
}
|
|
|
|
static bool CheckWeatherValidity(const FWeatherDescription &Weather)
|
|
{
|
|
if (Weather.Name.IsEmpty()) {
|
|
UE_LOG(LogCarla, Error, TEXT("Weather doesn't have a name, please provide one"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ADynamicWeather::LoadWeatherDescriptionsFromFile(
|
|
const FString &MapName,
|
|
TArray<FWeatherDescription> &Descriptions)
|
|
{
|
|
// Try to load config file.
|
|
FString DefaultFilePath;
|
|
if (GetWeatherIniFilePath(GetIniFileName(), DefaultFilePath)) {
|
|
UE_LOG(LogCarla, Log, TEXT("Loading weather description from %s"), *DefaultFilePath);
|
|
IniFile ConfigFile(DefaultFilePath);
|
|
|
|
{ // Override map specific presets.
|
|
FString MapOverridesFilePath;
|
|
if (GetWeatherIniFilePath(GetIniFileName(MapName), MapOverridesFilePath)) {
|
|
UE_LOG(LogCarla, Log, TEXT("Loading weather description from %s"), *MapOverridesFilePath);
|
|
ConfigFile.Combine(MapOverridesFilePath);
|
|
}
|
|
}
|
|
|
|
// For every section in the config file add a weather description.
|
|
for (auto &Item : ConfigFile.GetFConfigFile()) {
|
|
Descriptions.AddDefaulted(1u);
|
|
Descriptions.Last().ReadFromConfigFile(ConfigFile, Item.Key);
|
|
}
|
|
}
|
|
|
|
// If no description was found, append a defaulted one.
|
|
if (Descriptions.Num() == 0) {
|
|
UE_LOG(LogCarla, Warning, TEXT("No weather description found"));
|
|
Descriptions.AddDefaulted(1u);
|
|
Descriptions.Last().Name = TEXT("Default");
|
|
}
|
|
}
|
|
|
|
ADynamicWeather::ADynamicWeather(const FObjectInitializer& ObjectInitializer) :
|
|
Super(ObjectInitializer)
|
|
#if WITH_EDITORONLY_DATA
|
|
, FileName(GetIniFileName())
|
|
#endif // WITH_EDITORONLY_DATA
|
|
{
|
|
PrimaryActorTick.bCanEverTick = false;
|
|
|
|
RootComponent = ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneComponent0"));
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
ArrowComponent = CreateEditorOnlyDefaultSubobject<UArrowComponent>(TEXT("ArrowComponent0"));
|
|
if (ArrowComponent) {
|
|
ArrowComponent->ArrowColor = FColor(150, 200, 255);
|
|
ArrowComponent->bTreatAsASprite = true;
|
|
ArrowComponent->SpriteInfo.Category = TEXT("Lighting");
|
|
ArrowComponent->SpriteInfo.DisplayName = NSLOCTEXT( "SpriteCategory", "Lighting", "Lighting" );
|
|
ArrowComponent->SetupAttachment(RootComponent);
|
|
ArrowComponent->bLightAttachment = true;
|
|
ArrowComponent->bIsScreenSizeScaled = true;
|
|
}
|
|
#endif // WITH_EDITORONLY_DATA
|
|
}
|
|
|
|
void ADynamicWeather::OnConstruction(const FTransform &Transform)
|
|
{
|
|
Super::OnConstruction(Transform);
|
|
#if WITH_EDITOR
|
|
Update();
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
void ADynamicWeather::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
#if WITH_EDITOR
|
|
Update();
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void ADynamicWeather::PostEditChangeProperty(FPropertyChangedEvent &Event)
|
|
{
|
|
Super::PostEditChangeProperty(Event);
|
|
const FName PropertyName = (Event.Property != NULL ? Event.Property->GetFName() : NAME_None);
|
|
if (PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, Weather)) {
|
|
Update();
|
|
} else if ((PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, bSaveToConfigFile)) ||
|
|
(PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, bLoadFromConfigFile))) {
|
|
// Do nothing.
|
|
} else {
|
|
AdjustSunPositionBasedOnActorRotation();
|
|
}
|
|
if (bSaveToConfigFile) {
|
|
bSaveToConfigFile = false;
|
|
if (SaveToConfigFile()) {
|
|
UE_LOG(LogCarla, Log, TEXT("Weather \"%s\" saved to config file"), *Weather.Name);
|
|
} else {
|
|
UE_LOG(LogCarla, Error, TEXT("Error saving weather to config file"));
|
|
}
|
|
}
|
|
if (bLoadFromConfigFile) {
|
|
bLoadFromConfigFile = false;
|
|
if (LoadFromConfigFile()) {
|
|
UE_LOG(LogCarla, Log, TEXT("Weather \"%s\" loaded from config file"), *Weather.Name);
|
|
Update();
|
|
} else {
|
|
UE_LOG(LogCarla, Error, TEXT("Error loading weather from config file"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADynamicWeather::EditorApplyRotation(
|
|
const FRotator &DeltaRotation,
|
|
bool bAltDown,
|
|
bool bShiftDown,
|
|
bool bCtrlDown)
|
|
{
|
|
Super::EditorApplyRotation(DeltaRotation, bAltDown, bShiftDown, bCtrlDown);
|
|
AdjustSunPositionBasedOnActorRotation();
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
FVector ADynamicWeather::GetSunDirection() const
|
|
{
|
|
const FVector2D SphericalCoords(
|
|
FMath::DegreesToRadians(Weather.SunPolarAngle),
|
|
FMath::DegreesToRadians(Weather.SunAzimuthAngle));
|
|
return - SphericalCoords.SphericalToUnitCartesian();
|
|
}
|
|
|
|
void ADynamicWeather::AdjustSunPositionBasedOnActorRotation()
|
|
{
|
|
const FVector Direction = - GetActorQuat().GetForwardVector();
|
|
const FVector2D SphericalCoords = Direction.UnitCartesianToSpherical();
|
|
Weather.SunPolarAngle = FMath::RadiansToDegrees(SphericalCoords.X);
|
|
Weather.SunAzimuthAngle = FMath::RadiansToDegrees(SphericalCoords.Y);
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void ADynamicWeather::Update()
|
|
{
|
|
// Modify this actor's rotation according to Sun position.
|
|
if (!SetActorRotation(FQuat(GetSunDirection().Rotation()), ETeleportType::None)) {
|
|
UE_LOG(LogCarla, Warning, TEXT("Unable to rotate actor"));
|
|
}
|
|
|
|
if (bRefreshAutomatically) {
|
|
RefreshWeather();
|
|
}
|
|
}
|
|
|
|
bool ADynamicWeather::LoadFromConfigFile()
|
|
{
|
|
FString FilePath;
|
|
if (GetWeatherIniFilePath(FileName, FilePath) && CheckWeatherValidity(Weather)) {
|
|
IniFile ConfigFile(FilePath);
|
|
if (!ConfigFile.HasSection(Weather.Name)) {
|
|
UE_LOG(LogCarla, Error, TEXT("Weather \"%s\" is not present in config file"), *Weather.Name);
|
|
return false;
|
|
}
|
|
Weather.ReadFromConfigFile(ConfigFile, Weather.Name);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ADynamicWeather::SaveToConfigFile() const
|
|
{
|
|
FString FilePath;
|
|
if (GetWeatherIniFilePath(FileName, FilePath) && CheckWeatherValidity(Weather)) {
|
|
IniFile ConfigFile(FilePath);
|
|
Weather.WriteToConfigFile(ConfigFile);
|
|
return ConfigFile.Write(FilePath);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|