14#include "Components/InstancedStaticMeshComponent.h"
15#include "Engine/World.h"
19#include <unordered_set>
21#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
38 return Set.insert(&InHalfEdge).second &&
44 std::unordered_set<const HalfEdge *>
Set;
52 : Super(ObjectInitializer)
54 RoadMap = ObjectInitializer.CreateDefaultSubobject<
URoadMap>(
this, TEXT(
"RoadMap"));
68 FCoreUObjectDelegates::OnObjectSaved.Broadcast(
this);
69 if (!GIsCookerLoadingPackage) {
76 Super::PreSave(TargetPlatform);
103 FRandomStream randomStream;
104 randomStream.GenerateNewSeed();
105 Seed = randomStream.GetCurrentSeed();
114 UE_LOG(LogCarla, Warning, TEXT(
"Map size changed, was too small"));
116#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
121 UE_LOG(LogCarla, Log,
122 TEXT(
"Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
124 Dcel->CountHalfEdges(),
127#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
129 std::wstringstream sout;
130 sout <<
"\nGenerated " <<
DcelParser->CityAreaCount() <<
" city areas: ";
131 for (
auto i = 0u; i <
DcelParser->CityAreaCount(); ++i) {
133 auto &cityArea =
DcelParser->GetCityAreaAt(i);
134 for (
size_t j = 0u; j < cityArea.NodeCount(); ++j) {
135 sout << cityArea.GetNodeAt(j) <<
" ";
139 sout <<
"\nGenerated " <<
DcelParser->RoadSegmentCount() <<
" road segments: ";
140 for (
auto i = 0u; i <
DcelParser->RoadSegmentCount(); ++i) {
142 auto &roadSegment =
DcelParser->GetRoadSegmentAt(i);
143 for (
size_t j = 0u; j < roadSegment.Size(); ++j) {
144 sout << roadSegment[j] <<
" ";
148 UE_LOG(LogCarla, Log, TEXT(
"\n%s"), sout.str().c_str());
155 check(
Dcel !=
nullptr);
157 const Graph &graph = *
Dcel;
164 for (
auto &edge : graph.GetHalfEdges()) {
165 if (HalfEdgeCounter.
Insert(edge)) {
166 auto source = Graph::GetSource(edge).GetPosition();
167 auto target = Graph::GetTarget(edge).GetPosition();
169 if (source.x == target.x) {
171 auto y = 1u + margin + std::min(source.y, target.y);
172 auto end = std::max(source.y, target.y) - margin;
173 for (; y < end; ++y) {
174 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft, source.x, y, HALF_PI);
175 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight, source.x, y, HALF_PI);
176 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft, source.x, y, HALF_PI);
177 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight, source.x, y, HALF_PI);
178 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken, source.x, y, HALF_PI);
180 }
else if (source.y == target.y) {
182 auto x = 1u + margin + std::min(source.x, target.x);
183 auto end = std::max(source.x, target.x) - margin;
184 for (; x < end; ++x) {
185 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft, x, source.y);
186 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight, x, source.y);
187 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft, x, source.y);
188 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight, x, source.y);
189 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken, x, source.y);
192 UE_LOG(LogCarla, Warning, TEXT(
"Diagonal edge ignored"));
197#define ADD_INTERSECTION(tag, x, y, angle) \
198 AddInstance(tag ##_Lane0, x, y, angle); \
199 AddInstance(tag ##_Lane1, x, y, angle); \
200 AddInstance(tag ##_Lane2, x, y, angle); \
201 AddInstance(tag ##_Lane3, x, y, angle); \
202 AddInstance(tag ##_Lane4, x, y, angle); \
203 AddInstance(tag ##_Lane5, x, y, angle); \
204 AddInstance(tag ##_Lane6, x, y, angle); \
205 AddInstance(tag ##_Lane7, x, y, angle); \
206 AddInstance(tag ##_Lane8, x, y, angle); \
207 AddInstance(tag ##_Lane9, x, y, angle); \
208 AddInstance(tag ##_Sidewalk0, x, y, angle); \
209 AddInstance(tag ##_Sidewalk1, x, y, angle); \
210 AddInstance(tag ##_Sidewalk2, x, y, angle); \
211 AddInstance(tag ##_Sidewalk3, x, y, angle); \
212 AddInstance(tag ##_LaneMarking, x, y, angle);
215 for (
auto &node : graph.GetNodes()) {
216 const auto coords = node.GetPosition();
217 switch (node.IntersectionType) {
219 ADD_INTERSECTION(ECityMapMeshTag::Road90DegTurn, coords.x, coords.y, node.Rotation);
222 ADD_INTERSECTION(ECityMapMeshTag::RoadTIntersection, coords.x, coords.y, node.Rotation);
225 ADD_INTERSECTION(ECityMapMeshTag::RoadXIntersection, coords.x, coords.y, node.Rotation);
228 UE_LOG(LogCarla, Warning, TEXT(
"Intersection type not implemented"));
232#undef ADD_INTERSECTION
238 const FVector &Start,
240 FHitResult &HitResult)
242 TArray <FHitResult> OutHits;
243 static FName TraceTag = FName(TEXT(
"RoadTrace"));
244 const bool Success = World->LineTraceMultiByObjectType(
248 FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
249 FCollisionQueryParams(TraceTag,
true));
252 for (FHitResult &Item : OutHits) {
264 UE_LOG(LogCarla, Log, TEXT(
"Generating road map..."));
266 auto World = GetWorld();
267 check(GetWorld() !=
nullptr);
273 const uint32 Margin = IntersectionSize / 2u;
281 const FTransform &ActorTransform = GetActorTransform();
283 const FVector MapOffset(-Offset, -Offset, 0.0f);
284 RoadMap->
Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset);
286 for (uint32 PixelY = 0u; PixelY < SizeY; ++PixelY) {
287 for (uint32 PixelX = 0u; PixelX < SizeX; ++PixelX) {
288 const float X =
static_cast<float>(PixelX) * CmPerPixel - Offset;
289 const float Y =
static_cast<float>(PixelY) * CmPerPixel - Offset;
290 const FVector Start = ActorTransform.TransformPosition(FVector(X, Y, 50.0f));
291 const FVector End = ActorTransform.TransformPosition(FVector(X, Y, -50.0f));
296 auto StaticMeshComponent = Cast<UStaticMeshComponent>(Hit.Component.Get());
297 if (StaticMeshComponent ==
nullptr) {
298 UE_LOG(LogCarla, Error, TEXT(
"Road component is not UInstancedStaticMeshComponent"));
303 GetTag(*StaticMeshComponent->GetStaticMesh()),
304 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 PreSave(const ITargetPlatform *TargetPlatform) override
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
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.