diff --git a/CHANGELOG.md b/CHANGELOG.md index 24bcd3ab7..7b26a88bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## CARLA 0.2.4 + + * Fixed serialization of road map resulting in a huge map size. + ## CARLA 0.2.3 * Fixed rounding errors in HUD (100% was shown as 99%, 30 FPS as 29 FPS). diff --git a/Carla.uplugin b/Carla.uplugin index 33815a5d4..e51103ee1 100644 --- a/Carla.uplugin +++ b/Carla.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "0.2.3", + "VersionName": "0.2.4", "FriendlyName": "CARLA", "Description": "", "Category": "Science", diff --git a/Source/Carla/CityMapGenerator.cpp b/Source/Carla/CityMapGenerator.cpp index 9c6f5278a..743fa3378 100644 --- a/Source/Carla/CityMapGenerator.cpp +++ b/Source/Carla/CityMapGenerator.cpp @@ -162,6 +162,12 @@ void ACityMapGenerator::GenerateRoads() AddInstance(tag ##_Lane1, x, y, angle); \ AddInstance(tag ##_Lane2, x, y, angle); \ AddInstance(tag ##_Lane3, x, y, angle); \ + AddInstance(tag ##_Lane4, x, y, angle); \ + AddInstance(tag ##_Lane5, x, y, angle); \ + AddInstance(tag ##_Lane6, x, y, angle); \ + AddInstance(tag ##_Lane7, x, y, angle); \ + AddInstance(tag ##_Lane8, x, y, angle); \ + AddInstance(tag ##_Lane9, x, y, angle); \ AddInstance(tag ##_Sidewalk0, x, y, angle); \ AddInstance(tag ##_Sidewalk1, x, y, angle); \ AddInstance(tag ##_Sidewalk2, x, y, angle); \ @@ -189,7 +195,7 @@ void ACityMapGenerator::GenerateRoads() #undef ADD_INTERSECTION } -// Find first component of type road (checking at its stencil value). +// Find first component of type road. static bool LineTrace( UWorld *World, const FVector &Start, @@ -207,7 +213,7 @@ static bool LineTrace( if (Success) { for (FHitResult &Item : OutHits) { - if (Item.Component->CustomDepthStencilValue == static_cast(CityObjectLabel::Roads)) { + if (ATagger::MatchComponent(*Item.Component, ECityObjectLabel::Roads)) { HitResult = Item; return true; } @@ -224,7 +230,7 @@ void ACityMapGenerator::GenerateRoadMap() check(GetWorld() != nullptr); check(RoadMap != nullptr); - ATagger::TagActorsInLevel(*GetWorld()); // We need the tags. + ATagger::TagActorsInLevel(*GetWorld(), bTagForSemanticSegmentation); // We need the tags. const float IntersectionSize = CityMapMeshTag::GetRoadIntersectionSize(); const uint32 Margin = IntersectionSize / 2u; @@ -237,7 +243,9 @@ void ACityMapGenerator::GenerateRoadMap() const FTransform &ActorTransform = GetActorTransform(); - RoadMap->RoadMap.Empty(); + const FVector MapOffset(-Offset, -Offset, 0.0f); + RoadMap->Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset); + for (uint32 PixelY = 0u; PixelY < SizeY; ++PixelY) { for (uint32 PixelX = 0u; PixelX < SizeX; ++PixelX) { const float X = static_cast(PixelX) * CmPerPixel - Offset; @@ -245,8 +253,6 @@ void ACityMapGenerator::GenerateRoadMap() const FVector Start = ActorTransform.TransformPosition(FVector(X, Y, 50.0f)); const FVector End = ActorTransform.TransformPosition(FVector(X, Y, -50.0f)); - bool Success = false; - // Do the ray tracing. FHitResult Hit; if (LineTrace(World, Start, End, Hit)) { @@ -258,39 +264,21 @@ void ACityMapGenerator::GenerateRoadMap() if (!InstancedStaticMeshComponent->GetInstanceTransform(Hit.Item, InstanceTransform, true)) { UE_LOG(LogCarla, Error, TEXT("Failed to get instance's transform")); } else { - RoadMap->AppendPixel( + RoadMap->SetPixelAt( + PixelX, + PixelY, GetTag(*InstancedStaticMeshComponent->GetStaticMesh()), InstanceTransform, bLeftHandTraffic); - Success = true; } } } - - if (!Success) { - RoadMap->AppendEmptyPixel(); - } } } - const FVector MapOffset(-Offset, -Offset, 0.0f); - RoadMap->Set(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset); - const float MapSizeInMB = // Only map data, not the class itself. - static_cast(sizeof(FRoadMapPixelData) * RoadMap->RoadMap.Num()) / - (1024.0f * 1024.0f); - UE_LOG( - LogCarla, - Log, - TEXT("Generated road map %dx%d (%.2fMB) with %.2f cm/pixel"), - RoadMap->GetWidth(), - RoadMap->GetHeight(), - MapSizeInMB, - CmPerPixel); - - if (!RoadMap->IsValid()) { - UE_LOG(LogCarla, Error, TEXT("Error generating road map")); - return; - } +#ifdef WITH_EDITOR + RoadMap->Log(); +#endif // WITH_EDITOR if (bSaveRoadMapToDisk) { const FString MapName = World->GetMapName() + TEXT(".png"); @@ -298,5 +286,7 @@ void ACityMapGenerator::GenerateRoadMap() RoadMap->SaveAsPNG(FilePath); } +#ifdef WITH_EDITOR RoadMap->DrawDebugPixelsToLevel(GetWorld(), !bDrawDebugPixelsToLevel); +#endif // WITH_EDITOR } diff --git a/Source/Carla/CityMapGenerator.h b/Source/Carla/CityMapGenerator.h index ca463d1ad..55fac69ff 100644 --- a/Source/Carla/CityMapGenerator.h +++ b/Source/Carla/CityMapGenerator.h @@ -152,6 +152,14 @@ private: UPROPERTY(Category = "Road Map", EditAnywhere, AdvancedDisplay) bool bGenerateRoadMapOnSave = true; + /** If true, activate the custom depth pass of each tagged actor in the level. + * This pass is necessary for rendering the semantic segmentation. However, + * it may add a performance penalty since occlusion doesn't seem to be + * applied to objects having this value active. + */ + UPROPERTY(Category = "Road Map", EditAnywhere, AdvancedDisplay) + bool bTagForSemanticSegmentation = false; + UPROPERTY() URoadMap *RoadMap; diff --git a/Source/Carla/DynamicWeather.cpp b/Source/Carla/DynamicWeather.cpp new file mode 100644 index 000000000..5ed440a1b --- /dev/null +++ b/Source/Carla/DynamicWeather.cpp @@ -0,0 +1,83 @@ +// CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +#include "Carla.h" +#include "DynamicWeather.h" + +#include "Components/ArrowComponent.h" + +ADynamicWeather::ADynamicWeather(const FObjectInitializer& ObjectInitializer) +{ + PrimaryActorTick.bCanEverTick = false; + + RootComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("SceneComponent0")); + +#if WITH_EDITORONLY_DATA + ArrowComponent = CreateEditorOnlyDefaultSubobject(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); + Update(); +} + +#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 { + AdjustSunPositionBasedOnActorRotation(); + } +} + +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::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")); + } + + RefreshWeather(); +} + +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); +} diff --git a/Source/Carla/DynamicWeather.h b/Source/Carla/DynamicWeather.h new file mode 100644 index 000000000..2d1acf301 --- /dev/null +++ b/Source/Carla/DynamicWeather.h @@ -0,0 +1,60 @@ +// CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +#pragma once + +#include "GameFramework/Actor.h" +#include "WeatherDescription.h" +#include "DynamicWeather.generated.h" + +class UArrowComponent; + +UCLASS(Abstract) +class CARLA_API ADynamicWeather : public AActor +{ + GENERATED_BODY() + +public: + + ADynamicWeather(const FObjectInitializer& ObjectInitializer); + + virtual void OnConstruction(const FTransform &Transform) override; + +#if WITH_EDITOR + + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + + virtual void EditorApplyRotation(const FRotator & DeltaRotation, bool bAltDown, bool bShiftDown, bool bCtrlDown) override; + +#endif // WITH_EDITOR + + void SetWeatherDescription(const FWeatherDescription &WeatherDescription) + { + Weather = WeatherDescription; + } + + UFUNCTION(BlueprintCallable) + const FWeatherDescription &GetWeatherDescription() const + { + return Weather; + } + + UFUNCTION(BlueprintImplementableEvent) + void RefreshWeather(); + + UFUNCTION(BlueprintCallable) + FVector GetSunDirection() const; + +private: + + void Update(); + + void AdjustSunPositionBasedOnActorRotation(); + +#if WITH_EDITORONLY_DATA + UPROPERTY() + UArrowComponent* ArrowComponent; +#endif // WITH_EDITORONLY_DATA + + UPROPERTY(Category = "Weather", EditAnywhere) + FWeatherDescription Weather; +}; diff --git a/Source/Carla/Game/CarlaGameMode.cpp b/Source/Carla/Game/CarlaGameMode.cpp index 4fb66d4c3..cee954c05 100644 --- a/Source/Carla/Game/CarlaGameMode.cpp +++ b/Source/Carla/Game/CarlaGameMode.cpp @@ -70,7 +70,9 @@ void ACarlaGameMode::RestartPlayer(AController* NewPlayer) void ACarlaGameMode::BeginPlay() { Super::BeginPlay(); - TagActorsForSemanticSegmentation(); + if (GameInstance->GetCarlaSettings().bSemanticSegmentationEnabled) { + TagActorsForSemanticSegmentation(); + } GameController->BeginPlay(); } @@ -105,7 +107,7 @@ void ACarlaGameMode::AttachCaptureCamerasToPlayer(AController &Player) void ACarlaGameMode::TagActorsForSemanticSegmentation() { check(GetWorld() != nullptr); - ATagger::TagActorsInLevel(*GetWorld()); + ATagger::TagActorsInLevel(*GetWorld(), true); } APlayerStart *ACarlaGameMode::FindUnOccupiedStartPoints( diff --git a/Source/Carla/Game/CarlaPlayerState.cpp b/Source/Carla/Game/CarlaPlayerState.cpp index 23c46a3e1..c282cb9dd 100644 --- a/Source/Carla/Game/CarlaPlayerState.cpp +++ b/Source/Carla/Game/CarlaPlayerState.cpp @@ -39,9 +39,23 @@ void ACarlaPlayerState::CopyProperties(APlayerState *PlayerState) } } -void ACarlaPlayerState::RegisterCollision(AActor * /*Actor*/, FVector NormalImpulse) +void ACarlaPlayerState::RegisterCollision( + AActor * /*Actor*/, + AActor * /*OtherActor*/, + const FVector &NormalImpulse, + const FHitResult &Hit) { - CollisionIntensityOther += NormalImpulse.Size(); + switch (ATagger::GetTagOfTaggedComponent(*Hit.Component)) { + case ECityObjectLabel::Vehicles: + CollisionIntensityCars += NormalImpulse.Size(); + break; + case ECityObjectLabel::Pedestrians: + CollisionIntensityPedestrians += NormalImpulse.Size(); + break; + default: + CollisionIntensityOther += NormalImpulse.Size(); + break; + } } static int32 RoundToMilliseconds(float Seconds) diff --git a/Source/Carla/Game/CarlaPlayerState.h b/Source/Carla/Game/CarlaPlayerState.h index 6f7a82c09..165860cca 100644 --- a/Source/Carla/Game/CarlaPlayerState.h +++ b/Source/Carla/Game/CarlaPlayerState.h @@ -130,7 +130,11 @@ public: // =========================================================================== private: - void RegisterCollision(AActor *Actor, FVector NormalImpulse); + void RegisterCollision( + AActor *Actor, + AActor *OtherActor, + const FVector &NormalImpulse, + const FHitResult &Hit); void UpdateTimeStamp(float DeltaSeconds); diff --git a/Source/Carla/Game/CarlaSettings.cpp b/Source/Carla/Game/CarlaSettings.cpp index a757d43a2..6329960ec 100644 --- a/Source/Carla/Game/CarlaSettings.cpp +++ b/Source/Carla/Game/CarlaSettings.cpp @@ -112,6 +112,11 @@ static void ValidateCameraDescription(FCameraDescription &Camera) Camera.ImageSizeY = (Camera.ImageSizeY == 0u ? 512u : Camera.ImageSizeY); } +static bool RequestedSemanticSegmentation(const FCameraDescription &Camera) +{ + return (Camera.PostProcessEffect == EPostProcessEffect::SemanticSegmentation); +} + static void LoadSettingsFromFile(const FString &FileName, UCarlaSettings &Settings) { UE_LOG(LogCarla, Log, TEXT("Loading settings from \"%s\""), *FileName); @@ -141,6 +146,7 @@ static void LoadSettingsFromFile(const FString &FileName, UCarlaSettings &Settin } ValidateCameraDescription(Camera); + Settings.bSemanticSegmentationEnabled |= RequestedSemanticSegmentation(Camera); } } @@ -148,6 +154,9 @@ static bool GetSettingsFileName(FString &Value) { // Try to get it from the command-line arguments. if (FParse::Value(FCommandLine::Get(), TEXT("-carla-settings="), Value)) { + if (FPaths::IsRelative(Value)) { + Value = FPaths::ConvertRelativePathToFull(FPaths::LaunchDir(), Value); + } if (FPaths::FileExists(Value)) { return true; } @@ -183,7 +192,7 @@ void UCarlaSettings::LoadSettings() } } -void UCarlaSettings::LogSettings() +void UCarlaSettings::LogSettings() const { UE_LOG(LogCarla, Log, TEXT("== CARLA Settings ==============================================================")); UE_LOG(LogCarla, Log, TEXT("Settings file: %s"), *CurrentFileName); @@ -194,6 +203,7 @@ void UCarlaSettings::LogSettings() UE_LOG(LogCarla, Log, TEXT("Read Port = %d"), ReadPort); UE_LOG(LogCarla, Log, TEXT("[%s]"), S_CARLA_SCENECAPTURE); UE_LOG(LogCarla, Log, TEXT("Added %d cameras."), CameraDescriptions.Num()); + UE_LOG(LogCarla, Log, TEXT("Semantic Segmentation = %s"), (bSemanticSegmentationEnabled ? TEXT("enabled") : TEXT("disabled"))); for (auto &Item : CameraDescriptions) { UE_LOG(LogCarla, Log, TEXT("[%s/%s]"), S_CARLA_SCENECAPTURE, *Item.Key); UE_LOG(LogCarla, Log, TEXT("Image Size = %dx%d"), Item.Value.ImageSizeX, Item.Value.ImageSizeY); diff --git a/Source/Carla/Game/CarlaSettings.h b/Source/Carla/Game/CarlaSettings.h index 0b56688fc..ffa0cb3d9 100644 --- a/Source/Carla/Game/CarlaSettings.h +++ b/Source/Carla/Game/CarlaSettings.h @@ -18,7 +18,7 @@ public: void LoadSettings(); /** Log settings values. */ - void LogSettings(); + void LogSettings() const; private: @@ -59,5 +59,12 @@ public: UPROPERTY(Category = "Scene Capture", EditDefaultsOnly) TMap CameraDescriptions; + /** Whether semantic segmentation should be activated. The mechanisms for + * semantic segmentation impose some performance penalties even if it is not + * used, we only enable it if necessary. + */ + UPROPERTY(Category = "Scene Capture", EditDefaultsOnly) + bool bSemanticSegmentationEnabled = false; + /// @} }; diff --git a/Source/Carla/Game/CarlaVehicleController.cpp b/Source/Carla/Game/CarlaVehicleController.cpp index bb54832b6..f4d926243 100644 --- a/Source/Carla/Game/CarlaVehicleController.cpp +++ b/Source/Carla/Game/CarlaVehicleController.cpp @@ -251,12 +251,12 @@ void ACarlaVehicleController::ToggleManualMode() // ============================================================================= void ACarlaVehicleController::OnCollisionEvent( - AActor* /*Actor*/, + AActor* Actor, AActor* OtherActor, FVector NormalImpulse, - const FHitResult& /*Hit*/) + const FHitResult& Hit) { - CarlaPlayerState->RegisterCollision(OtherActor, NormalImpulse); + CarlaPlayerState->RegisterCollision(Actor, OtherActor, NormalImpulse, Hit); } // ============================================================================= diff --git a/Source/Carla/MapGen/CityMapMeshTag.h b/Source/Carla/MapGen/CityMapMeshTag.h index 9a10ee429..12d69b8b3 100644 --- a/Source/Carla/MapGen/CityMapMeshTag.h +++ b/Source/Carla/MapGen/CityMapMeshTag.h @@ -20,6 +20,12 @@ enum class ECityMapMeshTag : uint8 Road90DegTurn_Lane1 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 1"), Road90DegTurn_Lane2 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 2"), Road90DegTurn_Lane3 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 3"), + Road90DegTurn_Lane4 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 4"), + Road90DegTurn_Lane5 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 5"), + Road90DegTurn_Lane6 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 6"), + Road90DegTurn_Lane7 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 7"), + Road90DegTurn_Lane8 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 8"), + Road90DegTurn_Lane9 UMETA(DisplayName = "Road: 90 Degree Turn - Lane 9"), Road90DegTurn_Sidewalk0 UMETA(DisplayName = "Road: 90 Degree Turn - Sidewalk 0"), Road90DegTurn_Sidewalk1 UMETA(DisplayName = "Road: 90 Degree Turn - Sidewalk 1"), Road90DegTurn_Sidewalk2 UMETA(DisplayName = "Road: 90 Degree Turn - Sidewalk 2"), @@ -30,6 +36,12 @@ enum class ECityMapMeshTag : uint8 RoadTIntersection_Lane1 UMETA(DisplayName = "Road: T-Intersection - Lane 1"), RoadTIntersection_Lane2 UMETA(DisplayName = "Road: T-Intersection - Lane 2"), RoadTIntersection_Lane3 UMETA(DisplayName = "Road: T-Intersection - Lane 3"), + RoadTIntersection_Lane4 UMETA(DisplayName = "Road: T-Intersection - Lane 4"), + RoadTIntersection_Lane5 UMETA(DisplayName = "Road: T-Intersection - Lane 5"), + RoadTIntersection_Lane6 UMETA(DisplayName = "Road: T-Intersection - Lane 6"), + RoadTIntersection_Lane7 UMETA(DisplayName = "Road: T-Intersection - Lane 7"), + RoadTIntersection_Lane8 UMETA(DisplayName = "Road: T-Intersection - Lane 8"), + RoadTIntersection_Lane9 UMETA(DisplayName = "Road: T-Intersection - Lane 9"), RoadTIntersection_Sidewalk0 UMETA(DisplayName = "Road: T-Intersection - Sidewalk 0"), RoadTIntersection_Sidewalk1 UMETA(DisplayName = "Road: T-Intersection - Sidewalk 1"), RoadTIntersection_Sidewalk2 UMETA(DisplayName = "Road: T-Intersection - Sidewalk 2"), @@ -40,6 +52,12 @@ enum class ECityMapMeshTag : uint8 RoadXIntersection_Lane1 UMETA(DisplayName = "Road: X-Intersection - Lane 1"), RoadXIntersection_Lane2 UMETA(DisplayName = "Road: X-Intersection - Lane 2"), RoadXIntersection_Lane3 UMETA(DisplayName = "Road: X-Intersection - Lane 3"), + RoadXIntersection_Lane4 UMETA(DisplayName = "Road: X-Intersection - Lane 4"), + RoadXIntersection_Lane5 UMETA(DisplayName = "Road: X-Intersection - Lane 5"), + RoadXIntersection_Lane6 UMETA(DisplayName = "Road: X-Intersection - Lane 6"), + RoadXIntersection_Lane7 UMETA(DisplayName = "Road: X-Intersection - Lane 7"), + RoadXIntersection_Lane8 UMETA(DisplayName = "Road: X-Intersection - Lane 8"), + RoadXIntersection_Lane9 UMETA(DisplayName = "Road: X-Intersection - Lane 9"), RoadXIntersection_Sidewalk0 UMETA(DisplayName = "Road: X-Intersection - Sidewalk 0"), RoadXIntersection_Sidewalk1 UMETA(DisplayName = "Road: X-Intersection - Sidewalk 1"), RoadXIntersection_Sidewalk2 UMETA(DisplayName = "Road: X-Intersection - Sidewalk 2"), diff --git a/Source/Carla/MapGen/RoadMap.cpp b/Source/Carla/MapGen/RoadMap.cpp index b06a5a540..0b96ee84d 100644 --- a/Source/Carla/MapGen/RoadMap.cpp +++ b/Source/Carla/MapGen/RoadMap.cpp @@ -3,22 +3,190 @@ #include "Carla.h" #include "RoadMap.h" -#include "DrawDebugHelpers.h" #include "HighResScreenshot.h" +#ifdef WITH_EDITOR +#include "DrawDebugHelpers.h" +#endif // WITH_EDITOR + +#include + +/// ============================================================================ +/// -- Static local methods ---------------------------------------------------- +/// ============================================================================ + static uint32 ClampFloatToUInt(const float Value, int32 Min, int32 Max) { return FMath::Clamp(FMath::FloorToInt(Value), Min, Max); } -// Creates a valid empty map (every point is off-road). +// Return the azimuth angle (in spherical coordinates) rotated by PI so it lies +// in the range [0, 2*PI]. +static float GetRotatedAzimuthAngle(const FVector &Direction) +{ + const FVector2D SphericalCoords = Direction.UnitCartesianToSpherical(); + return SphericalCoords.Y + PI; +} + +/// ============================================================================ +/// -- FRoadMapPixelData ------------------------------------------------------- +/// ============================================================================ + +uint16 FRoadMapPixelData::Encode(bool IsRoad, bool HasDirection, const FVector &Direction) +{ + const uint16 AngleAsUInt = MaximumEncodedAngle * GetRotatedAzimuthAngle(Direction) / (2.0f * PI); + check(!(AngleAsUInt & (1 << IsRoadRow))); + check(!(AngleAsUInt & (1 << HasDirectionRow))); + return (IsRoad << IsRoadRow) | (HasDirection << HasDirectionRow) | (AngleAsUInt); +} + +FColor FRoadMapPixelData::EncodeAsColor() const +{ + if (!IsRoad()) { + return FColor(0u, 0u, 0u, 255u); + } else if (!HasDirection()) { + return FColor(255u, 255u, 255u, 255u); + } else { + auto ToColor = [](float X){ + return FMath::FloorToInt(256.0 * (X + PI) / (2.0f * PI)) % 256; + }; + const float Azimuth = GetDirectionAzimuthalAngle(); + return FColor(0u, 255u, ToColor(Azimuth), 255u); + } +} + +/// ============================================================================ +/// -- URoadMap ---------------------------------------------------------------- +/// ============================================================================ + URoadMap::URoadMap(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer), PixelsPerCentimeter(1.0f), Width(1u), Height(1u) { - AppendEmptyPixel(); + RoadMapData.Add(0u); + static_assert( + std::is_same::value, + "Declaration map of FRoadMapPixelData's value does not match current serialization type"); +} + +void URoadMap::Reset( + const uint32 inWidth, + const uint32 inHeight, + const float inPixelsPerCentimeter, + const FTransform &inWorldToMap, + const FVector &inMapOffset) +{ + RoadMapData.Init(0u, inWidth * inHeight); + Width = inWidth; + Height = inHeight; + PixelsPerCentimeter = inPixelsPerCentimeter; + WorldToMap = inWorldToMap; + MapOffset = inMapOffset; +} + +void URoadMap::SetPixelAt( + const uint32 PixelX, + const uint32 PixelY, + const ECityMapMeshTag Tag, + const FTransform &Transform, + const bool bInvertDirection) +{ + bool bIsRoad = false; + bool bHasDirection = false; + FVector Direction(0.0f, 0.0f, 0.0f); + + auto Rotator = Transform.GetRotation().Rotator(); + + switch (Tag) { + default: + // It's not road. + break; + case ECityMapMeshTag::RoadTwoLanes_LaneRight: + case ECityMapMeshTag::Road90DegTurn_Lane1: + case ECityMapMeshTag::RoadTIntersection_Lane1: + case ECityMapMeshTag::RoadTIntersection_Lane9: + case ECityMapMeshTag::RoadXIntersection_Lane1: + case ECityMapMeshTag::RoadXIntersection_Lane9: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 180.0f; + break; + case ECityMapMeshTag::RoadTwoLanes_LaneLeft: + case ECityMapMeshTag::Road90DegTurn_Lane0: + case ECityMapMeshTag::RoadTIntersection_Lane0: + case ECityMapMeshTag::RoadTIntersection_Lane2: + case ECityMapMeshTag::RoadTIntersection_Lane5: + case ECityMapMeshTag::RoadTIntersection_Lane8: + case ECityMapMeshTag::RoadXIntersection_Lane0: + case ECityMapMeshTag::RoadXIntersection_Lane8: + bIsRoad = true; + bHasDirection = true; + break; + case ECityMapMeshTag::Road90DegTurn_Lane9: + case ECityMapMeshTag::RoadTIntersection_Lane7: + case ECityMapMeshTag::RoadXIntersection_Lane7: + case ECityMapMeshTag::RoadXIntersection_Lane5: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 90.0f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane7: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 90.0f + 22.5f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane5: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 90.0f + 45.0f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane3: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 90.0f + 45.0f + 22.5f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane8: + case ECityMapMeshTag::RoadTIntersection_Lane4: + case ECityMapMeshTag::RoadXIntersection_Lane2: + case ECityMapMeshTag::RoadXIntersection_Lane4: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 270.0f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane6: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 270.0f + 22.5f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane4: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 270.0f + 45.0f; + break; + case ECityMapMeshTag::Road90DegTurn_Lane2: + bIsRoad = true; + bHasDirection = true; + Rotator.Yaw += 270.0f + 45.0f + 22.5f; + break; + case ECityMapMeshTag::RoadTIntersection_Lane3: + case ECityMapMeshTag::RoadTIntersection_Lane6: + case ECityMapMeshTag::RoadXIntersection_Lane3: + case ECityMapMeshTag::RoadXIntersection_Lane6: + bIsRoad = true; + bHasDirection = false; + break; + } + if (bHasDirection) { + FQuat Rotation(Rotator); + Direction = Rotation.GetForwardVector(); + if (bInvertDirection) { + Direction *= -1.0f; + } + } + const auto Value = FRoadMapPixelData::Encode(bIsRoad, bHasDirection, Direction); + RoadMapData[GetIndex(PixelX, PixelY)] = Value; } FVector URoadMap::GetWorldLocation(uint32 PixelX, uint32 PixelY) const @@ -30,7 +198,7 @@ FVector URoadMap::GetWorldLocation(uint32 PixelX, uint32 PixelY) const return WorldToMap.InverseTransformPosition(RelativePosition + MapOffset); } -const FRoadMapPixelData &URoadMap::GetDataAt(const FVector &WorldLocation) const +FRoadMapPixelData URoadMap::GetDataAt(const FVector &WorldLocation) const { check(IsValid()); const FVector Location = WorldToMap.TransformPosition(WorldLocation) - MapOffset; @@ -44,7 +212,8 @@ FRoadMapIntersectionResult URoadMap::Intersect( const FVector &BoxExtent, float ChecksPerCentimeter) const { - const auto &DirectionOfMovement = BoxTransform.GetRotation().GetForwardVector(); + auto DirectionOfMovement = BoxTransform.GetRotation().GetForwardVector(); + DirectionOfMovement.Z = 0.0f; // Project to XY plane (won't be normalized anymore). uint32 CheckCount = 0u; FRoadMapIntersectionResult Result = {0.0f, 0.0f}; const float Step = 1.0f / ChecksPerCentimeter; @@ -52,11 +221,11 @@ FRoadMapIntersectionResult URoadMap::Intersect( for (float Y = -BoxExtent.Y; Y < BoxExtent.Y; Y += Step) { ++CheckCount; auto Location = BoxTransform.TransformPosition(FVector(X, Y, 0.0f)); - auto &Data = GetDataAt(Location); - if (Data.bIsOffRoad) { + const auto &Data = GetDataAt(Location); + if (!Data.IsRoad()) { Result.OffRoad += 1.0f; - } else if (Data.bHasDirection && - 0.0f < FVector::DotProduct(Data.Direction, DirectionOfMovement)) { + } else if (Data.HasDirection() && + 0.0f > FVector::DotProduct(Data.GetDirection(), DirectionOfMovement)) { Result.OppositeLane += 1.0f; } } @@ -70,21 +239,6 @@ FRoadMapIntersectionResult URoadMap::Intersect( return Result; } -static FColor Encode(const FRoadMapPixelData &Data) -{ - if (Data.bIsOffRoad) { - return FColor(0u, 0u, 0u, 255u); - } else if (!Data.bHasDirection) { - return FColor(255u, 255u, 255u, 255u); - } else { - // Assumes normalized direction. - auto ToColor = [](float X){ - return FMath::FloorToInt(255.0 * (X + 1.0f) / 2.0f); - }; - return FColor(ToColor(Data.Direction.X), ToColor(Data.Direction.Y), ToColor(Data.Direction.Z)); - } -} - bool URoadMap::SaveAsPNG(const FString &Path) const { if (!IsValid()) { @@ -93,80 +247,61 @@ bool URoadMap::SaveAsPNG(const FString &Path) const } TArray BitMap; - for (auto &Data : RoadMap) { - BitMap.Emplace(Encode(Data)); + for (auto Value : RoadMapData) { + BitMap.Emplace(FRoadMapPixelData(Value).EncodeAsColor()); } FIntPoint DestSize(Width, Height); FString ResultPath; - FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig(); + FHighResScreenshotConfig &HighResScreenshotConfig = GetHighResScreenshotConfig(); + HighResScreenshotConfig.SetHDRCapture(false); HighResScreenshotConfig.SaveImage(Path, BitMap, DestSize, &ResultPath); + UE_LOG(LogCarla, Log, TEXT("Saved road map to \"%s\""), *ResultPath); return true; } +#ifdef WITH_EDITOR + +void URoadMap::Log() const +{ + const float MapSizeInMB = // Only map data, not the class itself. + static_cast(sizeof(decltype(RoadMapData)::ElementType) * RoadMapData.Num()) / + (1024.0f * 1024.0f); + UE_LOG( + LogCarla, + Log, + TEXT("Generated road map %dx%d (%.2fMB) with %.2f cm/pixel"), + GetWidth(), + GetHeight(), + MapSizeInMB, + 1.0f / PixelsPerCentimeter); + + if (!IsValid()) { + UE_LOG(LogCarla, Error, TEXT("Error generating road map")); + return; + } +} + void URoadMap::DrawDebugPixelsToLevel(UWorld *World, const bool bJustFlushDoNotDraw) const { + const FVector ZOffset(0.0f, 0.0f, 50.0f); FlushPersistentDebugLines(World); if (!bJustFlushDoNotDraw) { for (auto X = 0u; X < Width; ++X) { for (auto Y = 0u; Y < Height; ++Y) { - auto Location = GetWorldLocation(X, Y); - auto Color = Encode(GetDataAt(X, Y)); - DrawDebugPoint(World, Location, 20.0f, Color, true); + auto Location = GetWorldLocation(X, Y) + ZOffset; + const auto &Data = GetDataAt(X, Y); + auto Color = Data.EncodeAsColor(); + if (Data.HasDirection()) { + const FVector ArrowEnd = Location + 50.0f * Data.GetDirection(); + DrawDebugDirectionalArrow(World, Location, ArrowEnd, 60.0f, Color, true); + } else { + DrawDebugPoint(World, Location, 6.0f, Color, true); + } } } } } -void URoadMap::AppendPixel( - ECityMapMeshTag Tag, - const FTransform &Transform, - const bool bInvertDirection) -{ - AppendEmptyPixel(); - auto &Data = RoadMap.Last(); - Data.bIsOffRoad = false; - - auto Rotator = Transform.GetRotation().Rotator(); - switch (Tag) { - case ECityMapMeshTag::RoadTwoLanes_LaneRight: - case ECityMapMeshTag::Road90DegTurn_Lane0: - Data.bHasDirection = true; - break; - case ECityMapMeshTag::RoadTwoLanes_LaneLeft: - case ECityMapMeshTag::Road90DegTurn_Lane1: - Rotator.Yaw += 180.0f; - Data.bHasDirection = true; - break; - case ECityMapMeshTag::Road90DegTurn_Lane2: - Rotator.Yaw += 90.0f; - Data.bHasDirection = true; - break; - case ECityMapMeshTag::Road90DegTurn_Lane3: - Rotator.Yaw += 270.0f; - Data.bHasDirection = true; - break; - } - if (Data.bHasDirection) { - FQuat Rotation(Rotator); - Data.Direction = Rotation.GetForwardVector(); - if (bInvertDirection) { - Data.Direction *= -1.0f; - } - } -} - -void URoadMap::Set( - const uint32 inWidth, - const uint32 inHeight, - const float inPinxelsPerCentimeter, - const FTransform &inWorldToMap, - const FVector &inMapOffset) -{ - Width = inWidth; - Height = inHeight; - PixelsPerCentimeter = inPinxelsPerCentimeter; - WorldToMap = inWorldToMap; - MapOffset = inMapOffset; -} +#endif // WITH_EDITOR diff --git a/Source/Carla/MapGen/RoadMap.h b/Source/Carla/MapGen/RoadMap.h index 0e0fd59a8..3ba073526 100644 --- a/Source/Carla/MapGen/RoadMap.h +++ b/Source/Carla/MapGen/RoadMap.h @@ -5,33 +5,78 @@ #include "UObject/NoExportTypes.h" #include "RoadMap.generated.h" +/// Road map intersection result. See URoadMap. USTRUCT() struct FRoadMapIntersectionResult { GENERATED_BODY() - /** Percentage of the box lying off-road */ + /// Percentage of the box lying off-road. UPROPERTY(VisibleAnywhere, BlueprintReadOnly) float OffRoad; - /** Percentage of the box invading opposite lane (wrong direction) */ + /// Percentage of the box invading opposite lane (wrong direction). UPROPERTY(VisibleAnywhere, BlueprintReadOnly) float OppositeLane; }; -USTRUCT() +/// Data stored in a road map pixel. See URoadMap. struct FRoadMapPixelData { - GENERATED_BODY() + friend class URoadMap; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly) - bool bIsOffRoad = true; + constexpr static int IsRoadRow = 15; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly) - bool bHasDirection = false; + constexpr static int HasDirectionRow = 14; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly) - FVector Direction; + constexpr static uint16 MaximumEncodedAngle = (1 << 14) - 1; + + constexpr static uint16 AngleMask = (0xFFFF >> 2); + +public: + + explicit FRoadMapPixelData(uint16 inValue) : Value(inValue) {} + + /// Whether this pixel lies in-road. + bool IsRoad() const + { + return (Value & (1 << IsRoadRow)) != 0; + } + + /// Whether this pixel has a direction defined (e.g. road intersections are + /// not off-road but neither have defined direction). + bool HasDirection() const + { + return (Value & (1 << HasDirectionRow)) != 0; + } + + /// Get the azimuth angle [-PI, PI] of the road direction (in spherical + /// coordinates) at this pixel. + /// + /// Undefined if !HasDirection(). + float GetDirectionAzimuthalAngle() const + { + const float Angle = AngleMask & Value; + // Internally the angle is rotated by PI. + return (Angle * 2.0f * PI / MaximumEncodedAngle) - PI; + } + + /// Get the road direction at this pixel. + /// + /// Undefined if !HasDirection(). + FVector GetDirection() const + { + const FVector2D SphericalCoords(HALF_PI, GetDirectionAzimuthalAngle()); + return SphericalCoords.SphericalToUnitCartesian(); + } + + FColor EncodeAsColor() const; + +private: + + static uint16 Encode(bool IsRoad, bool HasDirection, const FVector &Direction); + + uint16 Value; }; /// Road map of the level. Contains information in 2D of which areas are road @@ -43,13 +88,23 @@ class CARLA_API URoadMap : public UObject public: + /// Creates a valid empty map (every point is off-road). URoadMap(const FObjectInitializer& ObjectInitializer); - UFUNCTION(BlueprintCallable) - bool IsValid() const - { - return ((RoadMap.Num() > 0) && (RoadMap.Num() == Height * Width)); - } + /// Resets current map an initializes an empty map of the given size. + void Reset( + uint32 Width, + uint32 Height, + float PixelsPerCentimeter, + const FTransform &WorldToMap, + const FVector &MapOffset); + + void SetPixelAt( + uint32 PixelX, + uint32 PixelY, + ECityMapMeshTag Tag, + const FTransform &Transform, + bool bInvertDirection = false); uint32 GetWidth() const { @@ -61,74 +116,73 @@ public: return Height; } + /// Return the world location of a given pixel. FVector GetWorldLocation(uint32 PixelX, uint32 PixelY) const; - const FRoadMapPixelData &GetDataAt(uint32 PixelX, uint32 PixelY) const + /// Retrieve the data stored at a given pixel. + FRoadMapPixelData GetDataAt(uint32 PixelX, uint32 PixelY) const { check(IsValid()); - return RoadMap[PixelX + Width * PixelY]; + return FRoadMapPixelData(RoadMapData[GetIndex(PixelX, PixelY)]); } - /** Clamps value if lies outside map limits */ - UFUNCTION(BlueprintCallable) - const FRoadMapPixelData &GetDataAt(const FVector &WorldLocation) const; + /// Clamps value if lies outside map limits. + FRoadMapPixelData GetDataAt(const FVector &WorldLocation) const; - /** Intersect actor bounds with map. - * - * Bounds box is projected to the map and checked against it for possible - * intersections with off-road areas and opposite lanes. - */ - UFUNCTION(BlueprintCallable) + /// Intersect actor bounds with map. + /// + /// Bounds box is projected to the map and checked against it for possible + /// intersections with off-road areas and opposite lanes. FRoadMapIntersectionResult Intersect( const FTransform &BoxTransform, const FVector &BoxExtent, float ChecksPerCentimeter) const; - UFUNCTION(BlueprintCallable) + /// Save the current map as PNG with the pixel data encoded as color. bool SaveAsPNG(const FString &Path) const; - /** Draw every pixel of the image as debug point */ - UFUNCTION(BlueprintCallable) +#ifdef WITH_EDITOR + + /// Log status of the map to the console. + void Log() const; + + /// Draw every pixel of the image as debug point. void DrawDebugPixelsToLevel(UWorld *World, bool bJustFlushDoNotDraw = false) const; +#endif // WITH_EDITOR + private: - friend class ACityMapGenerator; - - void AppendEmptyPixel() + int32 GetIndex(uint32 PixelX, uint32 PixelY) const { - RoadMap.AddDefaulted(1); + return PixelX + Width * PixelY; } - void AppendPixel( - ECityMapMeshTag Tag, - const FTransform &Transform, - bool bInvertDirection); - - void Set( - uint32 Width, - uint32 Height, - float PixelsPerCentimeter, - const FTransform &WorldToMap, - const FVector &MapOffset); + bool IsValid() const + { + return ((RoadMapData.Num() > 0) && (RoadMapData.Num() == Height * Width)); + } + /// World-to-map transform. UPROPERTY(VisibleAnywhere) FTransform WorldToMap; + /// Offset of the map in map coordinates. UPROPERTY(VisibleAnywhere) FVector MapOffset; + /// Number of pixels per centimeter. UPROPERTY(VisibleAnywhere) float PixelsPerCentimeter; - /** Width of the map in pixels */ + /// Width of the map in pixels. UPROPERTY(VisibleAnywhere) uint32 Width; - /** Height of the map in pixels */ + /// Height of the map in pixels. UPROPERTY(VisibleAnywhere) uint32 Height; UPROPERTY() - TArray RoadMap; + TArray RoadMapData; }; diff --git a/Source/Carla/Tagger.cpp b/Source/Carla/Tagger.cpp index 8d95e88c6..6dba77700 100644 --- a/Source/Carla/Tagger.cpp +++ b/Source/Carla/Tagger.cpp @@ -10,10 +10,10 @@ #include "PhysicsEngine/PhysicsAsset.h" #ifdef CARLA_TAGGER_EXTRA_LOG -static FString GetLabelAsString(const CityObjectLabel Label) +static FString GetLabelAsString(const ECityObjectLabel Label) { switch (Label) { -#define CARLA_GET_LABEL_STR(lbl) case CityObjectLabel:: lbl : return #lbl; +#define CARLA_GET_LABEL_STR(lbl) case ECityObjectLabel:: lbl : return #lbl; default: CARLA_GET_LABEL_STR(None) CARLA_GET_LABEL_STR(Buildings) @@ -38,42 +38,45 @@ static auto CastEnum(T label) return static_cast::type>(label); } -static CityObjectLabel GetLabelByFolderName(const FString &String) { - if (String == "Buildings") return CityObjectLabel::Buildings; - else if (String == "Fences") return CityObjectLabel::Fences; - else if (String == "Pedestrians") return CityObjectLabel::Pedestrians; - else if (String == "Pole") return CityObjectLabel::Poles; - else if (String == "Props") return CityObjectLabel::Other; - else if (String == "Road") return CityObjectLabel::Roads; - else if (String == "RoadLines") return CityObjectLabel::RoadLines; - else if (String == "SideWalk") return CityObjectLabel::Sidewalks; - else if (String == "Vegetation") return CityObjectLabel::Vegetation; - else if (String == "Vehicles") return CityObjectLabel::Vehicles; - else if (String == "Walls") return CityObjectLabel::Walls; - else return CityObjectLabel::None; +static ECityObjectLabel GetLabelByFolderName(const FString &String) { + if (String == "Buildings") return ECityObjectLabel::Buildings; + else if (String == "Fences") return ECityObjectLabel::Fences; + else if (String == "Pedestrians") return ECityObjectLabel::Pedestrians; + else if (String == "Pole") return ECityObjectLabel::Poles; + else if (String == "Props") return ECityObjectLabel::Other; + else if (String == "Road") return ECityObjectLabel::Roads; + else if (String == "RoadLines") return ECityObjectLabel::RoadLines; + else if (String == "SideWalk") return ECityObjectLabel::Sidewalks; + else if (String == "Vegetation") return ECityObjectLabel::Vegetation; + else if (String == "Vehicles") return ECityObjectLabel::Vehicles; + else if (String == "Walls") return ECityObjectLabel::Walls; + else return ECityObjectLabel::None; } template -static CityObjectLabel GetLabelByPath(const T *Object) +static ECityObjectLabel GetLabelByPath(const T *Object) { const FString Path = Object->GetPathName(); TArray StringArray; Path.ParseIntoArray(StringArray, TEXT("/"), false); - return (StringArray.Num() > 3 ? GetLabelByFolderName(StringArray[3]) : CityObjectLabel::None); + return (StringArray.Num() > 3 ? GetLabelByFolderName(StringArray[3]) : ECityObjectLabel::None); } -static void SetStencilValue(UPrimitiveComponent *comp, const CityObjectLabel &Label) { - if (Label != CityObjectLabel::None) { - comp->SetRenderCustomDepth(true); - comp->SetCustomDepthStencilValue(CastEnum(Label)); - } +static void SetStencilValue( + UPrimitiveComponent &Component, + const ECityObjectLabel &Label, + const bool bSetRenderCustomDepth) { + Component.SetRenderCustomDepth( + bSetRenderCustomDepth && + !ATagger::MatchComponent(Component, ECityObjectLabel::None)); + Component.SetCustomDepthStencilValue(CastEnum(Label)); } // ============================================================================= // -- static ATagger functions ------------------------------------------------- // ============================================================================= -void ATagger::TagActor(const AActor &Actor) +void ATagger::TagActor(const AActor &Actor, bool bTagForSemanticSegmentation) { #ifdef CARLA_TAGGER_EXTRA_LOG UE_LOG(LogCarla, Log, TEXT("Actor: %s"), *Actor.GetName()); @@ -84,7 +87,7 @@ void ATagger::TagActor(const AActor &Actor) Actor.GetComponents(StaticMeshComponents); for (UStaticMeshComponent *Component : StaticMeshComponents) { const auto Label = GetLabelByPath(Component->GetStaticMesh()); - SetStencilValue(Component, Label); + SetStencilValue(*Component, Label, bTagForSemanticSegmentation); #ifdef CARLA_TAGGER_EXTRA_LOG UE_LOG(LogCarla, Log, TEXT(" + StaticMeshComponent: %s"), *Component->GetName()); UE_LOG(LogCarla, Log, TEXT(" - Label: \"%s\""), *GetLabelAsString(Label)); @@ -96,7 +99,7 @@ void ATagger::TagActor(const AActor &Actor) Actor.GetComponents(SkeletalMeshComponents); for (USkeletalMeshComponent *Component : SkeletalMeshComponents) { const auto Label = GetLabelByPath(Component->GetPhysicsAsset()); - SetStencilValue(Component, Label); + SetStencilValue(*Component, Label, bTagForSemanticSegmentation); #ifdef CARLA_TAGGER_EXTRA_LOG UE_LOG(LogCarla, Log, TEXT(" + SkeletalMeshComponent: %s"), *Component->GetName()); UE_LOG(LogCarla, Log, TEXT(" - Label: \"%s\""), *GetLabelAsString(Label)); @@ -104,10 +107,24 @@ void ATagger::TagActor(const AActor &Actor) } } -void ATagger::TagActorsInLevel(UWorld &World) +void ATagger::TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation) { for (TActorIterator it(&World); it; ++it) { - TagActor(**it); + TagActor(**it, bTagForSemanticSegmentation); + } +} + +void ATagger::GetTagsOfTaggedActor(const AActor &Actor, TArray &Tags) +{ + TArray Components; + Actor.GetComponents(Components); + for (auto *Component : Components) { + if (Component != nullptr) { + const auto Tag = GetTagOfTaggedComponent(*Component); + if (Tag != ECityObjectLabel::None) { + Tags.Add(Tag); + } + } } } @@ -126,7 +143,7 @@ void ATagger::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent Super::PostEditChangeProperty(PropertyChangedEvent); if (PropertyChangedEvent.Property) { if (bTriggerTagObjects && (GetWorld() != nullptr)) { - TagActorsInLevel(*GetWorld()); + TagActorsInLevel(*GetWorld(), bTagForSemanticSegmentation); } } bTriggerTagObjects = false; diff --git a/Source/Carla/Tagger.h b/Source/Carla/Tagger.h index cf5c5d90e..5824ed6f6 100644 --- a/Source/Carla/Tagger.h +++ b/Source/Carla/Tagger.h @@ -3,9 +3,10 @@ #pragma once #include "GameFramework/Actor.h" +#include "Components/PrimitiveComponent.h" #include "Tagger.generated.h" -enum class CityObjectLabel : uint8 +enum class ECityObjectLabel : uint8 { None = 0u, Buildings = 1u, @@ -33,9 +34,37 @@ class CARLA_API ATagger : public AActor public: - static void TagActor(const AActor &Actor); + /// Set the tag of an actor. + /// + /// If bTagForSemanticSegmentation true, activate the custom depth pass. This + /// pass is necessary for rendering the semantic segmentation. However, it may + /// add a performance penalty since occlusion doesn't seem to be applied to + /// objects having this value active. + static void TagActor(const AActor &Actor, bool bTagForSemanticSegmentation); - static void TagActorsInLevel(UWorld &World); + /// Set the tag of every actor in level. + /// + /// If bTagForSemanticSegmentation true, activate the custom depth pass. This + /// pass is necessary for rendering the semantic segmentation. However, it may + /// add a performance penalty since occlusion doesn't seem to be applied to + /// objects having this value active. + static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation); + + /// Retrieve the tag of an already tagged component. + static ECityObjectLabel GetTagOfTaggedComponent(const UPrimitiveComponent &Component) + { + return static_cast(Component.CustomDepthStencilValue); + } + + /// Retrieve the tags of an already tagged actor. ECityObjectLabel::None is + /// not added to the array. + static void GetTagsOfTaggedActor(const AActor &Actor, TArray &Tags); + + /// Return true if @a Component has been tagged with the given @a Tag. + static bool MatchComponent(const UPrimitiveComponent &Component, ECityObjectLabel Tag) + { + return (Tag == GetTagOfTaggedComponent(Component)); + } ATagger(); @@ -49,4 +78,7 @@ private: UPROPERTY(Category = "Tagger", EditAnywhere) bool bTriggerTagObjects = false; + + UPROPERTY(Category = "Tagger", EditAnywhere) + bool bTagForSemanticSegmentation = false; }; diff --git a/Source/Carla/WeatherDescription.h b/Source/Carla/WeatherDescription.h new file mode 100644 index 000000000..5fe1dd3ca --- /dev/null +++ b/Source/Carla/WeatherDescription.h @@ -0,0 +1,118 @@ +// CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +#pragma once + +#include "WeatherDescription.generated.h" + +UENUM(BlueprintType) +enum class EPrecipitationType : uint8 +{ + Rain UMETA(DisplayName = "Rain"), +}; + +USTRUCT(BlueprintType) +struct FWeatherDescription +{ + GENERATED_USTRUCT_BODY() + + // =========================================================================== + /// @name Weather + // =========================================================================== + /// @{ + + /** Display name of the current weather. */ + UPROPERTY(Category = "Weather", EditAnywhere, BlueprintReadWrite) + FString Name; + + /// @} + // =========================================================================== + /// @name Weather - Sun + // =========================================================================== + /// @{ + + /** Polar angle of the Sun in degrees, with 0.0 at zenith, 90.0 at equator. */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "180.0")) + float SunPolarAngle = 45.0f; + + /** Azimuth angle of the Sun in degrees. */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "-180.0", ClampMax = "180.0")) + float SunAzimuthAngle = 0.0f; + + /** */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float SunBrightness; + + /** */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float SunDirectionalLightIntensity; + + /** */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite) + FLinearColor SunDirectionalLightColor; + + /** */ + UPROPERTY(Category = "Weather|Sun", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float SunIndirectLightIntensity; + + /// @} + // =========================================================================== + /// @name Weather - Sky + // =========================================================================== + /// @{ + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float CloudOpacity; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float HorizontFalloff; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite) + FLinearColor ZenithColor; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite) + FLinearColor HorizonColor; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite) + FLinearColor CloudColor; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite) + FLinearColor OverallSkyColor; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite, meta=(ClampMin = "0.0", ClampMax = "100.0")) + float SkyLightIntensity; + + /** */ + UPROPERTY(Category = "Weather|Sky", EditAnywhere, BlueprintReadWrite) + FLinearColor SkyLightColor; + + /// @} + // =========================================================================== + /// @name Weather - Precipitation + // =========================================================================== + /// @{ + + /** */ + UPROPERTY(Category = "Weather|Precipitation", EditAnywhere, BlueprintReadWrite) + bool bPrecipitation = false; + + /** */ + UPROPERTY(Category = "Weather|Precipitation", EditAnywhere, BlueprintReadWrite, meta=(EditCondition="bPrecipitation")) + EPrecipitationType PrecipitationType = EPrecipitationType::Rain; + + /** */ + UPROPERTY(Category = "Weather|Precipitation", EditAnywhere, BlueprintReadWrite, meta=(EditCondition="bPrecipitation", ClampMin = "0.0", ClampMax = "100.0")) + float PrecipitationAmount = 0.0f; + + /** */ + UPROPERTY(Category = "Weather|Precipitation", EditAnywhere, BlueprintReadWrite, meta=(EditCondition="bPrecipitation", ClampMin = "0.0", ClampMax = "100.0")) + float PrecipitationAccumulation = 0.0f; + + /// @} +};