Merge remote-tracking branch 'origin/nestor' into xisco

This commit is contained in:
Xisco Bosch 2017-05-08 12:20:59 +02:00
commit bdfbf53670
26 changed files with 1243 additions and 194 deletions

32
CHANGELOG.md Normal file
View File

@ -0,0 +1,32 @@
## CARLA 0.2.3
* Fixed rounding errors in HUD (100% was shown as 99%, 30 FPS as 29 FPS).
* Fixed crash when player goes out of road map.
* Fixed several issues related to the transform of the road map (wasn't working in CARLA_ORIGIN_1).
## CARLA 0.2.2
* Implemented signals for off-road and opposite lane invasion
* Fixed linking issues (use Unreal's libpng)
* Fixed memory leak in PNG compression
## CARLA 0.2.1
* Fixed the memory leak related to protobuf issues
* Fixed color shift in semantic segmentation and depth
* Added in-game timestamp (now sending both OS and in-game)
## CARLA 0.2.0
* Fixed Depth issues
* Added semantic segmentation
* Changed codification to PNG
* Camera configuration through config INI file
## CARLA 0.1.1
* Added build system for Windows and Linux
## CARLA 0.1.0
* Added basic functionality

View File

@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.2.1",
"VersionName": "0.2.3",
"FriendlyName": "CARLA",
"Description": "",
"Category": "Science",

View File

@ -3,6 +3,8 @@ CARLA UE4 Plugin
Plugin for Unreal Engine 4.
See [CHANGELOG](CHANGELOG.md).
Settings
--------

View File

@ -9,3 +9,14 @@ Other CARLA related command-line options
* `-carla-settings=<ini-file-path>` Load settings from the given INI file. See Example.CarlaSettings.ini.
* `-world-port=<port-number>` Listen for client connections at <port-number>, write and read ports are set to <port-number>+1 and <port-number>+2 respectively.
To activate semantic segmentation
---------------------------------
In the config file `CarlaUE4/Saved/Config/LinuxNoEditor/Engine.ini`, add the
following
```
[/Script/Engine.RendererSettings]
r.CustomDepth=3
```

View File

@ -4,6 +4,12 @@
#include "CityMapGenerator.h"
#include "MapGen/GraphGenerator.h"
#include "MapGen/RoadMap.h"
#include "Tagger.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Engine/World.h"
#include "Paths.h"
#include <algorithm>
@ -16,35 +22,59 @@
// =============================================================================
ACityMapGenerator::ACityMapGenerator(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {}
: Super(ObjectInitializer)
{
RoadMap = ObjectInitializer.CreateDefaultSubobject<URoadMap>(this, TEXT("RoadMap"));
}
ACityMapGenerator::~ACityMapGenerator() {}
// =============================================================================
// -- Map construction and update related methods ------------------------------
// -- Overriden from UObject ---------------------------------------------------
// =============================================================================
void ACityMapGenerator::UpdateMap() {
void ACityMapGenerator::PreSave(const ITargetPlatform *TargetPlatform)
{
if (bGenerateRoadMapOnSave) {
check(RoadMap != nullptr);
GenerateRoadMap();
}
Super::PreSave(TargetPlatform);
}
// =============================================================================
// -- Overriden from ACityMapMeshHolder ----------------------------------------
// =============================================================================
void ACityMapGenerator::UpdateMap()
{
UpdateSeeds();
GenerateGraph();
if (bGenerateRoads) {
GenerateRoads();
}
if (bTriggerRoadMapGeneration) {
bTriggerRoadMapGeneration = false;
GenerateRoadMap();
}
}
void ACityMapGenerator::UpdateSeeds() {
// =============================================================================
// -- Map construction and update related methods ------------------------------
// =============================================================================
void ACityMapGenerator::UpdateSeeds()
{
if (!bUseFixedSeed) {
bUseMultipleFixedSeeds = false;
FRandomStream randomStream;
randomStream.GenerateNewSeed();
Seed = randomStream.GetCurrentSeed();
}
if (!bUseMultipleFixedSeeds) {
RoadPlanningSeed = Seed;
}
}
void ACityMapGenerator::GenerateGraph() {
void ACityMapGenerator::GenerateGraph()
{
if ((MapSizeX < 5u) || (MapSizeY < 5u)) {
MapSizeX = 5u;
MapSizeY = 5u;
@ -54,7 +84,7 @@ void ACityMapGenerator::GenerateGraph() {
// Delete the dcel before the new one is created so indices are restored.
Dcel.Reset(nullptr);
#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
Dcel = MapGen::GraphGenerator::Generate(MapSizeX, MapSizeY, RoadPlanningSeed);
Dcel = MapGen::GraphGenerator::Generate(MapSizeX, MapSizeY, Seed);
UE_LOG(LogCarla, Log,
TEXT("Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
Dcel->CountNodes(),
@ -87,7 +117,8 @@ void ACityMapGenerator::GenerateGraph() {
#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
}
void ACityMapGenerator::GenerateRoads() {
void ACityMapGenerator::GenerateRoads()
{
check(Dcel != nullptr);
using Graph = MapGen::DoublyConnectedEdgeList;
const Graph &graph = *Dcel;
@ -157,3 +188,115 @@ void ACityMapGenerator::GenerateRoads() {
#undef ADD_INTERSECTION
}
// Find first component of type road (checking at its stencil value).
static bool LineTrace(
UWorld *World,
const FVector &Start,
const FVector &End,
FHitResult &HitResult)
{
TArray <FHitResult> OutHits;
static FName TraceTag = FName(TEXT("RoadTrace"));
const bool Success = World->LineTraceMultiByObjectType(
OutHits,
Start,
End,
FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldDynamic),
FCollisionQueryParams(TraceTag, true));
if (Success) {
for (FHitResult &Item : OutHits) {
if (Item.Component->CustomDepthStencilValue == static_cast<uint8>(CityObjectLabel::Roads)) {
HitResult = Item;
return true;
}
}
}
return false;
}
void ACityMapGenerator::GenerateRoadMap()
{
UE_LOG(LogCarla, Log, TEXT("Generating road map..."));
auto World = GetWorld();
check(GetWorld() != nullptr);
check(RoadMap != nullptr);
ATagger::TagActorsInLevel(*GetWorld()); // We need the tags.
const float IntersectionSize = CityMapMeshTag::GetRoadIntersectionSize();
const uint32 Margin = IntersectionSize / 2u;
const float Offset = GetMapScale() * Margin;
const float CmPerPixel = GetMapScale() / static_cast<float>(PixelsPerMapUnit);
const uint32 SizeX = PixelsPerMapUnit * (MapSizeX + 2u * Margin);
const uint32 SizeY = PixelsPerMapUnit * (MapSizeY + 2u * Margin);
const FTransform &ActorTransform = GetActorTransform();
RoadMap->RoadMap.Empty();
for (uint32 PixelY = 0u; PixelY < SizeY; ++PixelY) {
for (uint32 PixelX = 0u; PixelX < SizeX; ++PixelX) {
const float X = static_cast<float>(PixelX) * CmPerPixel - Offset;
const float Y = static_cast<float>(PixelY) * CmPerPixel - Offset;
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)) {
auto InstancedStaticMeshComponent = Cast<UInstancedStaticMeshComponent>(Hit.Component.Get());
if (InstancedStaticMeshComponent == nullptr) {
UE_LOG(LogCarla, Error, TEXT("Road component is not UInstancedStaticMeshComponent"));
} else {
FTransform InstanceTransform;
if (!InstancedStaticMeshComponent->GetInstanceTransform(Hit.Item, InstanceTransform, true)) {
UE_LOG(LogCarla, Error, TEXT("Failed to get instance's transform"));
} else {
RoadMap->AppendPixel(
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<float>(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;
}
if (bSaveRoadMapToDisk) {
const FString MapName = World->GetMapName() + TEXT(".png");
const FString FilePath = FPaths::Combine(FPaths::GameSavedDir(), MapName);
RoadMap->SaveAsPNG(FilePath);
}
RoadMap->DrawDebugPixelsToLevel(GetWorld(), !bDrawDebugPixelsToLevel);
}

View File

@ -7,10 +7,12 @@
#include "MapGen/GraphParser.h"
#include "CityMapGenerator.generated.h"
class URoadMap;
/// Generates a random city using the meshes provided.
///
/// @note At this point it only generates roads and sidewalks.
UCLASS(HideCategories=(Rendering, Input))
UCLASS(HideCategories=(Input,Rendering,Actor))
class CARLA_API ACityMapGenerator : public ACityMapMeshHolder
{
GENERATED_BODY()
@ -27,14 +29,42 @@ public:
/// @}
// ===========================================================================
/// @name Map construction and update related methods
/// @name Overriden from UObject
// ===========================================================================
/// @{
public:
virtual void PreSave(const ITargetPlatform *TargetPlatform) override;
/// @}
// ===========================================================================
/// @name Overriden from ACityMapMeshHolder
// ===========================================================================
/// @{
private:
/// Update the map based on the current settings.
virtual void UpdateMap() override;
/// @}
// ===========================================================================
/// @name Road map
// ===========================================================================
/// @{
public:
UFUNCTION(BlueprintCallable)
URoadMap *GetRoadMap()
{
return RoadMap;
}
/// @}
// ===========================================================================
/// @name Map construction and update related methods
// ===========================================================================
/// @{
private:
/// Update the random seeds. Generate random if no fixed seed is used.
void UpdateSeeds();
@ -44,6 +74,9 @@ private:
/// Add the road meshes to the scene based on the current DCEL.
void GenerateRoads();
/// Generate the road map image and save to disk if requested.
void GenerateRoadMap();
/// @}
// ===========================================================================
/// @name Map generation properties
@ -51,33 +84,76 @@ private:
/// @{
private:
/** Size X of the map in map units. The map unit is calculated based in the
* tile mesh of the road (see Map Scale).
*/
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (ClampMin = "10", ClampMax = "200"))
uint32 MapSizeX = 20u;
/** Size Y of the map in map units. The map unit is calculated based in the
* tile mesh of the road (see Map Scale).
*/
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (ClampMin = "10", ClampMax = "200"))
uint32 MapSizeY = 20u;
/** If false, no mesh is added, only the internal representation of road is
* generated.
*/
UPROPERTY(Category = "Map Generation", EditAnywhere)
bool bGenerateRoads = true;
/** If false, a random seed is generated each time. */
UPROPERTY(Category = "Map Generation", EditAnywhere)
bool bUseFixedSeed = true;
/** Seed of the random map generated. */
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (EditCondition = bUseFixedSeed))
int32 Seed = 123456789;
/// @}
// ===========================================================================
/// @name Map generation properties - advance display
/// @name Road Map
// ===========================================================================
/// @{
private:
UPROPERTY(Category = "Map Generation", EditAnywhere, AdvancedDisplay, meta = (EditCondition = bUseFixedSeed))
bool bUseMultipleFixedSeeds = false;
/** Trigger the generation a the road map image of the current layout (used
* for off-road and opposite lane invasion detection).
*/
UPROPERTY(Category = "Road Map", EditAnywhere)
bool bTriggerRoadMapGeneration = false;
UPROPERTY(Category = "Map Generation", EditAnywhere, AdvancedDisplay, meta = (EditCondition = bUseMultipleFixedSeeds))
int32 RoadPlanningSeed;
/** The resolution in pixels per map unit of the road map. The map unit is
* calculated based in the tile mesh of the road (see Map Scale).
*/
UPROPERTY(Category = "Road Map", EditAnywhere, meta = (ClampMin = "1", ClampMax = "500"))
uint32 PixelsPerMapUnit = 50u;
/** Whether the road map should be generated based on left-hand traffic. */
UPROPERTY(Category = "Road Map", EditAnywhere)
bool bLeftHandTraffic = false;
/** If true, the road map encoded as an image is saved to disk. The image is
* saved to the "Saved" folder of the project.
*/
UPROPERTY(Category = "Road Map", EditAnywhere)
bool bSaveRoadMapToDisk = true;
/** If true, a debug point is drawn in the level for each pixel of the road
* map.
*/
UPROPERTY(Category = "Road Map", EditAnywhere)
bool bDrawDebugPixelsToLevel = false;
/** The road map is re-computed on save so we always store an up-to-date
* version. Uncheck this only for testing purposes as the road map might get
* out-of-sync with the current road layout.
*/
UPROPERTY(Category = "Road Map", EditAnywhere, AdvancedDisplay)
bool bGenerateRoadMapOnSave = true;
UPROPERTY()
URoadMap *RoadMap;
/// @}
// ===========================================================================

View File

@ -70,7 +70,7 @@ void ACarlaGameMode::RestartPlayer(AController* NewPlayer)
void ACarlaGameMode::BeginPlay()
{
Super::BeginPlay();
TagObjectsForSemanticSegmentation();
TagActorsForSemanticSegmentation();
GameController->BeginPlay();
}
@ -102,11 +102,10 @@ void ACarlaGameMode::AttachCaptureCamerasToPlayer(AController &Player)
}
}
void ACarlaGameMode::TagObjectsForSemanticSegmentation()
void ACarlaGameMode::TagActorsForSemanticSegmentation()
{
auto Tagger = GetWorld()->SpawnActor<ATagger>();
Tagger->TagObjects();
Tagger->Destroy(); // We don't need you anymore.
check(GetWorld() != nullptr);
ATagger::TagActorsInLevel(*GetWorld());
}
APlayerStart *ACarlaGameMode::FindUnOccupiedStartPoints(

View File

@ -35,7 +35,7 @@ private:
void AttachCaptureCamerasToPlayer(AController &Player);
void TagObjectsForSemanticSegmentation();
void TagActorsForSemanticSegmentation();
/// Iterate all the APlayerStart present in the world and add the ones with
/// unoccupied locations to @a UnOccupiedStartPoints.
@ -47,7 +47,9 @@ private:
CarlaGameControllerBase *GameController;
UPROPERTY()
UCarlaGameInstance *GameInstance;
UPROPERTY()
AController *PlayerController;
};

View File

@ -12,7 +12,7 @@
static FText RoundedFloatAsText(float Value)
{
return FText::AsNumber(FMath::FloorToInt(Value));
return FText::AsNumber(FMath::RoundHalfFromZero(Value));
}
static FText GetVectorAsText(const FVector &Vector)

View File

@ -3,10 +3,13 @@
#include "Carla.h"
#include "CarlaVehicleController.h"
#include "SceneCaptureCamera.h"
#include "Camera/CameraComponent.h"
#include "Components/BoxComponent.h"
#include "EngineUtils.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/SpringArmComponent.h"
#include "SceneCaptureCamera.h"
#include "WheeledVehicle.h"
#include "WheeledVehicleMovementComponent.h"
@ -82,6 +85,14 @@ void ACarlaVehicleController::Possess(APawn *aPawn)
// Get vehicle movement component.
MovementComponent = WheeledVehicle->GetVehicleMovementComponent();
check(MovementComponent != nullptr);
// Get vehicle box component.
TArray<UBoxComponent *> BoundingBoxes;
WheeledVehicle->GetComponents<UBoxComponent>(BoundingBoxes);
if (BoundingBoxes.Num() > 0) {
VehicleBounds = BoundingBoxes[0];
} else {
UE_LOG(LogCarla, Error, TEXT("Pawn is missing the bounding box!"));
}
// Get custom player state.
CarlaPlayerState = Cast<ACarlaPlayerState>(PlayerState);
check(CarlaPlayerState != nullptr);
@ -111,6 +122,11 @@ void ACarlaVehicleController::BeginPlay()
Image.PostProcessEffect = Camera->GetPostProcessEffect();
}
}
TActorIterator<ACityMapGenerator> It(GetWorld());
if (It) {
RoadMap = It->GetRoadMap();
}
}
void ACarlaVehicleController::Tick(float DeltaTime)
@ -126,7 +142,7 @@ void ACarlaVehicleController::Tick(float DeltaTime)
const FVector CurrentSpeed = CarlaPlayerState->ForwardSpeed * CarlaPlayerState->Orientation;
CarlaPlayerState->Acceleration = (CurrentSpeed - PreviousSpeed) / DeltaTime;
CarlaPlayerState->CurrentGear = GetVehicleCurrentGear();
/// @todo #15 Set intersection factors.
IntersectPlayerWithRoadMap();
const auto NumberOfCameras = SceneCaptureCameras.Num();
check(NumberOfCameras == CarlaPlayerState->Images.Num());
for (auto i = 0; i < NumberOfCameras; ++i) {
@ -283,3 +299,27 @@ void ACarlaVehicleController::ChangeCameraRight(float Value)
Rotation.Yaw -= Value;
SpringArm->SetRelativeRotation(Rotation);
}
// =============================================================================
// -- Other --------------------------------------------------------------------
// =============================================================================
void ACarlaVehicleController::IntersectPlayerWithRoadMap()
{
if (RoadMap == nullptr) {
UE_LOG(LogCarla, Error, TEXT("Controller doesn't have a road map"));
return;
} else if (VehicleBounds == nullptr) {
UE_LOG(LogCarla, Error, TEXT("Vehicle doesn't have a bounding box"));
return;
}
constexpr float ChecksPerCentimeter = 0.1f;
auto Result = RoadMap->Intersect(
GetPawn()->GetTransform(),
VehicleBounds->GetScaledBoxExtent(),
ChecksPerCentimeter);
CarlaPlayerState->OffRoadIntersectionFactor = Result.OffRoad;
CarlaPlayerState->OtherLaneIntersectionFactor = Result.OppositeLane;
}

View File

@ -8,7 +8,9 @@
class ACarlaHUD;
class ACarlaPlayerState;
class ASceneCaptureCamera;
class UBoxComponent;
class UCameraComponent;
class URoadMap;
class USpringArmComponent;
class UWheeledVehicleMovementComponent;
struct FCameraDescription;
@ -160,6 +162,22 @@ private:
void SetupControllerInput();
/// @}
// ===========================================================================
/// @name Other
// ===========================================================================
/// @{
public:
void SetRoadMap(URoadMap *inRoadMap)
{
RoadMap = inRoadMap;
}
private:
void IntersectPlayerWithRoadMap();
/// @}
// ===========================================================================
// -- Member variables -------------------------------------------------------
@ -181,6 +199,12 @@ private:
UPROPERTY()
TArray<ASceneCaptureCamera *> SceneCaptureCameras;
UPROPERTY()
URoadMap *RoadMap;
UPROPERTY()
UBoxComponent *VehicleBounds;
// Cast for quick access to the custom player state.
UPROPERTY()
ACarlaPlayerState *CarlaPlayerState;

View File

@ -1,28 +0,0 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "RoadMap.h"
#include "CapturedImage.h"
static FVector2D Decode(const FColor &Color);
// void RoadMap::SetCapture(FCapturedImage &inMapCapture, const FVector2D &inMapSizeInCm)
// {
// MapCapture = &inMapCapture;
// MapSizeInCm = inMapSizeInCm
// }
// bool RoadMap::IsValid() const
// {
// return ((MapCapture != nullptr) &&
// (MapCapture->BitMap.Num() > 0) &&
// (MapSizeInCm.X > 0.0f) &&
// (MapSizeInCm.Y > 0.0f));
// }
// FVector2D RoadMap::GetDirectionAt(const FVector &Location) const
// {
// check(IsValid());
// return FVector2D();
// }

View File

@ -1,29 +0,0 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "Math/Vector.h"
struct FCapturedImage;
/// Road map of the level. Contains information in 2D of which areas are road
/// and lane directions.
class CARLA_API RoadMap
{
public:
void SetCapture(
const FCapturedImage &MapCapture,
const FVector2D &Origin,
const FVector2D &MapExtent);
bool IsValid() const;
FVector2D GetDirectionAt(const FVector &Location) const;
private:
uint32 GetPixelIndexAt(const FVector2D &Location) const;
TArray<FVector2D> DirectionMap;
};

View File

@ -67,6 +67,9 @@ FVector ACityMapMeshHolder::GetTileLocation(uint32 X, uint32 Y) const
void ACityMapMeshHolder::SetStaticMesh(ECityMapMeshTag Tag, UStaticMesh *Mesh)
{
StaticMeshes[Tag] = Mesh;
if (Mesh != nullptr) {
TagMap.Add(Mesh, Tag);
}
}
UStaticMesh *ACityMapMeshHolder::GetStaticMesh(ECityMapMeshTag Tag)
@ -79,6 +82,12 @@ const UStaticMesh *ACityMapMeshHolder::GetStaticMesh(ECityMapMeshTag Tag) const
return StaticMeshes[Tag];
}
ECityMapMeshTag ACityMapMeshHolder::GetTag(const UStaticMesh &StaticMesh) const
{
const ECityMapMeshTag *Tag = TagMap.Find(&StaticMesh);
return (Tag != nullptr ? *Tag : ECityMapMeshTag::INVALID);
}
void ACityMapMeshHolder::AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y)
{
AddInstance(Tag, FTransform(GetTileLocation(X, Y)));

View File

@ -38,6 +38,11 @@ protected:
// ===========================================================================
protected:
float GetMapScale() const
{
return MapScale;
}
/// Return the 3D world location (relative to this actor) of the given 2D
/// tile.
FVector GetTileLocation(uint32 X, uint32 Y) const;
@ -51,6 +56,9 @@ protected:
/// Return the static mesh corresponding to @a Tag.
const UStaticMesh *GetStaticMesh(ECityMapMeshTag Tag) const;
/// Return the tag corresponding to @a StaticMesh.
ECityMapMeshTag GetTag(const UStaticMesh &StaticMesh) const;
/// Add an instance of a mesh with a given tile location.
/// @param Tag The mesh' tag
/// @param X Tile coordinate X
@ -89,12 +97,15 @@ private:
UPROPERTY()
USceneComponent *SceneRootComponent;
UPROPERTY(Category = "Map Generation", VisibleAnywhere)
float MapScale;
UPROPERTY(Category = "Meshes", EditAnywhere)
TMap<ECityMapMeshTag, UStaticMesh *> StaticMeshes;
UPROPERTY(Category = "Meshes|Debug", VisibleAnywhere)
float MapScale;
UPROPERTY()
TMap<UStaticMesh *, ECityMapMeshTag> TagMap;
UPROPERTY(Category = "Meshes|Debug", VisibleAnywhere)
UPROPERTY(Category = "Meshes", VisibleAnywhere)
TArray<UInstancedStaticMeshComponent *> MeshInstatiators;
};

View File

@ -46,7 +46,8 @@ enum class ECityMapMeshTag : uint8
RoadXIntersection_Sidewalk3 UMETA(DisplayName = "Road: X-Intersection - Sidewalk 3"),
RoadXIntersection_LaneMarking UMETA(DisplayName = "Road: X-Intersection - Lane Marking"),
NUMBER_OF_TAGS UMETA(Hidden)
NUMBER_OF_TAGS UMETA(Hidden),
INVALID UMETA(Hidden)
};
/// Helper class for working with ECityMapMeshTag.

View File

@ -0,0 +1,172 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "RoadMap.h"
#include "DrawDebugHelpers.h"
#include "HighResScreenshot.h"
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).
URoadMap::URoadMap(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer),
PixelsPerCentimeter(1.0f),
Width(1u),
Height(1u)
{
AppendEmptyPixel();
}
FVector URoadMap::GetWorldLocation(uint32 PixelX, uint32 PixelY) const
{
const FVector RelativePosition(
static_cast<float>(PixelX) / PixelsPerCentimeter,
static_cast<float>(PixelY) / PixelsPerCentimeter,
0.0f);
return WorldToMap.InverseTransformPosition(RelativePosition + MapOffset);
}
const FRoadMapPixelData &URoadMap::GetDataAt(const FVector &WorldLocation) const
{
check(IsValid());
const FVector Location = WorldToMap.TransformPosition(WorldLocation) - MapOffset;
uint32 X = ClampFloatToUInt(PixelsPerCentimeter * Location.X, 0, Width - 1);
uint32 Y = ClampFloatToUInt(PixelsPerCentimeter * Location.Y, 0, Height - 1);
return GetDataAt(X, Y);
}
FRoadMapIntersectionResult URoadMap::Intersect(
const FTransform &BoxTransform,
const FVector &BoxExtent,
float ChecksPerCentimeter) const
{
const auto &DirectionOfMovement = BoxTransform.GetRotation().GetForwardVector();
uint32 CheckCount = 0u;
FRoadMapIntersectionResult Result = {0.0f, 0.0f};
const float Step = 1.0f / ChecksPerCentimeter;
for (float X = -BoxExtent.X; X < BoxExtent.X; X += Step) {
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) {
Result.OffRoad += 1.0f;
} else if (Data.bHasDirection &&
0.0f < FVector::DotProduct(Data.Direction, DirectionOfMovement)) {
Result.OppositeLane += 1.0f;
}
}
}
if (CheckCount > 0u) {
Result.OffRoad /= static_cast<float>(CheckCount);
Result.OppositeLane /= static_cast<float>(CheckCount);
} else {
UE_LOG(LogCarla, Warning, TEXT("URoadMap::Intersect did zero checks"));
}
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()) {
UE_LOG(LogCarla, Error, TEXT("Cannot save invalid road map to disk"));
return false;
}
TArray<FColor> BitMap;
for (auto &Data : RoadMap) {
BitMap.Emplace(Encode(Data));
}
FIntPoint DestSize(Width, Height);
FString ResultPath;
FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig();
HighResScreenshotConfig.SaveImage(Path, BitMap, DestSize, &ResultPath);
UE_LOG(LogCarla, Log, TEXT("Saved road map to \"%s\""), *ResultPath);
return true;
}
void URoadMap::DrawDebugPixelsToLevel(UWorld *World, const bool bJustFlushDoNotDraw) const
{
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);
}
}
}
}
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;
}

View File

@ -0,0 +1,134 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "UObject/NoExportTypes.h"
#include "RoadMap.generated.h"
USTRUCT()
struct FRoadMapIntersectionResult
{
GENERATED_BODY()
/** Percentage of the box lying off-road */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float OffRoad;
/** Percentage of the box invading opposite lane (wrong direction) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
float OppositeLane;
};
USTRUCT()
struct FRoadMapPixelData
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
bool bIsOffRoad = true;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
bool bHasDirection = false;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
FVector Direction;
};
/// Road map of the level. Contains information in 2D of which areas are road
/// and lane directions.
UCLASS()
class CARLA_API URoadMap : public UObject
{
GENERATED_BODY()
public:
URoadMap(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintCallable)
bool IsValid() const
{
return ((RoadMap.Num() > 0) && (RoadMap.Num() == Height * Width));
}
uint32 GetWidth() const
{
return Width;
}
uint32 GetHeight() const
{
return Height;
}
FVector GetWorldLocation(uint32 PixelX, uint32 PixelY) const;
const FRoadMapPixelData &GetDataAt(uint32 PixelX, uint32 PixelY) const
{
check(IsValid());
return RoadMap[PixelX + Width * PixelY];
}
/** Clamps value if lies outside map limits */
UFUNCTION(BlueprintCallable)
const 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)
FRoadMapIntersectionResult Intersect(
const FTransform &BoxTransform,
const FVector &BoxExtent,
float ChecksPerCentimeter) const;
UFUNCTION(BlueprintCallable)
bool SaveAsPNG(const FString &Path) const;
/** Draw every pixel of the image as debug point */
UFUNCTION(BlueprintCallable)
void DrawDebugPixelsToLevel(UWorld *World, bool bJustFlushDoNotDraw = false) const;
private:
friend class ACityMapGenerator;
void AppendEmptyPixel()
{
RoadMap.AddDefaulted(1);
}
void AppendPixel(
ECityMapMeshTag Tag,
const FTransform &Transform,
bool bInvertDirection);
void Set(
uint32 Width,
uint32 Height,
float PixelsPerCentimeter,
const FTransform &WorldToMap,
const FVector &MapOffset);
UPROPERTY(VisibleAnywhere)
FTransform WorldToMap;
UPROPERTY(VisibleAnywhere)
FVector MapOffset;
UPROPERTY(VisibleAnywhere)
float PixelsPerCentimeter;
/** Width of the map in pixels */
UPROPERTY(VisibleAnywhere)
uint32 Width;
/** Height of the map in pixels */
UPROPERTY(VisibleAnywhere)
uint32 Height;
UPROPERTY()
TArray<FRoadMapPixelData> RoadMap;
};

View File

@ -0,0 +1,57 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "StaticMeshCollection.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Engine/StaticMesh.h"
AStaticMeshCollection::AStaticMeshCollection(
const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
RootComponent =
ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneComponent"));
RootComponent->SetMobility(EComponentMobility::Static);
}
void AStaticMeshCollection::PushBackInstantiator(UStaticMesh *Mesh)
{
auto Instantiator = NewObject<UInstancedStaticMeshComponent>(this);
check(Instantiator != nullptr);
Instantiator->SetMobility(EComponentMobility::Static);
Instantiator->SetupAttachment(RootComponent);
Instantiator->SetStaticMesh(Mesh);
Instantiator->RegisterComponent();
MeshInstantiators.Add(Instantiator);
}
void AStaticMeshCollection::SetStaticMesh(uint32 i, UStaticMesh *Mesh)
{
if ((GetNumberOfInstantiators() > i) && (MeshInstantiators[i] != nullptr)) {
MeshInstantiators[i]->SetStaticMesh(Mesh);
}
}
void AStaticMeshCollection::AddInstance(uint32 i, const FTransform &Transform)
{
if ((GetNumberOfInstantiators() > i) && (MeshInstantiators[i] != nullptr)) {
MeshInstantiators[i]->AddInstance(Transform);
}
}
void AStaticMeshCollection::ClearInstances()
{
for (auto *Instantiator : MeshInstantiators) {
if (Instantiator != nullptr) {
Instantiator->ClearInstances();
}
}
}
void AStaticMeshCollection::ClearInstantiators()
{
ClearInstances();
MeshInstantiators.Empty();
}

View File

@ -0,0 +1,42 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GameFramework/Actor.h"
#include "StaticMeshCollection.generated.h"
class UInstancedStaticMeshComponent;
/// Holds static mesh instatiators.
UCLASS(Abstract)
class CARLA_API AStaticMeshCollection : public AActor
{
GENERATED_BODY()
public:
AStaticMeshCollection(const FObjectInitializer& ObjectInitializer);
protected:
uint32 GetNumberOfInstantiators() const
{
return MeshInstantiators.Num();
}
void PushBackInstantiator(UStaticMesh *Mesh);
void SetStaticMesh(uint32 i, UStaticMesh *Mesh);
void AddInstance(uint32 i, const FTransform &Transform);
void ClearInstances();
/// Clear the instances too.
void ClearInstantiators();
private:
UPROPERTY(Category = "Instanced Static Mesh Collection", VisibleAnywhere)
TArray<UInstancedStaticMeshComponent *> MeshInstantiators;
};

View File

@ -0,0 +1,61 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "RoadIntersection.h"
ARoadIntersection::ARoadIntersection(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
RootComponent =
ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("RootComponent"));
RootComponent->SetMobility(EComponentMobility::Static);
#define CARLA_CREATE_STATIC_MESH_COMPONENT(Mesh) \
{ \
auto Component = CreateDefaultSubobject<UStaticMeshComponent>(TEXT(#Mesh) TEXT("Component")); \
Component->SetMobility(EComponentMobility::Static); \
Component->SetupAttachment(RootComponent); \
StaticMeshComponents.Add(Component); \
StaticMeshes.Add(ERoadIntersectionItem:: Mesh, nullptr); \
}
CARLA_CREATE_STATIC_MESH_COMPONENT(Lane0)
CARLA_CREATE_STATIC_MESH_COMPONENT(Lane1)
CARLA_CREATE_STATIC_MESH_COMPONENT(Lane2)
CARLA_CREATE_STATIC_MESH_COMPONENT(Lane3)
CARLA_CREATE_STATIC_MESH_COMPONENT(Sidewalk0)
CARLA_CREATE_STATIC_MESH_COMPONENT(Sidewalk1)
CARLA_CREATE_STATIC_MESH_COMPONENT(Sidewalk2)
CARLA_CREATE_STATIC_MESH_COMPONENT(Sidewalk3)
CARLA_CREATE_STATIC_MESH_COMPONENT(LaneMarking)
#undef CARLA_CREATE_STATIC_MESH_COMPONENT
}
#if WITH_EDITOR
void ARoadIntersection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property) {
UpdateMeshes();
}
}
#endif // WITH_EDITOR
void ARoadIntersection::SetStaticMesh(ERoadIntersectionItem Item, UStaticMesh *StaticMesh)
{
if (static_cast<uint8>(Item) < StaticMeshes.Num()) {
StaticMeshes[Item] = StaticMesh;
}
}
void ARoadIntersection::UpdateMeshes()
{
check(StaticMeshes.Num() == StaticMeshComponents.Num());
int32 i = 0;
for (auto Item : StaticMeshes) {
check(StaticMeshComponents[i] != nullptr);
StaticMeshComponents[i]->SetStaticMesh(Item.Value);
++i;
}
}

View File

@ -0,0 +1,49 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "RoadIntersection.generated.h"
UENUM(BlueprintType)
enum class ERoadIntersectionItem : uint8
{
Lane0 UMETA(DisplayName = "Lane 1"),
Lane1 UMETA(DisplayName = "Lane 2"),
Lane2 UMETA(DisplayName = "Lane 3"),
Lane3 UMETA(DisplayName = "Lane 4"),
Sidewalk0 UMETA(DisplayName = "Sidewalk 1"),
Sidewalk1 UMETA(DisplayName = "Sidewalk 2"),
Sidewalk2 UMETA(DisplayName = "Sidewalk 3"),
Sidewalk3 UMETA(DisplayName = "Sidewalk 4"),
LaneMarking UMETA(DisplayName = "LaneMarking"),
};
/// A road intersection.
UCLASS()
class CARLA_API ARoadIntersection : public AActor
{
GENERATED_BODY()
public:
ARoadIntersection(const FObjectInitializer& ObjectInitializer);
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
UFUNCTION(BlueprintCallable)
void SetStaticMesh(ERoadIntersectionItem Item, UStaticMesh *StaticMesh);
private:
void UpdateMeshes();
UPROPERTY()
TArray<UStaticMeshComponent *> StaticMeshComponents;
UPROPERTY(Category = "Meshes", EditAnywhere)
TMap<ERoadIntersectionItem, UStaticMesh *> StaticMeshes;
};

View File

@ -0,0 +1,84 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "RoadSegment.h"
#include "Engine/StaticMesh.h"
enum RoadSegmentItems {
ELaneLeft,
ELaneRight,
ESidewalkLeft,
ESidewalkRight,
ELaneMarkingSolid,
ELaneMarkingBroken,
NUMBER_OF_ITEMS
};
ARoadSegment::ARoadSegment(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
}
void ARoadSegment::OnConstruction(const FTransform &Transform)
{
Super::OnConstruction(Transform);
UpdateMeshes();
}
#if WITH_EDITOR
void ARoadSegment::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property) {
GenerateRoad();
}
}
#endif // WITH_EDITOR
void ARoadSegment::GenerateRoad()
{
UpdateMeshes();
UpdateRoad();
}
void ARoadSegment::UpdateMeshes()
{
if (GetNumberOfInstantiators() != NUMBER_OF_ITEMS) {
ClearInstantiators();
for (auto i = 0u; i < NUMBER_OF_ITEMS; ++i) {
PushBackInstantiator(nullptr);
}
}
SetStaticMesh(ELaneLeft, LaneLeft);
SetStaticMesh(ELaneRight, LaneRight);
SetStaticMesh(ESidewalkLeft, SidewalkLeft);
SetStaticMesh(ESidewalkRight, SidewalkRight);
SetStaticMesh(ELaneMarkingSolid, LaneMarkingSolid);
SetStaticMesh(ELaneMarkingBroken, LaneMarkingBroken);
}
void ARoadSegment::UpdateRoad()
{
ClearInstances();
Scale = (LaneLeft != nullptr ? LaneLeft->GetBoundingBox().GetSize().X : 1.0f);
FVector Translation(0.0f, 0.0f, 0.0f);
for (auto &Item : RoadDescription) {
FTransform Transform{Translation};
AddInstance(ELaneLeft, Transform);
AddInstance(ELaneRight, Transform);
if (Item.bHasRightSidewalk) {
AddInstance(ESidewalkRight, Transform);
}
if (Item.bHasLeftSidewalk) {
AddInstance(ESidewalkLeft, Transform);
}
if (Item.LaneMarking == ELaneMarkingType::Solid) {
AddInstance(ELaneMarkingSolid, Transform);
} else if (Item.LaneMarking == ELaneMarkingType::Broken) {
AddInstance(ELaneMarkingBroken, Transform);
}
Translation.X += Scale;
}
}

136
Source/Carla/RoadSegment.h Normal file
View File

@ -0,0 +1,136 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "MapGen/StaticMeshCollection.h"
#include "RoadSegment.generated.h"
UENUM(BlueprintType)
enum class ELaneMarkingType : uint8
{
None UMETA(DisplayName = "None"),
Solid UMETA(DisplayName = "Solid Lane Marking"),
Broken UMETA(DisplayName = "Broken Lane Marking")
};
/// Description of a road segment piece.
USTRUCT()
struct FRoadSegmentPiece
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bHasLeftSidewalk = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bHasRightSidewalk = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
ELaneMarkingType LaneMarking = ELaneMarkingType::Solid;
};
/// A straight segment of road.
///
/// Please call GenerateRoad after modifying it.
UCLASS()
class CARLA_API ARoadSegment : public AStaticMeshCollection
{
GENERATED_BODY()
public:
ARoadSegment(const FObjectInitializer& ObjectInitializer);
virtual void OnConstruction(const FTransform &Transform) override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
UFUNCTION(BlueprintCallable, Category="Road Description")
void GenerateRoad();
UFUNCTION(BlueprintCallable, Category="Road Description")
int32 GetNumberOfPieces() const
{
return RoadDescription.Num();
}
UFUNCTION(BlueprintCallable, Category="Road Description")
void AppendPiece(const FRoadSegmentPiece &RoadSegmentPiece)
{
RoadDescription.Add(RoadSegmentPiece);
}
UFUNCTION(BlueprintCallable, Category="Road Description")
void RemoveAllPieces()
{
RoadDescription.Empty();
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_LaneLeft(UStaticMesh *StaticMesh)
{
LaneLeft = StaticMesh;
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_LaneRight(UStaticMesh *StaticMesh)
{
LaneRight = StaticMesh;
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_SidewalkLeft(UStaticMesh *StaticMesh)
{
SidewalkLeft = StaticMesh;
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_SidewalkRight(UStaticMesh *StaticMesh)
{
SidewalkRight = StaticMesh;
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_LaneMarkingSolid(UStaticMesh *StaticMesh)
{
LaneMarkingSolid = StaticMesh;
}
UFUNCTION(BlueprintCallable, Category="Set Static Mesh")
void SetStaticMesh_LaneMarkingBroken(UStaticMesh *StaticMesh)
{
LaneMarkingBroken = StaticMesh;
}
private:
void UpdateMeshes();
void UpdateRoad();
UPROPERTY(Category = "Road Description", EditAnywhere)
TArray<FRoadSegmentPiece> RoadDescription;
UPROPERTY(Category = "Road Description", AdvancedDisplay, EditAnywhere)
float Scale = 1.0f;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *LaneLeft;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *LaneRight;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *SidewalkLeft;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *SidewalkRight;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *LaneMarkingSolid;
UPROPERTY(Category = "Meshes", EditAnywhere)
UStaticMesh *LaneMarkingBroken;
};

View File

@ -9,43 +9,112 @@
#include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/PhysicsAsset.h"
enum class Label : uint8
#ifdef CARLA_TAGGER_EXTRA_LOG
static FString GetLabelAsString(const CityObjectLabel Label)
{
None = 0u,
Buildings = 1u,
Fences = 2u,
Other = 3u,
Pedestrians = 4u,
Poles = 5u,
RoadLines = 6u,
Roads = 7u,
Sidewalks = 8u,
Vegetation = 9u,
Vehicles = 10u,
Walls = 11u,
};
static Label GetLabel(const FString &str) {
if (str == "Buildings") return Label::Buildings;
else if (str == "Fences") return Label::Fences;
else if (str == "Pedestrians") return Label::Pedestrians;
else if (str == "Pole") return Label::Poles;
else if (str == "Props") return Label::Other;
else if (str == "Road") return Label::Roads;
else if (str == "RoadLines") return Label::RoadLines;
else if (str == "SideWalk") return Label::Sidewalks;
else if (str == "Vegetation") return Label::Vegetation;
else if (str == "Vehicles") return Label::Vehicles;
else if (str == "Walls") return Label::Walls;
else return Label::None;
switch (Label) {
#define CARLA_GET_LABEL_STR(lbl) case CityObjectLabel:: lbl : return #lbl;
default:
CARLA_GET_LABEL_STR(None)
CARLA_GET_LABEL_STR(Buildings)
CARLA_GET_LABEL_STR(Fences)
CARLA_GET_LABEL_STR(Other)
CARLA_GET_LABEL_STR(Pedestrians)
CARLA_GET_LABEL_STR(Poles)
CARLA_GET_LABEL_STR(RoadLines)
CARLA_GET_LABEL_STR(Roads)
CARLA_GET_LABEL_STR(Sidewalks)
CARLA_GET_LABEL_STR(Vegetation)
CARLA_GET_LABEL_STR(Vehicles)
CARLA_GET_LABEL_STR(Walls)
#undef CARLA_GET_LABEL_STR
}
}
#endif // CARLA_TAGGER_EXTRA_LOG
template <typename T>
static auto cast(T label)
static auto CastEnum(T label)
{
return static_cast<typename std::underlying_type<T>::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;
}
template <typename T>
static CityObjectLabel GetLabelByPath(const T *Object)
{
const FString Path = Object->GetPathName();
TArray<FString> StringArray;
Path.ParseIntoArray(StringArray, TEXT("/"), false);
return (StringArray.Num() > 3 ? GetLabelByFolderName(StringArray[3]) : CityObjectLabel::None);
}
static void SetStencilValue(UPrimitiveComponent *comp, const CityObjectLabel &Label) {
if (Label != CityObjectLabel::None) {
comp->SetRenderCustomDepth(true);
comp->SetCustomDepthStencilValue(CastEnum(Label));
}
}
// =============================================================================
// -- static ATagger functions -------------------------------------------------
// =============================================================================
void ATagger::TagActor(const AActor &Actor)
{
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Log, TEXT("Actor: %s"), *Actor.GetName());
#endif // CARLA_TAGGER_EXTRA_LOG
// Iterate static meshes.
TArray<UStaticMeshComponent *> StaticMeshComponents;
Actor.GetComponents<UStaticMeshComponent>(StaticMeshComponents);
for (UStaticMeshComponent *Component : StaticMeshComponents) {
const auto Label = GetLabelByPath(Component->GetStaticMesh());
SetStencilValue(Component, Label);
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Log, TEXT(" + StaticMeshComponent: %s"), *Component->GetName());
UE_LOG(LogCarla, Log, TEXT(" - Label: \"%s\""), *GetLabelAsString(Label));
#endif // CARLA_TAGGER_EXTRA_LOG
}
// Iterate skeletal meshes.
TArray<USkeletalMeshComponent *> SkeletalMeshComponents;
Actor.GetComponents<USkeletalMeshComponent>(SkeletalMeshComponents);
for (USkeletalMeshComponent *Component : SkeletalMeshComponents) {
const auto Label = GetLabelByPath(Component->GetPhysicsAsset());
SetStencilValue(Component, Label);
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Log, TEXT(" + SkeletalMeshComponent: %s"), *Component->GetName());
UE_LOG(LogCarla, Log, TEXT(" - Label: \"%s\""), *GetLabelAsString(Label));
#endif // CARLA_TAGGER_EXTRA_LOG
}
}
void ATagger::TagActorsInLevel(UWorld &World)
{
for (TActorIterator<AActor> it(&World); it; ++it) {
TagActor(**it);
}
}
// =============================================================================
// -- non-static ATagger functions ---------------------------------------------
// =============================================================================
ATagger::ATagger()
{
PrimaryActorTick.bCanEverTick = false;
@ -56,81 +125,10 @@ void ATagger::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property) {
if (bTriggerTagObjects) {
TagObjects();
if (bTriggerTagObjects && (GetWorld() != nullptr)) {
TagActorsInLevel(*GetWorld());
}
}
bTriggerTagObjects = false;
}
#endif // WITH_EDITOR
static void setStencilValue(UPrimitiveComponent *comp, const Label &label) {
if (label != Label::None)
{
comp->SetRenderCustomDepth(true);
comp->SetCustomDepthStencilValue(cast(label));
}
}
void ATagger::TagObjects()
{
for (TActorIterator<AActor> it(GetWorld()); it; ++it) {
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Warning, TEXT("Actor: %s"), *it->GetName());
#endif // CARLA_TAGGER_EXTRA_LOG
/// get UStaticMeshComponents
TArray<UStaticMeshComponent*> staticComponents;
it->GetComponents<UStaticMeshComponent>(staticComponents);
for (auto& meshIt : staticComponents)
{
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Warning, TEXT(" + StaticMeshComponent: %s"), *meshIt->GetName());
#endif // CARLA_TAGGER_EXTRA_LOG
FString Path = meshIt->GetStaticMesh()->GetPathName();
TArray<FString> stringArray;
Path.ParseIntoArray(stringArray, TEXT("/"), false);
/*for (int32 i = 0; i < stringArray.Num(); i++) {
UE_LOG(LogCarla, Warning, TEXT(" -\"%s\""), *stringArray[i]);
}*/
if (stringArray.Num() > 3)
{
Label lab = GetLabel(stringArray[3]);
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Warning, TEXT(" - Label: \"%s\""), *stringArray[3]);
#endif // CARLA_TAGGER_EXTRA_LOG
setStencilValue(meshIt, lab);
}
}
/// get USkeletalMeshComponents
TArray<USkeletalMeshComponent*> skeletalComponents;
it->GetComponents<USkeletalMeshComponent>(skeletalComponents);
for (auto& meshIt : skeletalComponents)
{
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Warning, TEXT(" + SkeletalMeshComponent: %s"), *meshIt->GetName());
#endif // CARLA_TAGGER_EXTRA_LOG
FString Path = meshIt->GetPhysicsAsset()->GetPathName();
TArray<FString> stringArray;
Path.ParseIntoArray(stringArray, TEXT("/"), false);
if (stringArray.Num() > 3)
{
Label lab = GetLabel(stringArray[3]);
#ifdef CARLA_TAGGER_EXTRA_LOG
UE_LOG(LogCarla, Warning, TEXT(" - Label: \"%s\""), *stringArray[3]);
#endif // CARLA_TAGGER_EXTRA_LOG
setStencilValue(meshIt, lab);
}
}
}
}

View File

@ -5,6 +5,27 @@
#include "GameFramework/Actor.h"
#include "Tagger.generated.h"
enum class CityObjectLabel : uint8
{
None = 0u,
Buildings = 1u,
Fences = 2u,
Other = 3u,
Pedestrians = 4u,
Poles = 5u,
RoadLines = 6u,
Roads = 7u,
Sidewalks = 8u,
Vegetation = 9u,
Vehicles = 10u,
Walls = 11u,
};
/// Sets actors' custom depth stencil value for semantic segmentation according
/// to their meshes.
///
/// Non-static functions present so it can be dropped into the scene for testing
/// purposes.
UCLASS()
class CARLA_API ATagger : public AActor
{
@ -12,9 +33,11 @@ class CARLA_API ATagger : public AActor
public:
ATagger();
static void TagActor(const AActor &Actor);
void TagObjects();
static void TagActorsInLevel(UWorld &World);
ATagger();
protected: