14#include "Components/InstancedStaticMeshComponent.h"
15#include "Engine/World.h"
19#include <unordered_set>
21#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
36 return Set.insert(&InHalfEdge).second &&
41 std::unordered_set<const HalfEdge *>
Set;
49 : Super(ObjectInitializer)
51 RoadMap = ObjectInitializer.CreateDefaultSubobject<
URoadMap>(
this, TEXT(
"RoadMap"));
65 FCoreUObjectDelegates::OnObjectSaved.Broadcast(
this);
66 if (!GIsCookerLoadingPackage) {
73 Super::PreSave(TargetPlatform);
100 FRandomStream randomStream;
101 randomStream.GenerateNewSeed();
102 Seed = randomStream.GetCurrentSeed();
111 UE_LOG(LogCarla, Warning, TEXT(
"Map size changed, was too small"));
113#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
119 TEXT(
"Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
121 Dcel->CountHalfEdges(),
124#ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
126 std::wstringstream sout;
127 sout <<
"\nGenerated " <<
DcelParser->CityAreaCount() <<
" city areas: ";
128 for (
auto i = 0u; i <
DcelParser->CityAreaCount(); ++i) {
130 auto &cityArea =
DcelParser->GetCityAreaAt(i);
131 for (
size_t j = 0u; j < cityArea.NodeCount(); ++j) {
132 sout << cityArea.GetNodeAt(j) <<
" ";
136 sout <<
"\nGenerated " <<
DcelParser->RoadSegmentCount() <<
" road segments: ";
137 for (
auto i = 0u; i <
DcelParser->RoadSegmentCount(); ++i) {
139 auto &roadSegment =
DcelParser->GetRoadSegmentAt(i);
140 for (
size_t j = 0u; j < roadSegment.Size(); ++j) {
141 sout << roadSegment[j] <<
" ";
145 UE_LOG(LogCarla, Log, TEXT(
"\n%s"), sout.str().c_str());
152 check(
Dcel !=
nullptr);
154 const Graph &graph = *
Dcel;
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();
166 if (source.x == target.x) {
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);
177 }
else if (source.y == target.y) {
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);
189 UE_LOG(LogCarla, Warning, TEXT(
"Diagonal edge ignored"));
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);
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);
219 ADD_INTERSECTION(ECityMapMeshTag::RoadTIntersection, coords.x, coords.y, node.Rotation);
222 ADD_INTERSECTION(ECityMapMeshTag::RoadXIntersection, coords.x, coords.y, node.Rotation);
225 UE_LOG(LogCarla, Warning, TEXT(
"Intersection type not implemented"));
229#undef ADD_INTERSECTION
235 const FVector &Start,
237 FHitResult &HitResult)
239 TArray <FHitResult> OutHits;
240 static FName TraceTag = FName(TEXT(
"RoadTrace"));
245 FCollisionObjectQueryParams(ECollisionChannel::ECC_WorldStatic),
246 FCollisionQueryParams(TraceTag,
true));
249 for (FHitResult &Item : OutHits) {
261 UE_LOG(LogCarla, Log, TEXT(
"Generating road map..."));
263 auto World = GetWorld();
264 check(GetWorld() !=
nullptr);
270 const uint32 Margin = IntersectionSize / 2u;
278 const FTransform &ActorTransform = GetActorTransform();
280 const FVector MapOffset(-Offset, -Offset, 0.0f);
281 RoadMap->
Reset(SizeX, SizeY, 1.0f / CmPerPixel, ActorTransform.Inverse(), MapOffset);
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));
293 auto StaticMeshComponent = Cast<UStaticMeshComponent>(Hit.Component.Get());
294 if (StaticMeshComponent ==
nullptr) {
295 UE_LOG(LogCarla, Error, TEXT(
"Road component is not UInstancedStaticMeshComponent"));
300 GetTag(*StaticMeshComponent->GetStaticMesh()),
301 StaticMeshComponent->GetOwner()->GetTransform(),
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尺寸。地图单位是基于道路的瓦片网格来计算的(详见地图比例相关说明)。
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。
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
设置 level 中每个 actor 的标签。
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 信息 和车道方向。
void Reset(uint32 Width, uint32 Height, float PixelsPerCentimeter, const FTransform &WorldToMap, const FVector &MapOffset)
重置当前映射 - 初始化给定大小的空映射。
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
将当前地图另存为 PNG,并将像素数据编码为颜色。