CARLA
 
载入中...
搜索中...
未找到
CityMapGenerator.cpp
浏览该文件的文档.
1// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#include "Carla.h"
8#include "CityMapGenerator.h"
9
11#include "MapGen/RoadMap.h"
12#include "Game/Tagger.h"
13
14#include "Components/InstancedStaticMeshComponent.h"
15#include "Engine/World.h"
16#include "Paths.h"
17
18#include <algorithm>
19#include <unordered_set>
20
21#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
22#include <sstream>
23#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
24
25namespace crp = carla::rpc;
26
27// =============================================================================
28// -- Private types ------------------------------------------------------------
29// =============================================================================
30
32public:
33
35
36 bool Insert(const HalfEdge &InHalfEdge)
37 {
38 return Set.insert(&InHalfEdge).second &&
39 Set.insert(&MapGen::DoublyConnectedEdgeList::GetPair(InHalfEdge)).second;
40 }
41
42private:
43
44 std::unordered_set<const HalfEdge *> Set;
45};
46
47// =============================================================================
48// -- Constructor and destructor -----------------------------------------------
49// =============================================================================
50
51ACityMapGenerator::ACityMapGenerator(const FObjectInitializer& ObjectInitializer)
52 : Super(ObjectInitializer)
53{
54 RoadMap = ObjectInitializer.CreateDefaultSubobject<URoadMap>(this, TEXT("RoadMap"));
55}
56
58
59// =============================================================================
60// -- Overriden from UObject ---------------------------------------------------
61// =============================================================================
62
63void ACityMapGenerator::PreSave(const ITargetPlatform *TargetPlatform)
64{
65#if WITH_EDITOR
67 // Generate road map only if we are not cooking.
68 FCoreUObjectDelegates::OnObjectSaved.Broadcast(this);
69 if (!GIsCookerLoadingPackage) {
70 check(RoadMap != nullptr);
72 }
73 }
74#endif // WITH_EDITOR
75
76 Super::PreSave(TargetPlatform);
77}
78
79// =============================================================================
80// -- Overriden from ACityMapMeshHolder ----------------------------------------
81// =============================================================================
82
95
96// =============================================================================
97// -- Map construction and update related methods ------------------------------
98// =============================================================================
99
101{
102 if (!bUseFixedSeed) {
103 FRandomStream randomStream;
104 randomStream.GenerateNewSeed();
105 Seed = randomStream.GetCurrentSeed();
106 }
107}
108
110{
111 if ((MapSizeX < 5u) || (MapSizeY < 5u)) {
112 MapSizeX = 5u;
113 MapSizeY = 5u;
114 UE_LOG(LogCarla, Warning, TEXT("Map size changed, was too small"));
115 }
116#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
117 // Delete the dcel before the new one is created so indices are restored.
118 Dcel.Reset(nullptr);
119#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
121 UE_LOG(LogCarla, Log,
122 TEXT("Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
123 Dcel->CountNodes(),
124 Dcel->CountHalfEdges(),
125 Dcel->CountFaces());
126 DcelParser = MakeUnique<MapGen::GraphParser>(*Dcel);
127#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
128 { // print the results of the parser.
129 std::wstringstream sout;
130 sout << "\nGenerated " << DcelParser->CityAreaCount() << " city areas: ";
131 for (auto i = 0u; i < DcelParser->CityAreaCount(); ++i) {
132 sout << "{ ";
133 auto &cityArea = DcelParser->GetCityAreaAt(i);
134 for (size_t j = 0u; j < cityArea.NodeCount(); ++j) {
135 sout << cityArea.GetNodeAt(j) << " ";
136 }
137 sout << "} ";
138 }
139 sout << "\nGenerated " << DcelParser->RoadSegmentCount() << " road segments: ";
140 for (auto i = 0u; i < DcelParser->RoadSegmentCount(); ++i) {
141 sout << "{ ";
142 auto &roadSegment = DcelParser->GetRoadSegmentAt(i);
143 for (size_t j = 0u; j < roadSegment.Size(); ++j) {
144 sout << roadSegment[j] << " ";
145 }
146 sout << "} ";
147 }
148 UE_LOG(LogCarla, Log, TEXT("\n%s"), sout.str().c_str());
149 }
150#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
151}
152
154{
155 check(Dcel != nullptr);
157 const Graph &graph = *Dcel;
158
159 const uint32 margin = CityMapMeshTag::GetRoadIntersectionSize() / 2u;
160
161 FHalfEdgeCounter HalfEdgeCounter;
162
163 // For each edge add road segment.
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();
168
169 if (source.x == target.x) {
170 // vertical
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);
179 }
180 } else if (source.y == target.y) {
181 // horizontal
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);
190 }
191 } else {
192 UE_LOG(LogCarla, Warning, TEXT("Diagonal edge ignored"));
193 }
194 }
195 }
196
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);
213
214 // For each node add the intersection.
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);
220 break;
222 ADD_INTERSECTION(ECityMapMeshTag::RoadTIntersection, coords.x, coords.y, node.Rotation);
223 break;
225 ADD_INTERSECTION(ECityMapMeshTag::RoadXIntersection, coords.x, coords.y, node.Rotation);
226 break;
227 default:
228 UE_LOG(LogCarla, Warning, TEXT("Intersection type not implemented"));
229 }
230 }
231
232#undef ADD_INTERSECTION
233}
234
235// Find first component of type road.
236static bool LineTrace(
237 UWorld *World,
238 const FVector &Start,
239 const FVector &End,
240 FHitResult &HitResult)
241{
242 TArray <FHitResult> OutHits;
243 static FName TraceTag = FName(TEXT("RoadTrace"));
244 const bool Success = World->LineTraceMultiByObjectType(
245 OutHits,
246 Start,
247 End,
248 FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
249 FCollisionQueryParams(TraceTag, true));
250
251 if (Success) {
252 for (FHitResult &Item : OutHits) {
253 if (ATagger::MatchComponent(*Item.Component, crp::CityObjectLabel::Roads)) {
254 HitResult = Item;
255 return true;
256 }
257 }
258 }
259 return false;
260}
261
263{
264 UE_LOG(LogCarla, Log, TEXT("Generating road map..."));
265
266 auto World = GetWorld();
267 check(GetWorld() != nullptr);
268 check(RoadMap != nullptr);
269
270 ATagger::TagActorsInLevel(*GetWorld(), bTagForSemanticSegmentation); // We need the tags.
271
272 const float IntersectionSize = CityMapMeshTag::GetRoadIntersectionSize();
273 const uint32 Margin = IntersectionSize / 2u;
274 const float Offset = GetMapScale() * Margin;
275
276 const float CmPerPixel = GetMapScale() / static_cast<float>(PixelsPerMapUnit);
277
278 const uint32 SizeX = PixelsPerMapUnit * (MapSizeX + 2u * Margin);
279 const uint32 SizeY = PixelsPerMapUnit * (MapSizeY + 2u * Margin);
280
281 const FTransform &ActorTransform = GetActorTransform();
282
283 const FVector MapOffset(-Offset, -Offset, 0.0f);
284 RoadMap->Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset);
285
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));
292
293 // Do the ray tracing.
294 FHitResult Hit;
295 if (LineTrace(World, Start, End, Hit)) {
296 auto StaticMeshComponent = Cast<UStaticMeshComponent>(Hit.Component.Get());
297 if (StaticMeshComponent == nullptr) {
298 UE_LOG(LogCarla, Error, TEXT("Road component is not UInstancedStaticMeshComponent"));
299 } else {
301 PixelX,
302 PixelY,
303 GetTag(*StaticMeshComponent->GetStaticMesh()),
304 StaticMeshComponent->GetOwner()->GetTransform(),
306 }
307 }
308 }
309 }
310
311#if WITH_EDITOR
312 RoadMap->Log();
313#endif // WITH_EDITOR
314
315 if (bSaveRoadMapToDisk) {
316 RoadMap->SaveAsPNG(FPaths::ProjectSavedDir(), World->GetMapName());
317 }
318
319#if WITH_EDITOR
320 RoadMap->DrawDebugPixelsToLevel(GetWorld(), !bDrawDebugPixelsToLevel);
321#endif // WITH_EDITOR
322}
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.
Definition Tagger.h:62
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
Set the tag of every actor in level.
Definition Tagger.cpp:223
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...
Road map of the level.
Definition RoadMap.h:91
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.
Definition RoadMap.cpp:83
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
Definition RoadMap.cpp:98
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
Save the current map as PNG with the pixel data encoded as color.
Definition RoadMap.cpp:251