19 UE_LOG(LogTemp, Warning, TEXT(
"Reading latlon coordinates"));
21 TArray<FVector2D> Coordinates;
25 if (FFileHelper::LoadFileToString(FileContent, *Path))
27 TArray<FString> Lines;
28 FileContent.ParseIntoArrayLines(Lines);
30 for (int32 i = 0; i < Lines.Num(); ++i)
32 FString Line = Lines[i];
33 TArray<FString> Columns;
34 Line.ParseIntoArray(Columns, TEXT(
","),
true);
36 if (Columns.Num() >= 2)
38 float X = FCString::Atof(*Columns[0]);
39 float Y = FCString::Atof(*Columns[1]);
47 UE_LOG(LogTemp, Warning, TEXT(
"Failed to read file at: %s"), *Path);
56 if (!World || Points.Num() < 2)
58 UE_LOG(LogTemp, Log, TEXT(
"Invalid world pointer"));
62 AActor *SplineActor = World->SpawnActor<
AActor>(AActor::StaticClass(), FTransform::Identity);
65 UE_LOG(LogGeometryImporter, Log, TEXT(
"Spline actor not created"));
69 SplineActor->SetActorLabel(SplineName);
71 SplineActor->Tags.Add(FName(
"curb"));
73 USplineComponent *Spline = NewObject<USplineComponent>(SplineActor);
74 Spline->ClearSplinePoints();
75 Spline->RegisterComponent();
76 Spline->SetMobility(EComponentMobility::Static);
77 SplineActor->SetRootComponent(Spline);
79 for (int32 i = 0; i < Points.Num(); ++i)
81 Spline->AddSplinePoint(Points[i], ESplineCoordinateSpace::World);
84 for (int32 i = 0; i < Spline->GetNumberOfSplinePoints(); ++i)
86 Spline->SetSplinePointType(i, ESplinePointType::Curve,
false);
89 Spline->UpdateSpline();
91 const float TangentScale = 0.25f;
92 int32 Num = Spline->GetNumberOfSplinePoints();
94 for (int32 i = 0; i < Num; ++i)
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);
104 FVector Dir = (Next - Prev) * 0.5f;
105 float DesiredLength = FMath::Min(Dir.Size() * TangentScale, Clamped);
106 FVector Tangent = Dir.GetSafeNormal() * DesiredLength;
109 Spline->SetTangentAtSplinePoint(i, Tangent, ESplineCoordinateSpace::Local,
false);
112 Spline->UpdateSpline();
114 Spline->SetClosedLoop(
true);
115 Spline->UpdateSpline();
122 UE_LOG(LogTemp, Log, TEXT(
"Importing geojson and creating splines from file: %s"), *GeoJsonFilePath);
124 TArray<USplineComponent *> CreatedSplines;
127 if (!FFileHelper::LoadFileToString(JsonString, *GeoJsonFilePath))
129 UE_LOG(LogTemp, Error, TEXT(
"Failed to load GeoJSON file: %s"), *GeoJsonFilePath);
130 return CreatedSplines;
133 TSharedPtr<FJsonObject> JsonParsed;
134 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
136 if (!FJsonSerializer::Deserialize(Reader, JsonParsed) || !JsonParsed.IsValid())
138 UE_LOG(LogTemp, Error, TEXT(
"Failed to parse GeoJSON file."));
139 return CreatedSplines;
142 const TArray<TSharedPtr<FJsonValue>> *Features;
143 if (!JsonParsed->TryGetArrayField(TEXT(
"features"), Features))
145 UE_LOG(LogTemp, Error, TEXT(
"No 'features' array found in GeoJSON."));
146 return CreatedSplines;
150 for (
const TSharedPtr<FJsonValue> &FeatureValue : *Features)
152 const TSharedPtr<FJsonObject> FeatureObj = FeatureValue->AsObject();
153 const TSharedPtr<FJsonObject> Geometry = FeatureObj->GetObjectField(TEXT(
"geometry"));
155 FString GeometryType = Geometry->GetStringField(TEXT(
"type"));
156 if (GeometryType !=
"Polygon")
159 const TArray<TSharedPtr<FJsonValue>> *Rings;
160 if (!Geometry->TryGetArrayField(TEXT(
"coordinates"), Rings) || Rings->Num() == 0)
163 const TArray<TSharedPtr<FJsonValue>> &OuterRing = (*Rings)[0]->AsArray();
164 TArray<FVector> Points;
166 for (
const auto &Coord : OuterRing)
168 const TArray<TSharedPtr<FJsonValue>> &CoordArray = Coord->AsArray();
169 double Lon = CoordArray[0]->AsNumber();
170 double Lat = CoordArray[1]->AsNumber();
172 FVector Pos = FVector(Pos2D.X, Pos2D.Y, 0.0f);
177 if (Points.Num() > 1 && Points[0].Equals(Points.Last(), 0.01f))
182 FString SplineName =
"Spline_" + FString::FromInt(i);
183 USplineComponent *Spline =
CreateSpline(World, Points, SplineName);
186 CreatedSplines.Add(Spline);
192 return CreatedSplines;
197 const FString &JsonFilePath,
202 TArray<USplineComponent *> CreatedSplines;
205 if (!FFileHelper::LoadFileToString(JsonString, *JsonFilePath))
207 UE_LOG(LogGeometryImporter, Error, TEXT(
"Failed to load JSON from: %s"), *JsonFilePath);
208 return CreatedSplines;
211 TSharedPtr<FJsonValue> RootValue;
212 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
213 if (!FJsonSerializer::Deserialize(Reader, RootValue) || !RootValue.IsValid())
215 UE_LOG(LogGeometryImporter, Error, TEXT(
"Failed to parse JSON"));
216 return CreatedSplines;
219 const TArray<TSharedPtr<FJsonValue>> *ContourArray;
220 if (!RootValue->TryGetArray(ContourArray))
221 return CreatedSplines;
223 float ZOffset = 0.0f;
225 for (int32 ContourIdx = 0; ContourIdx < ContourArray->Num(); ++ContourIdx)
227 const TArray<TSharedPtr<FJsonValue>> *PointList;
228 if (!(*ContourArray)[ContourIdx]->TryGetArray(PointList))
231 TArray<FVector> Points;
233 for (
const TSharedPtr<FJsonValue> &PointVal : *PointList)
235 const TArray<TSharedPtr<FJsonValue>> *XY;
236 if (!(PointVal->TryGetArray(XY) && XY->Num() == 2))
238 double X = 0.0, Y = 0.0;
239 if (!((*XY)[0]->TryGetNumber(X) && (*XY)[1]->TryGetNumber(Y)))
241 auto UV = FVector2D(X, Y);
242 UV /= FVector2D(RTExtent);
244 UV -= FVector2D(0.5F, 0.5F);
246 FVector2D P = Center + UV * Extent;
247 Points.Add(FVector(P.X, P.Y, ZOffset));
250 FString Name = FString::Printf(TEXT(
"Spline_%d"), ContourIdx);
251 USplineComponent *Spline =
CreateSpline(World, Points, Name);
254 CreatedSplines.Add(Spline);
257 return CreatedSplines;
263 if (!FPaths::FileExists(ObjFilePath))
265 UE_LOG(LogGeometryImporter, Error, TEXT(
"File does not exist: %s"), *ObjFilePath);
270 FAssetToolsModule &AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(
"AssetTools");
273 UFbxFactory *ObjFactory = NewObject<UFbxFactory>();
274 ObjFactory->AddToRoot();
276 UAutomatedAssetImportData *ImportData = NewObject<UAutomatedAssetImportData>();
277 ImportData->Factory = ObjFactory;
278 ImportData->Filenames.Add(ObjFilePath);
279 ImportData->DestinationPath = TEXT(
"/Game/ImportedMeshes");
280 ImportData->bReplaceExisting =
true;
283 TArray<UObject *> ImportedAssets = AssetToolsModule.Get().ImportAssetsAutomated(ImportData);
285 if (ImportedAssets.Num() > 0)
287 if (UStaticMesh *ImportedMesh = Cast<UStaticMesh>(ImportedAssets[0]))
290 FActorSpawnParameters SpawnParams;
291 FVector SpawnLocation(0, 0, 0);
292 AStaticMeshActor *MeshActor = World->SpawnActor<AStaticMeshActor>(SpawnLocation, FRotator::ZeroRotator, SpawnParams);
296 MeshActor->GetStaticMeshComponent()->SetStaticMesh(ImportedMesh);
297 MeshActor->SetActorLabel(TEXT(
"UpdatedRoad"));
299 UStaticMeshComponent *MeshComp = MeshActor->GetStaticMeshComponent();
300 MeshComp->SetStaticMesh(ImportedMesh);
305 MeshComp->SetMaterial(0, Material);
309 UE_LOG(LogGeometryImporter, Warning, TEXT(
"Material not found."));
312 UE_LOG(LogGeometryImporter, Log, TEXT(
"Imported and spawned mesh: %s"), *ImportedMesh->GetName());
317 UE_LOG(LogGeometryImporter, Error, TEXT(
"Imported object is not a static mesh."));
322 UE_LOG(LogGeometryImporter, Error, TEXT(
"No assets imported from file: %s"), *ObjFilePath);
325 UE_LOG(LogGeometryImporter, Error, TEXT(
"ImportObj is editor-only; cannot import \"%s\"."), *ObjFilePath);