14 #include "Components/InstancedStaticMeshComponent.h" 15 #include "Engine/World.h" 19 #include <unordered_set> 21 #ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG 23 #endif // 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 119 #endif // 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(),
126 DcelParser = MakeUnique<MapGen::GraphParser>(*Dcel);
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());
150 #endif // CARLA_ROAD_GENERATOR_EXTRA_LOG 155 check(
Dcel !=
nullptr);
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(),
313 #endif // WITH_EDITOR 321 #endif // WITH_EDITOR TUniquePtr< MapGen::DoublyConnectedEdgeList > Dcel
void AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y)
Add an instance of a mesh with a given tile location.
static bool LineTrace(UWorld *World, const FVector &Start, const FVector &End, FHitResult &HitResult)
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.
ACityMapGenerator(const FObjectInitializer &ObjectInitializer)
bool bLeftHandTraffic
Whether the road map should be generated based on left-hand traffic.
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
void UpdateSeeds()
Update the random seeds. Generate random if no fixed seed is used.
Simple doubly-connected edge list structure.
static TUniquePtr< DoublyConnectedEdgeList > Generate(uint32 SizeX, uint32 SizeY, int32 Seed)
Create a squared DoublyConnectedEdgeList of size SizeX times SizeY and generate random connections in...
virtual void UpdateMap() override
Here does nothing, implement in derived classes.
uint32 MapSizeX
Size X of the map in map units.
static uint32 GetRoadIntersectionSize()
Get the size in tiles of a road intersection side.
static HalfEdge & GetPair(HalfEdge &halfEdge)
bool bGenerateRoads
If false, no mesh is added, only the internal representation of road is generated.
uint32 PixelsPerMapUnit
The resolution in pixels per map unit of the road map.
DoublyConnectedEdgeList Graph
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
Set the tag of every actor in level.
int32 Seed
Seed of the random map generated.
bool Insert(const HalfEdge &InHalfEdge)
void GenerateRoadMap()
Generate the road map image and save to disk if requested.
uint32 MapSizeY
Size Y of the map in map units.
double min(double v1, double v2)
bool bTriggerRoadMapGeneration
Trigger the generation a the road map image of the current layout (used for off-road and opposite lan...
virtual void PreSave(const ITargetPlatform *TargetPlatform) override
void GenerateRoads()
Add the road meshes to the scene based on the current DCEL.
bool bSaveRoadMapToDisk
If true, the road map encoded as an image is saved to disk.
TUniquePtr< MapGen::GraphParser > DcelParser
bool bGenerateRoadMapOnSave
The road map is re-computed on save so we always store an up-to-date version.
std::unordered_set< const HalfEdge * > Set
bool bDrawDebugPixelsToLevel
If true, a debug point is drawn in the level for each pixel of the road map.
float GetMapScale() const
ECityMapMeshTag GetTag(const UStaticMesh &StaticMesh) const
Return the tag corresponding to StaticMesh.
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
Save the current map as PNG with the pixel data encoded as color.
bool bTagForSemanticSegmentation
If true, activate the custom depth pass of each tagged actor in the level.
bool bUseFixedSeed
If false, a random seed is generated each time.
#define ADD_INTERSECTION(tag, x, y, angle)
static bool MatchComponent(const UPrimitiveComponent &Component, crp::CityObjectLabel Tag)
Return true if Component has been tagged with the given Tag.
void GenerateGraph()
Regenerate the DCEL.