CARLA
Loading...
Searching...
No Matches
GeometryImporter.cpp
Go to the documentation of this file.
2#include "Misc/FileHelper.h"
3#include "Misc/Paths.h"
4#include "Engine/Engine.h"
5#include "Engine/StaticMeshActor.h"
6#include "Modules/ModuleManager.h"
8#if WITH_EDITOR
9#include "AssetToolsModule.h"
10#include "AutomatedAssetImportData.h"
11#include "Factories/FbxFactory.h"
12#include "Editor.h"
13#endif
14
15DEFINE_LOG_CATEGORY(LogGeometryImporter);
16
17TArray<FVector2D> UGeometryImporter::ReadCSVCoordinates(FString Path, FVector2D OriginGeoCoordinates)
18{
19 UE_LOG(LogTemp, Warning, TEXT("Reading latlon coordinates"));
20
21 TArray<FVector2D> Coordinates;
22
23 FString FileContent;
24
25 if (FFileHelper::LoadFileToString(FileContent, *Path))
26 {
27 TArray<FString> Lines;
28 FileContent.ParseIntoArrayLines(Lines);
29
30 for (int32 i = 0; i < Lines.Num(); ++i)
31 {
32 FString Line = Lines[i];
33 TArray<FString> Columns;
34 Line.ParseIntoArray(Columns, TEXT(","), true);
35
36 if (Columns.Num() >= 2)
37 {
38 float X = FCString::Atof(*Columns[0]);
39 float Y = FCString::Atof(*Columns[1]);
40 FVector2D Pos = UMapGenFunctionLibrary::GetTransversemercProjection(Y, X, OriginGeoCoordinates.X, OriginGeoCoordinates.Y);
41 Coordinates.Add(Pos);
42 }
43 }
44 }
45 else
46 {
47 UE_LOG(LogTemp, Warning, TEXT("Failed to read file at: %s"), *Path);
48 }
49
50 return Coordinates;
51}
52
53USplineComponent *UGeometryImporter::CreateSpline(UWorld *World, const TArray<FVector> &Points, const FString SplineName)
54{
55
56 if (!World || Points.Num() < 2)
57 {
58 UE_LOG(LogTemp, Log, TEXT("Invalid world pointer"));
59 return nullptr;
60 }
61
62 AActor *SplineActor = World->SpawnActor<AActor>(AActor::StaticClass(), FTransform::Identity);
63 if (!SplineActor)
64 {
65 UE_LOG(LogGeometryImporter, Log, TEXT("Spline actor not created"));
66 return nullptr;
67 }
68#if WITH_EDITOR
69 SplineActor->SetActorLabel(SplineName);
70#endif
71 SplineActor->Tags.Add(FName("curb"));
72
73 USplineComponent *Spline = NewObject<USplineComponent>(SplineActor);
74 Spline->ClearSplinePoints();
75 Spline->RegisterComponent();
76 Spline->SetMobility(EComponentMobility::Static);
77 SplineActor->SetRootComponent(Spline);
78
79 for (int32 i = 0; i < Points.Num(); ++i)
80 {
81 Spline->AddSplinePoint(Points[i], ESplineCoordinateSpace::World);
82 }
83
84 for (int32 i = 0; i < Spline->GetNumberOfSplinePoints(); ++i)
85 {
86 Spline->SetSplinePointType(i, ESplinePointType::Curve, false);
87 }
88
89 Spline->UpdateSpline();
90
91 const float TangentScale = 0.25f; // 0 = no curve, 1 = full handle length = distance to neighbor
92 int32 Num = Spline->GetNumberOfSplinePoints();
93
94 for (int32 i = 0; i < Num; ++i)
95 {
96 FVector Current = Spline->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local);
97 FVector Prev = Spline->GetLocationAtSplinePoint(FMath::Max(i - 1, 0), ESplineCoordinateSpace::Local);
98 FVector Next = Spline->GetLocationAtSplinePoint(FMath::Min(i + 1, Num - 1), ESplineCoordinateSpace::Local);
99 auto D1 = FVector::Distance(Current, Prev);
100 auto D2 = FVector::Distance(Current, Next);
101 auto Clamped = FMath::Min(D1, D2);
102
103 // Direction toward next point
104 FVector Dir = (Next - Prev) * 0.5f;
105 float DesiredLength = FMath::Min(Dir.Size() * TangentScale, Clamped);
106 FVector Tangent = Dir.GetSafeNormal() * DesiredLength;
107
108 // Arrive tangent points backward toward Prev, leave tangent toward Next
109 Spline->SetTangentAtSplinePoint(i, Tangent, ESplineCoordinateSpace::Local, false);
110 }
111
112 Spline->UpdateSpline();
113
114 Spline->SetClosedLoop(true);
115 Spline->UpdateSpline();
116
117 return Spline;
118}
119
120TArray<USplineComponent *> UGeometryImporter::ImportGeoJsonPolygonsToSplines(UWorld *World, const FString &GeoJsonFilePath, const FVector2D OriginGeoCoordinates)
121{
122 UE_LOG(LogTemp, Log, TEXT("Importing geojson and creating splines from file: %s"), *GeoJsonFilePath);
123
124 TArray<USplineComponent *> CreatedSplines;
125
126 FString JsonString;
127 if (!FFileHelper::LoadFileToString(JsonString, *GeoJsonFilePath))
128 {
129 UE_LOG(LogTemp, Error, TEXT("Failed to load GeoJSON file: %s"), *GeoJsonFilePath);
130 return CreatedSplines;
131 }
132
133 TSharedPtr<FJsonObject> JsonParsed;
134 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
135
136 if (!FJsonSerializer::Deserialize(Reader, JsonParsed) || !JsonParsed.IsValid())
137 {
138 UE_LOG(LogTemp, Error, TEXT("Failed to parse GeoJSON file."));
139 return CreatedSplines;
140 }
141
142 const TArray<TSharedPtr<FJsonValue>> *Features;
143 if (!JsonParsed->TryGetArrayField(TEXT("features"), Features))
144 {
145 UE_LOG(LogTemp, Error, TEXT("No 'features' array found in GeoJSON."));
146 return CreatedSplines;
147 }
148
149 int i = 0;
150 for (const TSharedPtr<FJsonValue> &FeatureValue : *Features)
151 {
152 const TSharedPtr<FJsonObject> FeatureObj = FeatureValue->AsObject();
153 const TSharedPtr<FJsonObject> Geometry = FeatureObj->GetObjectField(TEXT("geometry"));
154
155 FString GeometryType = Geometry->GetStringField(TEXT("type"));
156 if (GeometryType != "Polygon")
157 continue;
158
159 const TArray<TSharedPtr<FJsonValue>> *Rings;
160 if (!Geometry->TryGetArrayField(TEXT("coordinates"), Rings) || Rings->Num() == 0)
161 continue;
162
163 const TArray<TSharedPtr<FJsonValue>> &OuterRing = (*Rings)[0]->AsArray();
164 TArray<FVector> Points;
165
166 for (const auto &Coord : OuterRing)
167 {
168 const TArray<TSharedPtr<FJsonValue>> &CoordArray = Coord->AsArray();
169 double Lon = CoordArray[0]->AsNumber();
170 double Lat = CoordArray[1]->AsNumber();
171 FVector2D Pos2D = UMapGenFunctionLibrary::GetTransversemercProjection(Lat, Lon, OriginGeoCoordinates.X, OriginGeoCoordinates.Y);
172 FVector Pos = FVector(Pos2D.X, Pos2D.Y, 0.0f); // Initialize height as 0
173 Points.Add(Pos);
174 }
175
176 // Remove last point if it's a duplicate of the first
177 if (Points.Num() > 1 && Points[0].Equals(Points.Last(), 0.01f))
178 {
179 Points.Pop();
180 }
181
182 FString SplineName = "Spline_" + FString::FromInt(i);
183 USplineComponent *Spline = CreateSpline(World, Points, SplineName);
184 if (Spline)
185 {
186 CreatedSplines.Add(Spline);
187 }
188
189 i++;
190 }
191
192 return CreatedSplines;
193}
194
196 UWorld *World,
197 const FString &JsonFilePath,
198 FVector2D Center,
199 FVector2D Extent,
200 FIntPoint RTExtent)
201{
202 TArray<USplineComponent *> CreatedSplines;
203
204 FString JsonString;
205 if (!FFileHelper::LoadFileToString(JsonString, *JsonFilePath))
206 {
207 UE_LOG(LogGeometryImporter, Error, TEXT("Failed to load JSON from: %s"), *JsonFilePath);
208 return CreatedSplines;
209 }
210
211 TSharedPtr<FJsonValue> RootValue;
212 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
213 if (!FJsonSerializer::Deserialize(Reader, RootValue) || !RootValue.IsValid())
214 {
215 UE_LOG(LogGeometryImporter, Error, TEXT("Failed to parse JSON"));
216 return CreatedSplines;
217 }
218
219 const TArray<TSharedPtr<FJsonValue>> *ContourArray;
220 if (!RootValue->TryGetArray(ContourArray))
221 return CreatedSplines;
222
223 float ZOffset = 0.0f;
224
225 for (int32 ContourIdx = 0; ContourIdx < ContourArray->Num(); ++ContourIdx)
226 {
227 const TArray<TSharedPtr<FJsonValue>> *PointList;
228 if (!(*ContourArray)[ContourIdx]->TryGetArray(PointList))
229 continue;
230
231 TArray<FVector> Points;
232
233 for (const TSharedPtr<FJsonValue> &PointVal : *PointList)
234 {
235 const TArray<TSharedPtr<FJsonValue>> *XY;
236 if (!(PointVal->TryGetArray(XY) && XY->Num() == 2))
237 continue;
238 double X = 0.0, Y = 0.0;
239 if (!((*XY)[0]->TryGetNumber(X) && (*XY)[1]->TryGetNumber(Y)))
240 continue;
241 auto UV = FVector2D(X, Y);
242 UV /= FVector2D(RTExtent);
243 UV.Y = 1.0F - UV.Y;
244 UV -= FVector2D(0.5F, 0.5F);
245 UV.X = -UV.X;
246 FVector2D P = Center + UV * Extent;
247 Points.Add(FVector(P.X, P.Y, ZOffset));
248 }
249
250 FString Name = FString::Printf(TEXT("Spline_%d"), ContourIdx);
251 USplineComponent *Spline = CreateSpline(World, Points, Name);
252 if (Spline)
253 {
254 CreatedSplines.Add(Spline);
255 }
256 }
257 return CreatedSplines;
258}
259
260void UGeometryImporter::ImportObj(const FString &ObjFilePath, UWorld *World, UMaterialInterface *Material)
261{
262#if WITH_EDITOR
263 if (!FPaths::FileExists(ObjFilePath))
264 {
265 UE_LOG(LogGeometryImporter, Error, TEXT("File does not exist: %s"), *ObjFilePath);
266 return;
267 }
268
269 // Get AssetTools
270 FAssetToolsModule &AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
271
272 // Setup import data
273 UFbxFactory *ObjFactory = NewObject<UFbxFactory>();
274 ObjFactory->AddToRoot(); // prevent GC
275
276 UAutomatedAssetImportData *ImportData = NewObject<UAutomatedAssetImportData>();
277 ImportData->Factory = ObjFactory;
278 ImportData->Filenames.Add(ObjFilePath);
279 ImportData->DestinationPath = TEXT("/Game/ImportedMeshes");
280 ImportData->bReplaceExisting = true;
281
282 // Perform the import
283 TArray<UObject *> ImportedAssets = AssetToolsModule.Get().ImportAssetsAutomated(ImportData);
284
285 if (ImportedAssets.Num() > 0)
286 {
287 if (UStaticMesh *ImportedMesh = Cast<UStaticMesh>(ImportedAssets[0]))
288 {
289 // Spawn the mesh in the world
290 FActorSpawnParameters SpawnParams;
291 FVector SpawnLocation(0, 0, 0);
292 AStaticMeshActor *MeshActor = World->SpawnActor<AStaticMeshActor>(SpawnLocation, FRotator::ZeroRotator, SpawnParams);
293
294 if (MeshActor)
295 {
296 MeshActor->GetStaticMeshComponent()->SetStaticMesh(ImportedMesh);
297 MeshActor->SetActorLabel(TEXT("UpdatedRoad"));
298
299 UStaticMeshComponent *MeshComp = MeshActor->GetStaticMeshComponent();
300 MeshComp->SetStaticMesh(ImportedMesh);
301
302 // Assign material
303 if (Material)
304 {
305 MeshComp->SetMaterial(0, Material);
306 }
307 else
308 {
309 UE_LOG(LogGeometryImporter, Warning, TEXT("Material not found."));
310 }
311
312 UE_LOG(LogGeometryImporter, Log, TEXT("Imported and spawned mesh: %s"), *ImportedMesh->GetName());
313 }
314 }
315 else
316 {
317 UE_LOG(LogGeometryImporter, Error, TEXT("Imported object is not a static mesh."));
318 }
319 }
320 else
321 {
322 UE_LOG(LogGeometryImporter, Error, TEXT("No assets imported from file: %s"), *ObjFilePath);
323 }
324#else
325 UE_LOG(LogGeometryImporter, Error, TEXT("ImportObj is editor-only; cannot import \"%s\"."), *ObjFilePath);
326#endif
327}
DEFINE_LOG_CATEGORY(LogGeometryImporter)
static TArray< USplineComponent * > CreateSplinesFromJson(UWorld *World, const FString &JsonFilePath, FVector2D Center, FVector2D Extent, FIntPoint RTExtent)
static USplineComponent * CreateSpline(UWorld *World, const TArray< FVector > &Points, const FString SplineName)
static TArray< USplineComponent * > ImportGeoJsonPolygonsToSplines(UWorld *World, const FString &GeoJsonFilePath, const FVector2D OriginGeoCoordinates)
static void ImportObj(const FString &ObjFilePath, UWorld *World, UMaterialInterface *Material)
TArray< FVector2D > ReadCSVCoordinates(FString Path, FVector2D OriginGeoCoordinates)
static FVector2D GetTransversemercProjection(float lat, float lon, float lat0, float lon0)