13#include <util/ue-header-guard-begin.h>
14#include "Components/InstancedStaticMeshComponent.h"
15#include "Engine/World.h"
16#include "Misc/Paths.h"
17#include <util/ue-header-guard-end.h>
20#include <unordered_set>
22#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
39 return Set.insert(&InHalfEdge).second &&
45 std::unordered_set<const HalfEdge *>
Set;
53 : Super(ObjectInitializer)
55 RoadMap = ObjectInitializer.CreateDefaultSubobject<
URoadMap>(
this, TEXT(
"RoadMap"));
69 FCoreUObjectDelegates::OnObjectPreSave.Broadcast(
this, ObjectSaveContext);
70 if (!GIsCookerLoadingPackage) {
77 Super::PreSave(ObjectSaveContext);
104 FRandomStream randomStream;
105 randomStream.GenerateNewSeed();
106 Seed = randomStream.GetCurrentSeed();
115 UE_LOG(LogCarla, Warning, TEXT(
"Map size changed, was too small"));
117#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
122 UE_LOG(LogCarla, Log,
123 TEXT(
"Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
125 Dcel->CountHalfEdges(),
128#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
130 std::wstringstream sout;
131 sout <<
"\nGenerated " <<
DcelParser->CityAreaCount() <<
" city areas: ";
132 for (
auto i = 0u; i <
DcelParser->CityAreaCount(); ++i) {
134 auto &cityArea =
DcelParser->GetCityAreaAt(i);
135 for (
size_t j = 0u; j < cityArea.NodeCount(); ++j) {
136 sout << cityArea.GetNodeAt(j) <<
" ";
140 sout <<
"\nGenerated " <<
DcelParser->RoadSegmentCount() <<
" road segments: ";
141 for (
auto i = 0u; i <
DcelParser->RoadSegmentCount(); ++i) {
143 auto &roadSegment =
DcelParser->GetRoadSegmentAt(i);
144 for (
size_t j = 0u; j < roadSegment.Size(); ++j) {
145 sout << roadSegment[j] <<
" ";
149 UE_LOG(LogCarla, Log, TEXT(
"\n%s"), sout.str().c_str());
156 check(
Dcel !=
nullptr);
158 const Graph &graph = *
Dcel;
165 for (
auto &edge : graph.GetHalfEdges()) {
166 if (HalfEdgeCounter.
Insert(edge)) {
167 auto source = Graph::GetSource(edge).GetPosition();
168 auto target = Graph::GetTarget(edge).GetPosition();
170 if (source.x == target.x) {
172 auto y = 1u + margin + std::min(source.y, target.y);
173 auto end = std::max(source.y, target.y) - margin;
174 for (;
y < end; ++
y) {
175 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft, source.x,
y, HALF_PI);
176 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight, source.x,
y, HALF_PI);
177 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft, source.x,
y, HALF_PI);
178 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight, source.x,
y, HALF_PI);
179 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken, source.x,
y, HALF_PI);
181 }
else if (source.y == target.y) {
183 auto x = 1u + margin + std::min(source.x, target.x);
184 auto end = std::max(source.x, target.x) - margin;
185 for (;
x < end; ++
x) {
186 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft,
x, source.y);
187 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight,
x, source.y);
188 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft,
x, source.y);
189 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight,
x, source.y);
190 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken,
x, source.y);
193 UE_LOG(LogCarla, Warning, TEXT(
"Diagonal edge ignored"));
198#define ADD_INTERSECTION(tag, x, y, angle) \
199 AddInstance(tag ##_Lane0, x, y, angle); \
200 AddInstance(tag ##_Lane1, x, y, angle); \
201 AddInstance(tag ##_Lane2, x, y, angle); \
202 AddInstance(tag ##_Lane3, x, y, angle); \
203 AddInstance(tag ##_Lane4, x, y, angle); \
204 AddInstance(tag ##_Lane5, x, y, angle); \
205 AddInstance(tag ##_Lane6, x, y, angle); \
206 AddInstance(tag ##_Lane7, x, y, angle); \
207 AddInstance(tag ##_Lane8, x, y, angle); \
208 AddInstance(tag ##_Lane9, x, y, angle); \
209 AddInstance(tag ##_Sidewalk0, x, y, angle); \
210 AddInstance(tag ##_Sidewalk1, x, y, angle); \
211 AddInstance(tag ##_Sidewalk2, x, y, angle); \
212 AddInstance(tag ##_Sidewalk3, x, y, angle); \
213 AddInstance(tag ##_LaneMarking, x, y, angle);
216 for (
auto &
node : graph.GetNodes()) {
217 const auto coords =
node.GetPosition();
218 switch (
node.IntersectionType) {
229 UE_LOG(LogCarla, Warning, TEXT(
"Intersection type not implemented"));
233#undef ADD_INTERSECTION
239 const FVector &Start,
241 FHitResult &HitResult)
243 TArray <FHitResult> OutHits;
244 static FName TraceTag = FName(TEXT(
"RoadTrace"));
245 const bool Success = World->LineTraceMultiByObjectType(
249 FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
250 FCollisionQueryParams(TraceTag,
true));
253 for (FHitResult &Item : OutHits) {
265 UE_LOG(LogCarla, Log, TEXT(
"Generating road map..."));
267 auto World = GetWorld();
268 check(GetWorld() !=
nullptr);
274 const uint32 Margin = IntersectionSize / 2u;
282 const FTransform &ActorTransform = GetActorTransform();
284 const FVector MapOffset(-Offset, -Offset, 0.0f);
285 RoadMap->
Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset);
287 for (uint32 PixelY = 0u; PixelY < SizeY; ++PixelY) {
288 for (uint32 PixelX = 0u; PixelX < SizeX; ++PixelX) {
289 const float X =
static_cast<float>(PixelX) * CmPerPixel - Offset;
290 const float Y =
static_cast<float>(PixelY) * CmPerPixel - Offset;
291 const FVector Start = ActorTransform.TransformPosition(FVector(X, Y, 50.0f));
292 const FVector End = ActorTransform.TransformPosition(FVector(X, Y, -50.0f));
297 auto StaticMeshComponent = Cast<UStaticMeshComponent>(Hit.Component.Get());
298 if (StaticMeshComponent ==
nullptr) {
299 UE_LOG(LogCarla, Error, TEXT(
"Road component is not UInstancedStaticMeshComponent"));
304 GetTag(*StaticMeshComponent->GetStaticMesh()),
305 StaticMeshComponent->GetOwner()->GetTransform(),
static bool LineTrace(UWorld *World, const FVector &Start, const FVector &End, FHitResult &HitResult)
#define ADD_INTERSECTION(tag, x, y, angle)
bool bGenerateRoadMapOnSave
The road map is re-computed on save so we always store an up-to-date version.
void GenerateGraph()
Regenerate the DCEL.
bool bGenerateRoads
If false, no mesh is added, only the internal representation of road is generated.
uint32 MapSizeY
Size Y of the map in map units.
int32 Seed
Seed of the random map generated.
uint32 PixelsPerMapUnit
The resolution in pixels per map unit of the road map.
virtual void UpdateMap() override
Here does nothing, implement in derived classes.
TUniquePtr< MapGen::DoublyConnectedEdgeList > Dcel
bool bDrawDebugPixelsToLevel
If true, a debug point is drawn in the level for each pixel of the road map.
void GenerateRoads()
Add the road meshes to the scene based on the current DCEL.
bool bLeftHandTraffic
Whether the road map should be generated based on left-hand traffic.
bool bUseFixedSeed
If false, a random seed is generated each time.
void GenerateRoadMap()
Generate the road map image and save to disk if requested.
void UpdateSeeds()
Update the random seeds. Generate random if no fixed seed is used.
bool bTriggerRoadMapGeneration
Trigger the generation a the road map image of the current layout (used for off-road and opposite lan...
ACityMapGenerator(const FObjectInitializer &ObjectInitializer)
TUniquePtr< MapGen::GraphParser > DcelParser
virtual void PreSave(FObjectPreSaveContext ObjectSaveContext) override
uint32 MapSizeX
Size X of the map in map units.
bool bSaveRoadMapToDisk
If true, the road map encoded as an image is saved to disk.
bool bTagForSemanticSegmentation
If true, activate the custom depth pass of each tagged actor in the level.
void AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y)
Add an instance of a mesh with a given tile location.
float GetMapScale() const
ECityMapMeshTag GetTag(const UStaticMesh &StaticMesh) const
Return the tag corresponding to StaticMesh.
static bool MatchComponent(const UPrimitiveComponent &Component, crp::CityObjectLabel Tag)
Return true if Component has been tagged with the given Tag.
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
Set the tag of every actor in level.
static uint32 GetRoadIntersectionSize()
Get the size in tiles of a road intersection side.
std::unordered_set< const HalfEdge * > Set
bool Insert(const HalfEdge &InHalfEdge)
Simple doubly-connected edge list structure.
static HalfEdge & GetPair(HalfEdge &halfEdge)
static TUniquePtr< DoublyConnectedEdgeList > Generate(uint32 SizeX, uint32 SizeY, int32 Seed)
Create a squared DoublyConnectedEdgeList of size SizeX times SizeY and generate random connections in...
void Reset(uint32 Width, uint32 Height, float PixelsPerCentimeter, const FTransform &WorldToMap, const FVector &MapOffset)
Resets current map an initializes an empty map of the given size.
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
Save the current map as PNG with the pixel data encoded as color.