12 #include "FileHelper.h" 13 #include "HighResScreenshot.h" 16 #include "DrawDebugHelpers.h" 19 #include <type_traits> 21 #define LOCTEXT_NAMESPACE "CarlaRoadMap" 29 return FMath::Clamp(FMath::FloorToInt(Value), Min, Max);
36 const FVector2D SphericalCoords = Direction.UnitCartesianToSpherical();
37 return SphericalCoords.Y +
PI;
55 return FColor(0u, 0u, 0u, 255u);
57 return FColor(255u, 255u, 255u, 255u);
59 auto ToColor = [](
float X){
60 return FMath::FloorToInt(256.0 * (X +
PI) / (2.0f *
PI)) % 256;
63 return FColor(0u, 255u, ToColor(Azimuth), 255u);
72 Super(ObjectInitializer),
73 PixelsPerCentimeter(1.0f),
80 "Declaration map of FRoadMapPixelData's value does not match current serialization type");
85 const uint32 inHeight,
86 const float inPixelsPerCentimeter,
87 const FTransform &inWorldToMap,
88 const FVector &inMapOffset)
103 const bool bInvertDirection)
105 bool bIsRoad =
false;
106 bool bHasDirection =
false;
107 FVector Direction(0.0f, 0.0f, 0.0f);
109 auto Rotator = Transform.GetRotation().Rotator();
115 case ECityMapMeshTag::RoadTwoLanes_LaneRight:
116 case ECityMapMeshTag::Road90DegTurn_Lane1:
117 case ECityMapMeshTag::RoadTIntersection_Lane1:
118 case ECityMapMeshTag::RoadTIntersection_Lane9:
119 case ECityMapMeshTag::RoadXIntersection_Lane1:
120 case ECityMapMeshTag::RoadXIntersection_Lane9:
122 bHasDirection =
true;
123 Rotator.Yaw += 180.0f;
125 case ECityMapMeshTag::RoadTwoLanes_LaneLeft:
126 case ECityMapMeshTag::Road90DegTurn_Lane0:
127 case ECityMapMeshTag::RoadTIntersection_Lane0:
128 case ECityMapMeshTag::RoadTIntersection_Lane2:
129 case ECityMapMeshTag::RoadTIntersection_Lane5:
130 case ECityMapMeshTag::RoadTIntersection_Lane8:
131 case ECityMapMeshTag::RoadXIntersection_Lane0:
132 case ECityMapMeshTag::RoadXIntersection_Lane8:
134 bHasDirection =
true;
136 case ECityMapMeshTag::Road90DegTurn_Lane9:
137 case ECityMapMeshTag::RoadTIntersection_Lane7:
138 case ECityMapMeshTag::RoadXIntersection_Lane7:
139 case ECityMapMeshTag::RoadXIntersection_Lane5:
141 bHasDirection =
true;
142 Rotator.Yaw += 90.0f;
144 case ECityMapMeshTag::Road90DegTurn_Lane7:
146 bHasDirection =
true;
147 Rotator.Yaw += 90.0f;
149 case ECityMapMeshTag::Road90DegTurn_Lane5:
151 bHasDirection =
true;
152 Rotator.Yaw += 90.0f + 35.0f;
154 case ECityMapMeshTag::Road90DegTurn_Lane3:
156 bHasDirection =
true;
157 Rotator.Yaw += 90.0f + 45.0f + 20.5f;
159 case ECityMapMeshTag::Road90DegTurn_Lane8:
160 case ECityMapMeshTag::RoadTIntersection_Lane4:
161 case ECityMapMeshTag::RoadXIntersection_Lane2:
162 case ECityMapMeshTag::RoadXIntersection_Lane4:
164 bHasDirection =
true;
165 Rotator.Yaw += 270.0f;
167 case ECityMapMeshTag::Road90DegTurn_Lane6:
169 bHasDirection =
true;
170 Rotator.Yaw += 270.0f + 50.0f;
172 case ECityMapMeshTag::Road90DegTurn_Lane4:
174 bHasDirection =
true;
175 Rotator.Yaw += 270.0f + 80.0f;
177 case ECityMapMeshTag::Road90DegTurn_Lane2:
179 bHasDirection =
true;
182 case ECityMapMeshTag::RoadTIntersection_Lane3:
183 case ECityMapMeshTag::RoadTIntersection_Lane6:
184 case ECityMapMeshTag::RoadXIntersection_Lane3:
185 case ECityMapMeshTag::RoadXIntersection_Lane6:
187 bHasDirection =
false;
192 Direction = Rotation.GetForwardVector();
193 if (bInvertDirection) {
203 const FVector RelativePosition(
220 const FTransform &BoxTransform,
221 const FVector &BoxExtent,
222 float ChecksPerCentimeter)
const 224 auto DirectionOfMovement = BoxTransform.GetRotation().GetForwardVector();
225 DirectionOfMovement.Z = 0.0f;
226 uint32 CheckCount = 0u;
228 const float Step = 1.0f / ChecksPerCentimeter;
229 for (
float X = -BoxExtent.X; X < BoxExtent.X; X += Step) {
230 for (
float Y = -BoxExtent.Y; Y < BoxExtent.Y; Y += Step) {
232 auto Location = BoxTransform.TransformPosition(FVector(X, Y, 0.0f));
234 if (!Data.IsRoad()) {
236 }
else if (Data.HasDirection() &&
237 0.0f > FVector::DotProduct(Data.GetDirection(), DirectionOfMovement)) {
242 if (CheckCount > 0u) {
243 Result.
OffRoad /=
static_cast<float>(CheckCount);
246 UE_LOG(LogCarla, Warning, TEXT(
"URoadMap::Intersect did zero checks"));
254 UE_LOG(LogCarla, Error, TEXT(
"Cannot save invalid road map to disk"));
258 const FString ImagePath = FPaths::Combine(Folder, MapName + TEXT(
".png"));
259 const FString MetadataPath = FPaths::Combine(Folder, MapName + TEXT(
".txt"));
262 TUniquePtr<TImagePixelData<FColor>> PixelData = MakeUnique<TImagePixelData<FColor>>(DestSize);
270 FFormatNamedArguments Args;
271 Args.Add(
"MapName", FText::FromString(MapName));
275 Args.Add(
"Transform", FText::FromString(
WorldToMap.ToString()));
276 Args.Add(
"Offset", FText::FromString(
MapOffset.ToString()));
277 const auto Contents = FText::Format(
278 LOCTEXT(
"RoadMapMetadata",
279 "Map name = {MapName}\n" 280 "Size = {Width}x{Height} pixels\n" 281 "Density = {CmPerPixel} cm/pixel\n" 282 "World-To-Map Transform (T|R|S) = ({Transform})\n" 283 "Map Offset = ({Offset})\n"),
285 if (!FFileHelper::SaveStringToFile(Contents.ToString(), *MetadataPath)) {
286 UE_LOG(LogCarla, Error, TEXT(
"Failed to save map metadata"));
289 UE_LOG(LogCarla, Log, TEXT(
"Saved road map to \"%s\""), *ImagePath);
295 void URoadMap::Log()
const 297 const float MapSizeInMB =
303 TEXT(
"Generated road map %dx%d (%.2fMB) with %.2f cm/pixel"),
310 UE_LOG(LogCarla, Error, TEXT(
"Error generating road map"));
315 void URoadMap::DrawDebugPixelsToLevel(UWorld *World,
const bool bJustFlushDoNotDraw)
const 317 const FVector ZOffset(0.0f, 0.0f, 50.0f);
318 FlushPersistentDebugLines(World);
319 if (!bJustFlushDoNotDraw) {
320 for (
auto X = 0u; X <
Width; ++X) {
321 for (
auto Y = 0u; Y <
Height; ++Y) {
324 auto Color = Data.EncodeAsColor();
325 if (Data.HasDirection()) {
326 const FVector ArrowEnd =
Location + 50.0f * Data.GetDirection();
327 DrawDebugDirectionalArrow(World,
Location, ArrowEnd, 60.0f,
Color,
true);
336 #endif // WITH_EDITOR 338 #undef LOCTEXT_NAMESPACE TArray< uint16 > RoadMapData
bool IsRoad() const
Whether this pixel lies in-road.
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.
FVector GetWorldLocation(uint32 PixelX, uint32 PixelY) const
Return the world location of a given pixel.
void SetPixelAt(uint32 PixelX, uint32 PixelY, ECityMapMeshTag Tag, const FTransform &Transform, bool bInvertDirection=false)
sensor::data::Color Color
uint32 Height
Height of the map in pixels.
Road map intersection result. See URoadMap.
ECityMapMeshTag
Tag to identify the meshes used by the ProceduralMapGenerator.
URoadMap(const FObjectInitializer &ObjectInitializer)
Creates a valid empty map (every point is off-road).
FRoadMapPixelData GetDataAt(uint32 PixelX, uint32 PixelY) const
Retrieve the data stored at a given pixel.
Data stored in a road map pixel. See URoadMap.
static constexpr int IsRoadRow
static uint16 Encode(bool IsRoad, bool HasDirection, const FVector &Direction)
static float GetRotatedAzimuthAngle(const FVector &Direction)
static constexpr uint16 MaximumEncodedAngle
FRoadMapIntersectionResult Intersect(const FTransform &BoxTransform, const FVector &BoxExtent, float ChecksPerCentimeter) const
Intersect actor bounds with map.
static uint32 ClampFloatToUInt(const float Value, int32 Min, int32 Max)
static constexpr int HasDirectionRow
FTransform WorldToMap
World-to-map transform.
FColor EncodeAsColor() const
float GetDirectionAzimuthalAngle() const
Get the azimuth angle [-PI, PI] of the road direction (in spherical coordinates) at this pixel...
float OppositeLane
Percentage of the box invading opposite lane (wrong direction).
uint32 Width
Width of the map in pixels.
bool SaveAsPNG(const FString &Folder, const FString &MapName) const
Save the current map as PNG with the pixel data encoded as color.
int32 GetIndex(uint32 PixelX, uint32 PixelY) const
FVector MapOffset
Offset of the map in map coordinates.
static TFuture< bool > SavePixelsToDisk(UTextureRenderTarget2D &RenderTarget, const FString &FilePath)
Asynchronously save the pixels in RenderTarget to disk.
geom::Transform Transform
float PixelsPerCentimeter
Number of pixels per centimeter.
float OffRoad
Percentage of the box lying off-road.
bool HasDirection() const
Whether this pixel has a direction defined (e.g.