CARLA
Loading...
Searching...
No Matches
MapGenFunctionLibrary.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 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
8
10
11#include <util/ue-header-guard-begin.h>
12#include "AssetRegistry/AssetRegistryModule.h"
13#include "Materials/MaterialInstance.h"
14#include "StaticMeshAttributes.h"
15#include "RenderingThread.h"
16#include "PhysicsEngine/BodySetup.h"
17#include "Engine/StaticMeshActor.h"
18#include "UObject/SavePackage.h"
19#if WITH_EDITOR
20#include "Editor/EditorEngine.h"
21#include "Editor/Transactor.h"
22#endif
23#include "Components/HierarchicalInstancedStaticMeshComponent.h"
24#include <util/ue-header-guard-end.h>
25
26DEFINE_LOG_CATEGORY(LogCarlaMapGenFunctionLibrary);
27static const float OSMToCentimetersScaleFactor = 100.0f;
28
30 const FProceduralCustomMesh& Data,
31 const TArray<FProcMeshTangent>& ParamTangents,
32 UMaterialInstance* MaterialInstance )
33{
34
35 int32 VertexCount = Data.Vertices.Num();
36 int32 VertexInstanceCount = Data.Triangles.Num();
37 int32 PolygonCount = Data.Vertices.Num()/3;
38
39 FMeshDescription MeshDescription;
40 FStaticMeshAttributes AttributeGetter(MeshDescription);
41 AttributeGetter.Register();
42
43 auto PolygonGroupNames = AttributeGetter.GetPolygonGroupMaterialSlotNames();
44 auto VertexPositions = AttributeGetter.GetVertexPositions();
45 auto Tangents = AttributeGetter.GetVertexInstanceTangents();
46 auto BinormalSigns = AttributeGetter.GetVertexInstanceBinormalSigns();
47 auto Normals = AttributeGetter.GetVertexInstanceNormals();
48 auto Colors = AttributeGetter.GetVertexInstanceColors();
49 auto UVs = AttributeGetter.GetVertexInstanceUVs();
50
51 // Calculate the totals for each ProcMesh element type
52 FPolygonGroupID PolygonGroupForSection;
53 MeshDescription.ReserveNewVertices(VertexCount);
54 MeshDescription.ReserveNewVertexInstances(VertexInstanceCount);
55 MeshDescription.ReserveNewPolygons(PolygonCount);
56 MeshDescription.ReserveNewEdges(PolygonCount * 2);
57 UVs.SetNumChannels(4);
58
59 // Create Materials
60 TMap<UMaterialInterface*, FPolygonGroupID> UniqueMaterials;
61 const int32 NumSections = 1;
62 UniqueMaterials.Reserve(1);
63 FPolygonGroupID NewPolygonGroup = MeshDescription.CreatePolygonGroup();
64
65 if( MaterialInstance != nullptr ){
66 UMaterialInterface *Material = MaterialInstance;
67 UniqueMaterials.Add(Material, NewPolygonGroup);
68 PolygonGroupNames[NewPolygonGroup] = Material->GetFName();
69 }else{
70 UE_LOG(LogCarla, Error, TEXT("MaterialInstance is nullptr"));
71 }
72 PolygonGroupForSection = NewPolygonGroup;
73
74
75
76 // Create the vertex
77 int32 NumVertex = Data.Vertices.Num();
78 TMap<int32, FVertexID> VertexIndexToVertexID;
79 VertexIndexToVertexID.Reserve(NumVertex);
80 for (int32 VertexIndex = 0; VertexIndex < NumVertex; ++VertexIndex)
81 {
82 auto &Vert = Data.Vertices[VertexIndex];
83 auto VertexID = MeshDescription.CreateVertex();
84 VertexPositions[VertexID] = FVector3f(Vert);
85 VertexIndexToVertexID.Add(VertexIndex, VertexID);
86 }
87
88 // Create the VertexInstance
89 int32 NumIndices = Data.Triangles.Num();
90 int32 NumTri = NumIndices / 3;
91 TMap<int32, FVertexInstanceID> IndiceIndexToVertexInstanceID;
92 IndiceIndexToVertexInstanceID.Reserve(NumVertex);
93 for (int32 IndiceIndex = 0; IndiceIndex < NumIndices; IndiceIndex++)
94 {
95 const int32 VertexIndex = Data.Triangles[IndiceIndex];
96 const FVertexID VertexID = VertexIndexToVertexID[VertexIndex];
97 const FVertexInstanceID VertexInstanceID =
98 MeshDescription.CreateVertexInstance(VertexID);
99 IndiceIndexToVertexInstanceID.Add(IndiceIndex, VertexInstanceID);
100 Normals[VertexInstanceID] = FVector3f(Data.Normals[VertexIndex]);
101
102 if(ParamTangents.Num() == Data.Vertices.Num())
103 {
104 Tangents[VertexInstanceID] = FVector3f(ParamTangents[VertexIndex].TangentX);
105 BinormalSigns[VertexInstanceID] =
106 ParamTangents[VertexIndex].bFlipTangentY ? -1.f : 1.f;
107 }else{
108
109 }
110 Colors[VertexInstanceID] = FLinearColor(0,0,0);
111 if(Data.UV0.Num() == Data.Vertices.Num())
112 {
113 UVs.Set(VertexInstanceID, 0, FVector2f(Data.UV0[VertexIndex]));
114 }else{
115 UVs.Set(VertexInstanceID, 0, FVector2f(0,0));
116 }
117 UVs.Set(VertexInstanceID, 1, FVector2f(0,0));
118 UVs.Set(VertexInstanceID, 2, FVector2f(0,0));
119 UVs.Set(VertexInstanceID, 3, FVector2f(0,0));
120 }
121
122 for (int32 TriIdx = 0; TriIdx < NumTri; TriIdx++)
123 {
124 FVertexID VertexIndexes[3];
125 TArray<FVertexInstanceID> VertexInstanceIDs;
126 VertexInstanceIDs.SetNum(3);
127
128 for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
129 {
130 const int32 IndiceIndex = (TriIdx * 3) + CornerIndex;
131 const int32 VertexIndex = Data.Triangles[IndiceIndex];
132 VertexIndexes[CornerIndex] = VertexIndexToVertexID[VertexIndex];
133 VertexInstanceIDs[CornerIndex] =
134 IndiceIndexToVertexInstanceID[IndiceIndex];
135 }
136
137 // Insert a polygon into the mesh
138 MeshDescription.CreatePolygon(NewPolygonGroup, VertexInstanceIDs);
139
140 }
141
142 return MeshDescription;
143}
144
146 const FProceduralCustomMesh& Data,
147 const TArray<FProcMeshTangent>& ParamTangents,
148 UMaterialInstance* MaterialInstance,
149 FString MapName,
150 FString FolderName,
151 FName MeshName)
152{
153 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
154
155 UStaticMesh::FBuildMeshDescriptionsParams Params;
156 Params.bBuildSimpleCollision = true;
157
158 FString PackageName = "/Game/CustomMaps/" + MapName + "/Static/" + FolderName + "/" + MeshName.ToString();
159
160 if (!PlatformFile.DirectoryExists(*PackageName))
161 {
162 //PlatformFile.CreateDirectory(*PackageName);
163 }
164
165
166 FMeshDescription Description = BuildMeshDescriptionFromData(Data,ParamTangents, MaterialInstance);
167
168 if (Description.Polygons().Num() > 0)
169 {
170 UPackage* Package = CreatePackage(*PackageName);
171 check(Package);
172 UStaticMesh* Mesh = NewObject<UStaticMesh>( Package, MeshName, RF_Public | RF_Standalone);
173
174 Mesh->InitResources();
175 Mesh->SetLightingGuid(FGuid::NewGuid());
176 Mesh->GetStaticMaterials().Add(FStaticMaterial(MaterialInstance));
177 Mesh->BuildFromMeshDescriptions({ &Description }, Params);
178 Mesh->CreateBodySetup();
179 Mesh->GetBodySetup()->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple;
180 Mesh->GetBodySetup()->CreatePhysicsMeshes();
181 // Build mesh from source
182 Mesh->NeverStream = false;
183 TArray<UObject*> CreatedAssets;
184 CreatedAssets.Add(Mesh);
185
186 // Notify asset registry of new asset
187 FAssetRegistryModule::AssetCreated(Mesh);
188
189 /*
190
191 FSavePackageArgs SaveArgs;
192 SaveArgs.TopLevelFlags =
193 EObjectFlags::RF_Public |
194 EObjectFlags::RF_Standalone;
195 SaveArgs.Error = GError;
196 SaveArgs.bForceByteSwapping = true;
197 SaveArgs.bWarnOfLongFilename = true;
198 SaveArgs.SaveFlags = SAVE_NoError;
199
200 UPackage::SavePackage(
201 Package,
202 Mesh,
203 *(MeshName.ToString()),
204 SaveArgs);
205
206 */
207
208 Package->MarkPackageDirty();
209 return Mesh;
210 }
211 return nullptr;
212}
213
214FVector2D UMapGenFunctionLibrary::GetTransversemercProjection(float lat, float lon, float lat0, float lon0)
215{
216 // earth radius in m
217 const float R = 6373000.0f;
218 float latt = FMath::DegreesToRadians(lat);
219 float lonn = FMath::DegreesToRadians(lon - lon0);
220 float latt0 = FMath::DegreesToRadians(lat0);
221 float eps = atan(tan(latt)/cos(lonn));
222 float nab = asinh(sin(lonn)/sqrt(tan(latt)*tan(latt)+cos(lonn)*cos(lonn)));
223 float x = R*nab;
224 float y = R*eps;
225 float eps0 = atan(tan(latt0)/cos(0));
226 float nab0 = asinh(sin(0)/sqrt(tan(latt0)*tan(latt0)+cos(0)*cos(0)));
227 float x0 = R*nab0;
228 float y0 = R*eps0;
229 FVector2D Result = FVector2D(x, -(y - y0)) * OSMToCentimetersScaleFactor;
230
231 return Result;
232}
233
235{
236 //FGenericPlatformProcess::Sleep(seconds);
237}
238
240{
241 FlushRenderingCommands();
242}
243
244#if WITH_EDITOR
245extern UNREALED_API UEditorEngine* GEditor;
246#endif
247
249#if WITH_EDITOR
250 GEngine->PerformGarbageCollectionAndCleanupActors();
251 FText TransResetText(FText::FromString("Clean up after Move actors to sublevels"));
252 if ( GEditor->Trans )
253 {
254 GEditor->Trans->Reset(TransResetText);
255 GEditor->Cleanse(true, true, TransResetText);
256 }
257#endif
258}
259
260TArray<AInstancedStaticMeshActor*> UMapGenFunctionLibrary::ChangeStaticMeshesInTheLevelForInstancedStaticMeshes(UWorld* World, TArray<UStaticMesh*> Filter, int MinNumOfInstancesToBeChanged)
261{
262 TArray<AInstancedStaticMeshActor*> Result;
263 TArray<AActor*> FoundActors;
264 UGameplayStatics::GetAllActorsOfClass(World, AStaticMeshActor::StaticClass(), FoundActors);
265 TMap<UStaticMesh*, TArray<AStaticMeshActor*>> ActorsToCheckIfReplacedMap;
266 for(AActor* CurrentActor : FoundActors )
267 {
268 AStaticMeshActor* CurrentStaticMeshActor = Cast<AStaticMeshActor>(CurrentActor);
269 UStaticMeshComponent* CurrentStaticMeshComponent = CurrentStaticMeshActor->GetStaticMeshComponent();
270 UStaticMesh* CurrentStaticMesh = CurrentStaticMeshComponent->GetStaticMesh();
271 if( Filter.IsEmpty() || Filter.Contains(CurrentStaticMesh) )
272 {
273 TArray<AStaticMeshActor*>& Instances = ActorsToCheckIfReplacedMap.FindOrAdd(CurrentStaticMesh);
274 Instances.Add(CurrentStaticMeshActor);
275 }
276 }
277
278 for(auto CurrentPair : ActorsToCheckIfReplacedMap)
279 {
280
281 if(CurrentPair.Value.Num() > MinNumOfInstancesToBeChanged)
282 {
283 TArray<FTransform> TransformsToBeInstanced;
284 for( AStaticMeshActor* SMActor : CurrentPair.Value )
285 {
286 TransformsToBeInstanced.Add(SMActor->GetActorTransform());
287 SMActor->Destroy();
288 }
289
290 AInstancedStaticMeshActor* InstancedStaticMeshActor = World->SpawnActor<AInstancedStaticMeshActor>();
291 if(InstancedStaticMeshActor)
292 {
293 InstancedStaticMeshActor->GetInstancedStaticMeshComponent()->SetStaticMesh(CurrentPair.Key);
294 InstancedStaticMeshActor->GetInstancedStaticMeshComponent()->AddInstances(TransformsToBeInstanced, false, true);
295 Result.Add(InstancedStaticMeshActor);
296 }
297 }
298 }
299
300 return Result;
301}
302
303TArray<AStaticMeshActor*> UMapGenFunctionLibrary::RevertStaticMeshesInTheLevelForInstancedStaticMeshes(UWorld* World, TArray<UStaticMesh*> Filter)
304{
305 TArray<AActor*> FoundActors;
306 TArray<AStaticMeshActor*> Result;
307 UGameplayStatics::GetAllActorsOfClass(World, AInstancedStaticMeshActor::StaticClass(), FoundActors);
308 for(AActor* CurrentActor : FoundActors )
309 {
310 AInstancedStaticMeshActor* InstancedStaticMeshActor = Cast<AInstancedStaticMeshActor>(CurrentActor);
311 if(InstancedStaticMeshActor)
312 {
313 UStaticMesh* CurrentStaticMesh = InstancedStaticMeshActor->GetInstancedStaticMeshComponent()->GetStaticMesh();
314 if( Filter.IsEmpty() || Filter.Contains(CurrentStaticMesh))
315 {
316 const TArray<FInstancedStaticMeshInstanceData>& Instances = InstancedStaticMeshActor->GetInstancedStaticMeshComponent()->PerInstanceSMData;
317 for( FInstancedStaticMeshInstanceData CurrentInstanceData : Instances )
318 {
319 AStaticMeshActor* StaticMeshActor = World->SpawnActor<AStaticMeshActor>();
320 StaticMeshActor->GetStaticMeshComponent()->SetStaticMesh(CurrentStaticMesh);
321 StaticMeshActor->SetActorTransform(FTransform(CurrentInstanceData.Transform));
322 Result.Add(StaticMeshActor);
323 }
324 InstancedStaticMeshActor->Destroy();
325 }
326 }
327 }
328
329 return Result;
330}
331
332// Ported from carla-digitaltwins' CarlaMeshGeneration (MIT) for the building generator.
334{
335 if (!TargetActor)
336 {
337 UE_LOG(LogCarlaMapGenFunctionLibrary, Warning, TEXT("Invalid TargetActor in AddInstancedStaticMeshComponentToActor"));
338 return nullptr;
339 }
340 if (!TargetActor->GetRootComponent())
341 {
342 USceneComponent* NewRoot = NewObject<USceneComponent>(TargetActor, TEXT("GeneratedRootComponent"));
343 TargetActor->SetRootComponent(NewRoot);
344 NewRoot->RegisterComponent();
345 }
346 UInstancedStaticMeshComponent* ISMComponent = NewObject<UInstancedStaticMeshComponent>(TargetActor);
347 if (!ISMComponent)
348 {
349 UE_LOG(LogCarlaMapGenFunctionLibrary, Error, TEXT("Could not create UInstancedStaticMeshComponent"));
350 return nullptr;
351 }
352 ISMComponent->SetupAttachment(TargetActor->GetRootComponent());
353 ISMComponent->RegisterComponent();
354 TargetActor->AddInstanceComponent(ISMComponent);
355 return ISMComponent;
356}
357
359{
360 if (!TargetActor)
361 {
362 UE_LOG(LogCarlaMapGenFunctionLibrary, Warning, TEXT("Invalid TargetActor in AddStaticMeshComponentToActor"));
363 return nullptr;
364 }
365 if (!TargetActor->GetRootComponent())
366 {
367 USceneComponent* NewRoot = NewObject<USceneComponent>(TargetActor, TEXT("GeneratedRootComponent"));
368 TargetActor->SetRootComponent(NewRoot);
369 NewRoot->RegisterComponent();
370 }
371 UStaticMeshComponent* SMComponent = NewObject<UStaticMeshComponent>(TargetActor);
372 if (!SMComponent)
373 {
374 UE_LOG(LogCarlaMapGenFunctionLibrary, Error, TEXT("Could not create UStaticMeshComponent"));
375 return nullptr;
376 }
377 SMComponent->SetupAttachment(TargetActor->GetRootComponent());
378 SMComponent->RegisterComponent();
379 TargetActor->AddInstanceComponent(SMComponent);
380 return SMComponent;
381}
382
383FVector2D UMapGenFunctionLibrary::InverseTransverseMercatorProjection(float x, float y, float lat0, float lon0)
384{
385 const float R = 6373000.0f;
386
389
390 float latt0 = FMath::DegreesToRadians(lat0);
391 float eps0 = latt0; // atan(tan(latt0)/cos(0));
392 float y0 = R * eps0;
393
394 float eps = (y + y0) / R;
395 float nab = x / R;
396
397 float lat = FMath::RadiansToDegrees(atan(sin(eps)/sqrt(tan(nab)*tan(nab)+cos(eps)*cos(eps))));
398 float lon = lon0 + FMath::RadiansToDegrees(atan(sinh(nab) / cos(eps)));
399
400 return FVector2D(lat, lon);
401}
402
404{
405 if (!TargetActor)
406 {
407 UE_LOG(LogCarlaMapGenFunctionLibrary, Warning, TEXT("Invalid TargetActor in AddSceneComponentToActor"));
408 return nullptr;
409 }
410
411 if (!TargetActor->GetRootComponent())
412 {
413 USceneComponent* NewRoot = NewObject<USceneComponent>(TargetActor, TEXT("GeneratedRoot"));
414 TargetActor->SetRootComponent(NewRoot);
415 NewRoot->RegisterComponent();
416 }
417
418 USceneComponent* SceneComp = NewObject<USceneComponent>(TargetActor);
419 if (!SceneComp)
420 {
421 UE_LOG(LogCarlaMapGenFunctionLibrary, Error, TEXT("Could not create USceneComponent"));
422 return nullptr;
423 }
424
425 SceneComp->SetupAttachment(TargetActor->GetRootComponent());
426 SceneComp->RegisterComponent();
427
428 TargetActor->AddInstanceComponent(SceneComp);
429
430 return SceneComp;
431}
432
434 TArray<FVector>& Vertices,
435 const TArray<int32>& Indices,
436 int Depth, // Number of neighbor levels
437 int NumIterations,
438 float SmoothingFactor // Blend between original and averaged
439)
440{
441 const int32 NumVertices = Vertices.Num();
442
443 // Step 1: Build adjacency map
444 TMap<int32, TSet<int32>> VertexNeighbors;
445 const int32 NumTriangles = Indices.Num() / 3;
446
447 for (int32 i = 0; i < NumTriangles; ++i)
448 {
449 int32 I0 = Indices[i * 3 + 0];
450 int32 I1 = Indices[i * 3 + 1];
451 int32 I2 = Indices[i * 3 + 2];
452
453 VertexNeighbors.FindOrAdd(I0).Add(I1);
454 VertexNeighbors.FindOrAdd(I0).Add(I2);
455
456 VertexNeighbors.FindOrAdd(I1).Add(I0);
457 VertexNeighbors.FindOrAdd(I1).Add(I2);
458
459 VertexNeighbors.FindOrAdd(I2).Add(I0);
460 VertexNeighbors.FindOrAdd(I2).Add(I1);
461 }
462
463 // Step 2: Iterative smoothing
464 for (int32 Iter = 0; Iter < NumIterations; ++Iter)
465 {
466 TArray<FVector> NewVertices = Vertices;
467 for (int32 i = 0; i < NumVertices; ++i)
468 {
469 TSet<int32> Visited;
470 TQueue<TPair<int32, int32>> Queue; // Pair of (vertex index, current depth)
471 Visited.Add(i);
472 Queue.Enqueue(TPair<int32, int32>(i, 0));
473 TArray<int32> CollectedNeighbors;
474
475 while (!Queue.IsEmpty())
476 {
477 TPair<int32, int32> Current;
478 Queue.Dequeue(Current);
479 int32 CurrentIndex = Current.Key;
480 int32 CurrentDepth = Current.Value;
481
482 if (CurrentDepth >= Depth)
483 continue;
484 const TSet<int32>* Neighbors = VertexNeighbors.Find(CurrentIndex);
485 if (!Neighbors) continue;
486
487 for (int32 NeighborIndex : *Neighbors)
488 {
489 if (!Visited.Contains(NeighborIndex))
490 {
491 Visited.Add(NeighborIndex);
492 CollectedNeighbors.Add(NeighborIndex);
493 Queue.Enqueue(TPair<int32, int32>(NeighborIndex, CurrentDepth + 1));
494 }
495 }
496 }
497
498 if (CollectedNeighbors.Num() > 0)
499 {
500 FVector Average = FVector::ZeroVector;
501 for (int32 NeighborIdx : CollectedNeighbors)
502 {
503 Average += Vertices[NeighborIdx];
504 }
505 Average /= CollectedNeighbors.Num();
506 NewVertices[i] = FMath::Lerp(Vertices[i], Average, SmoothingFactor);
507 }
508 }
509 Vertices = NewVertices;
510 }
511}
512
513float UMapGenFunctionLibrary::BicubicSampleG16(const TArrayView64<const uint16>& Pixels, int Width, int Height, float X, float Y)
514{
515 int ix = FMath::FloorToInt(X);
516 int iy = FMath::FloorToInt(Y);
517 float fx = X - ix;
518 float fy = Y - iy;
519
520 float patch[4][4];
521
522 // Fetch surrounding 4x4 pixel values
523 for (int m = -1; m <= 2; ++m)
524 {
525 for (int n = -1; n <= 2; ++n)
526 {
527 uint16 val = GetPixelG16(Pixels, Width, Height, ix + n, iy + m);
528 patch[m + 1][n + 1] = val / 65535.0f; // Normalize to [0,1]
529 }
530 }
531
532 float col[4];
533 for (int i = 0; i < 4; ++i)
534 {
535 col[i] = CubicHermite(patch[i][0], patch[i][1], patch[i][2], patch[i][3], fx);
536 }
537
538 float result = CubicHermite(col[0], col[1], col[2], col[3], fy);
539 return FMath::Clamp(result, 0.0f, 1.0f); // Final result in [0,1]
540}
float y
float x
carla::rpc::Response< T > R
static const float OSMToCentimetersScaleFactor
DEFINE_LOG_CATEGORY(LogCarlaMapGenFunctionLibrary)
UHierarchicalInstancedStaticMeshComponent * GetInstancedStaticMeshComponent()
static TArray< class AInstancedStaticMeshActor * > ChangeStaticMeshesInTheLevelForInstancedStaticMeshes(UWorld *World, TArray< UStaticMesh * > Filter, int MinNumOfInstancesToBeChanged=20)
static void FlushRenderingCommandsInBlueprint()
static float CubicHermite(float A, float B, float C, float D, float T)
static uint16 GetPixelG16(const TArrayView64< const uint16 > &Pixels, int Width, int Height, int X, int Y)
static TArray< AStaticMeshActor * > RevertStaticMeshesInTheLevelForInstancedStaticMeshes(UWorld *World, TArray< UStaticMesh * > Filter)
static UStaticMesh * CreateMesh(const FProceduralCustomMesh &Data, const TArray< FProcMeshTangent > &ParamTangents, UMaterialInstance *MaterialInstance, FString MapName, FString FolderName, FName MeshName)
static UInstancedStaticMeshComponent * AddInstancedStaticMeshComponentToActor(AActor *TargetActor)
static void SetThreadToSleep(float seconds)
static FMeshDescription BuildMeshDescriptionFromData(const FProceduralCustomMesh &Data, const TArray< FProcMeshTangent > &ParamTangents, UMaterialInstance *MaterialInstance)
static FVector2D GetTransversemercProjection(float lat, float lon, float lat0, float lon0)
static FVector2D InverseTransverseMercatorProjection(float x, float y, float lat0, float lon0)
static UStaticMeshComponent * AddStaticMeshComponentToActor(AActor *TargetActor)
static USceneComponent * AddSceneComponentToActor(AActor *TargetActor)
static float BicubicSampleG16(const TArrayView64< const uint16 > &Pixels, int Width, int Height, float X, float Y)
static void SmoothVerticesDeep(TArray< FVector > &Vertices, const TArray< int32 > &Indices, int Depth=3, int NumIterations=1, float SmoothingFactor=1.0f)
A definition of a Carla Mesh.
TArray< FVector2D > UV0