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" // 包含Carla模块的主要头文件
8#include "CityMapGenerator.h" // 包含城市地图生成器的头文件
9
10#include "MapGen/GraphGenerator.h" // 包含图生成器的头文件
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; // 使用carla::rpc命名空间的别名
26
27// =============================================================================
28// -- 私有类型 ------------------------------------------------------------
29// =============================================================================
30
31class FHalfEdgeCounter { // 定义一个用于计数半边的辅助类
32public:
33 using HalfEdge = MapGen::DoublyConnectedEdgeList::HalfEdge; // 使用图生成器中的HalfEdge类型
34
35 bool Insert(const HalfEdge &InHalfEdge) { // 插入一个半边并返回是否成功
36 return Set.insert(&InHalfEdge).second && // 插入半边
37 Set.insert(&MapGen::DoublyConnectedEdgeList::GetPair(InHalfEdge)).second; // 插入配对的半边
38 }
39
40private:
41 std::unordered_set<const HalfEdge *> Set; // 存储半边指针的无序集合
42};
43
44// =============================================================================
45// -- 构造函数和析构函数-----------------------------------------------
46// =============================================================================
47
48ACityMapGenerator::ACityMapGenerator(const FObjectInitializer& ObjectInitializer) // 城市地图生成器的构造函数
49 : Super(ObjectInitializer) // 调用父类的构造函数
50{
51 RoadMap = ObjectInitializer.CreateDefaultSubobject<URoadMap>(this, TEXT("RoadMap")); // 创建默认的道路图子对象
52}
53
54ACityMapGenerator::~ACityMapGenerator() {} // 城市地图生成器的析构函数
55
56// =============================================================================
57// -- 从 UObject 覆盖 ---------------------------------------------------
58// =============================================================================
59
60void ACityMapGenerator::PreSave(const ITargetPlatform *TargetPlatform) // 覆盖UObject的PreSave函数,用于保存前的准备
61{
62#if WITH_EDITOR
64 //只有在我们不烹饪时才生成路线图。
65 FCoreUObjectDelegates::OnObjectSaved.Broadcast(this);
66 if (!GIsCookerLoadingPackage) {
67 check(RoadMap != nullptr);
69 }
70 }
71#endif // WITH_EDITOR
72
73 Super::PreSave(TargetPlatform);
74}
75
76// =============================================================================
77// -- 从 ACityMapMeshHolder 覆盖----------------------------------------
78// =============================================================================
79
92
93// =============================================================================
94// -- 地图构建和更新相关方法 ------------------------------
95// =============================================================================
96
98{
99 if (!bUseFixedSeed) {
100 FRandomStream randomStream;
101 randomStream.GenerateNewSeed();
102 Seed = randomStream.GetCurrentSeed();
103 }
104}
105
107{
108 if ((MapSizeX < 5u) || (MapSizeY < 5u)) {
109 MapSizeX = 5u;
110 MapSizeY = 5u;
111 UE_LOG(LogCarla, Warning, TEXT("Map size changed, was too small"));
112 }
113#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
114 //在创建新 dcel 之前删除 dcel,以便恢复索引。
115 Dcel.Reset(nullptr);
116#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
118 UE_LOG(LogCarla, Log,
119 TEXT("Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
120 Dcel->CountNodes(),
121 Dcel->CountHalfEdges(),
122 Dcel->CountFaces());
123 DcelParser = MakeUnique<MapGen::GraphParser>(*Dcel);
124#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
125 { //打印解析器的结果。
126 std::wstringstream sout;
127 sout << "\nGenerated " << DcelParser->CityAreaCount() << " city areas: ";
128 for (auto i = 0u; i < DcelParser->CityAreaCount(); ++i) {
129 sout << "{ ";
130 auto &cityArea = DcelParser->GetCityAreaAt(i);
131 for (size_t j = 0u; j < cityArea.NodeCount(); ++j) {
132 sout << cityArea.GetNodeAt(j) << " ";
133 }
134 sout << "} ";
135 }
136 sout << "\nGenerated " << DcelParser->RoadSegmentCount() << " road segments: ";
137 for (auto i = 0u; i < DcelParser->RoadSegmentCount(); ++i) {
138 sout << "{ ";
139 auto &roadSegment = DcelParser->GetRoadSegmentAt(i);
140 for (size_t j = 0u; j < roadSegment.Size(); ++j) {
141 sout << roadSegment[j] << " ";
142 }
143 sout << "} ";
144 }
145 UE_LOG(LogCarla, Log, TEXT("\n%s"), sout.str().c_str());
146 }
147#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
148}
149
151{
152 check(Dcel != nullptr);
154 const Graph &graph = *Dcel;
155
156 const uint32 margin = CityMapMeshTag::GetRoadIntersectionSize() / 2u;
157
158 FHalfEdgeCounter HalfEdgeCounter;
159
160 // 对于每条边,添加路段。
161 for (auto &edge : graph.GetHalfEdges()) {
162 if (HalfEdgeCounter.Insert(edge)) {
163 auto source = Graph::GetSource(edge).GetPosition();
164 auto target = Graph::GetTarget(edge).GetPosition();
165
166 if (source.x == target.x) {
167 // 垂直
168 auto y = 1u + margin + std::min(source.y, target.y);
169 auto end = std::max(source.y, target.y) - margin;
170 for (; y < end; ++y) {
171 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft, source.x, y, HALF_PI);
172 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight, source.x, y, HALF_PI);
173 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft, source.x, y, HALF_PI);
174 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight, source.x, y, HALF_PI);
175 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken, source.x, y, HALF_PI);
176 }
177 } else if (source.y == target.y) {
178 // 水平
179 auto x = 1u + margin + std::min(source.x, target.x);
180 auto end = std::max(source.x, target.x) - margin;
181 for (; x < end; ++x) {
182 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneLeft, x, source.y);
183 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneRight, x, source.y);
184 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkLeft, x, source.y);
185 AddInstance(ECityMapMeshTag::RoadTwoLanes_SidewalkRight, x, source.y);
186 AddInstance(ECityMapMeshTag::RoadTwoLanes_LaneMarkingBroken, x, source.y);
187 }
188 } else {
189 UE_LOG(LogCarla, Warning, TEXT("Diagonal edge ignored"));
190 }
191 }
192 }
193
194#define ADD_INTERSECTION(tag, x, y, angle) \
195 AddInstance(tag ##_Lane0, x, y, angle); \
196 AddInstance(tag ##_Lane1, x, y, angle); \
197 AddInstance(tag ##_Lane2, x, y, angle); \
198 AddInstance(tag ##_Lane3, x, y, angle); \
199 AddInstance(tag ##_Lane4, x, y, angle); \
200 AddInstance(tag ##_Lane5, x, y, angle); \
201 AddInstance(tag ##_Lane6, x, y, angle); \
202 AddInstance(tag ##_Lane7, x, y, angle); \
203 AddInstance(tag ##_Lane8, x, y, angle); \
204 AddInstance(tag ##_Lane9, x, y, angle); \
205 AddInstance(tag ##_Sidewalk0, x, y, angle); \
206 AddInstance(tag ##_Sidewalk1, x, y, angle); \
207 AddInstance(tag ##_Sidewalk2, x, y, angle); \
208 AddInstance(tag ##_Sidewalk3, x, y, angle); \
209 AddInstance(tag ##_LaneMarking, x, y, angle);
210
211 // 对于每个节点,添加交集。
212 for (auto &node : graph.GetNodes()) {
213 const auto coords = node.GetPosition();
214 switch (node.IntersectionType) {
216 ADD_INTERSECTION(ECityMapMeshTag::Road90DegTurn, coords.x, coords.y, node.Rotation);
217 break;
219 ADD_INTERSECTION(ECityMapMeshTag::RoadTIntersection, coords.x, coords.y, node.Rotation);
220 break;
222 ADD_INTERSECTION(ECityMapMeshTag::RoadXIntersection, coords.x, coords.y, node.Rotation);
223 break;
224 default:
225 UE_LOG(LogCarla, Warning, TEXT("Intersection type not implemented"));
226 }
227 }
228
229#undef ADD_INTERSECTION
230}
231
232//查找 road 类型的第一个组件。
233static bool LineTrace(
234 UWorld *World,
235 const FVector &Start,
236 const FVector &End,
237 FHitResult &HitResult)
238{
239 TArray <FHitResult> OutHits;
240 static FName TraceTag = FName(TEXT("RoadTrace"));
241 const bool Success = World->LineTraceMultiByObjectType(
242 OutHits,
243 Start,
244 End,
245 FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
246 FCollisionQueryParams(TraceTag, true));
247
248 if (Success) {
249 for (FHitResult &Item : OutHits) {
250 if (ATagger::MatchComponent(*Item.Component, crp::CityObjectLabel::Roads)) {
251 HitResult = Item;
252 return true;
253 }
254 }
255 }
256 return false;
257}
258
260{
261 UE_LOG(LogCarla, Log, TEXT("Generating road map..."));
262
263 auto World = GetWorld();
264 check(GetWorld() != nullptr);
265 check(RoadMap != nullptr);
266
267 ATagger::TagActorsInLevel(*GetWorld(), bTagForSemanticSegmentation); // We need the tags.
268
269 const float IntersectionSize = CityMapMeshTag::GetRoadIntersectionSize();
270 const uint32 Margin = IntersectionSize / 2u;
271 const float Offset = GetMapScale() * Margin;
272
273 const float CmPerPixel = GetMapScale() / static_cast<float>(PixelsPerMapUnit);
274
275 const uint32 SizeX = PixelsPerMapUnit * (MapSizeX + 2u * Margin);
276 const uint32 SizeY = PixelsPerMapUnit * (MapSizeY + 2u * Margin);
277
278 const FTransform &ActorTransform = GetActorTransform();
279
280 const FVector MapOffset(-Offset, -Offset, 0.0f);
281 RoadMap->Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset);
282
283 for (uint32 PixelY = 0u; PixelY < SizeY; ++PixelY) {
284 for (uint32 PixelX = 0u; PixelX < SizeX; ++PixelX) {
285 const float X = static_cast<float>(PixelX) * CmPerPixel - Offset;
286 const float Y = static_cast<float>(PixelY) * CmPerPixel - Offset;
287 const FVector Start = ActorTransform.TransformPosition(FVector(X, Y, 50.0f));
288 const FVector End = ActorTransform.TransformPosition(FVector(X, Y, -50.0f));
289
290 //执行光线追踪。
291 FHitResult Hit;
292 if (LineTrace(World, Start, End, Hit)) {
293 auto StaticMeshComponent = Cast<UStaticMeshComponent>(Hit.Component.Get());
294 if (StaticMeshComponent == nullptr) {
295 UE_LOG(LogCarla, Error, TEXT("Road component is not UInstancedStaticMeshComponent"));
296 } else {
298 PixelX,
299 PixelY,
300 GetTag(*StaticMeshComponent->GetStaticMesh()),
301 StaticMeshComponent->GetOwner()->GetTransform(),
303 }
304 }
305 }
306 }
307
308#if WITH_EDITOR
309 RoadMap->Log();
310#endif // WITH_EDITOR
311
312 if (bSaveRoadMapToDisk) {
313 RoadMap->SaveAsPNG(FPaths::ProjectSavedDir(), World->GetMapName());
314 }
315
316#if WITH_EDITOR
317 RoadMap->DrawDebugPixelsToLevel(GetWorld(), !bDrawDebugPixelsToLevel);
318#endif // WITH_EDITOR
319}
UE_LOG(LogCarla, Log, TEXT("UActorDispatcher::Destroying actor: '%s' %x"), *Id, Actor)
auto end() const noexcept
TSharedPtr< const FActorInfo > carla::rpc::ActorState UWorld * World
static bool LineTrace(UWorld *World, const FVector &Start, const FVector &End, FHitResult &HitResult)
#define ADD_INTERSECTION(tag, x, y, angle)
bool bGenerateRoadMapOnSave
道路地图在保存时会重新计算,所以我们总是存储最新版本。仅出于测试目的才取消勾选此选项,因为道路地图可能会与当前道路布局不同步。
bool bGenerateRoads
如果为假,则不添加网格,仅生成道路的内部表示形式。
uint32 MapSizeY
地图在地图单位下的Y尺寸。地图单位是基于道路的瓦片网格来计算的(详见地图比例相关说明)。
int32 Seed
生成的随机地图的种子。
uint32 PixelsPerMapUnit
道路地图中每地图单位对应的像素分辨率。
virtual void PreSave(const ITargetPlatform *TargetPlatform) override
virtual void UpdateMap() override
TUniquePtr< MapGen::DoublyConnectedEdgeList > Dcel
bool bDrawDebugPixelsToLevel
如果为真,会在关卡中为道路地图的每个像素绘制一个调试点。
bool bLeftHandTraffic
道路地图是否应基于靠左行驶交通规则来生成。
bool bUseFixedSeed
如果为假,则每次都会生成一个随机种子。
bool bTriggerRoadMapGeneration
触发生成当前布局的道路地图图像(用于越野和对向车道入侵检测)。
ACityMapGenerator(const FObjectInitializer &ObjectInitializer)
TUniquePtr< MapGen::GraphParser > DcelParser
uint32 MapSizeX
地图在地图单位下的X尺寸。地图单位是基于道路的瓦片网格来计算的(详见地图比例相关说明)。
bool bSaveRoadMapToDisk
如果为真,将编码为图像的道路地图保存到磁盘。图像会保存到项目的“Saved”文件夹中。
bool bTagForSemanticSegmentation
如果为真,激活关卡中每个被标记的演员的自定义深度通道。此通道对于渲染语义分割是必要的。然而,由于似乎不会对具有此值激活的对象应用遮挡,所以可能会增加性能开销。
void AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y)
float GetMapScale() const
ECityMapMeshTag GetTag(const UStaticMesh &StaticMesh) const
static bool MatchComponent(const UPrimitiveComponent &Component, crp::CityObjectLabel Tag)
如果 Component 已使用给定的 标记进行标记,则返回 true。
Definition Tagger.h:62
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
设置 level 中每个 actor 的标签。
Definition Tagger.cpp:229
static uint32 GetRoadIntersectionSize()
获取道路交叉口一侧的图块大小。即,返回N,使得NxN 大小相当于一个道路交叉口的部分
std::unordered_set< const HalfEdge * > Set
bool Insert(const HalfEdge &InHalfEdge)
简单的双连通边链表结构。它只允许添加元素,不允许删除元素。
static HalfEdge & GetPair(HalfEdge &halfEdge)
static TUniquePtr< DoublyConnectedEdgeList > Generate(uint32 SizeX, uint32 SizeY, int32 Seed)
创建一个大小为 SizeX 乘以 SizeY 的平方双连通边链表 DoublyConnectedEdgeList, 并使用固定的随机数生成种子 Seed 在内部生成随机连接。
关卡路线图。包含哪些区域是道路的 2D 信息 和车道方向。
Definition RoadMap.h:91
void Reset(uint32 Width, uint32 Height, float PixelsPerCentimeter, const FTransform &WorldToMap, const FVector &MapOffset)
重置当前映射 - 初始化给定大小的空映射。
Definition RoadMap.cpp:128
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
Definition RoadMap.cpp:150
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
将当前地图另存为 PNG,并将像素数据编码为颜色。
Definition RoadMap.cpp:332