CARLA
 
载入中...
搜索中...
未找到
PrepareAssetsForCookingCommandlet.cpp
浏览该文件的文档.
1// Copyright (c) 2019 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//是一个用于准备资源用于“烹饪”(在游戏开发中,“烹饪”通常指将资源打包成最终可使用的格式)的命令类。
9#include "AssetRegistry/AssetRegistryModule.h"
10//与资产注册模块相关,用于管理项目中的资产。
11#include "SSTags.h"
12//与某些标签系统相关的头文件。
13#if WITH_EDITOR
14#include "FileHelpers.h"//是一个与文件操作相关的辅助类,并且只在编辑器环境下使用。
15#endif
16#include "Misc/FileHelper.h"//是另一个与文件操作相关的辅助类。
17#include "Serialization/JsonReader.h"
18#include "Serialization/JsonSerializer.h"
19#include "HAL/PlatformFilemanager.h"
20#include "UObject/ConstructorHelpers.h"
21#include "Materials/MaterialInstanceConstant.h"
23
24// 静态函数,用于验证静态网格是否符合某些条件
25static bool ValidateStaticMesh(UStaticMesh *Mesh)
26{
27 // 获取网格资源的名称
28 const FString AssetName = Mesh->GetName();
29
30 // 检查网格名称是否包含“light”或“sign”,忽略大小写
31 if (AssetName.Contains(TEXT("light"), ESearchCase::IgnoreCase) ||
32 AssetName.Contains(TEXT("sign"), ESearchCase::IgnoreCase))
33 {
34 // 如果包含,则返回false,表示验证不通过
35 return false;
36 }
37
38 // 遍历网格的所有静态材质
39 for (int i = 0; i < Mesh->StaticMaterials.Num(); i++)
40 {
41 // 获取当前索引处的材质
42 UMaterialInterface *Material = Mesh->GetMaterial(i);
43 if (!Material) {
44 // 如果材质为空,则使用默认的表面材质
45 Material = UMaterial::GetDefaultMaterial(MD_Surface);
46 }
47 // 获取材质的名称
48 const FString MaterialName = Material->GetName();
49
50 // 检查材质名称是否包含“light”或“sign”,忽略大小写
51 if (MaterialName.Contains(TEXT("light"), ESearchCase::IgnoreCase) ||
52 MaterialName.Contains(TEXT("sign"), ESearchCase::IgnoreCase))
53 {
54 // 如果包含,则返回false,表示验证不通过
55 return false;
56 }
57 }
58
59 // 如果所有检查都通过,则返回true,表示验证通过
60 return true;
61}
62
64{
65 // 设置必要的标志以运行命令行工具
66 IsClient = false;
67 IsEditor = true;
68 IsServer = false;
69 LogToConsole = true;
70
71#if WITH_EDITORONLY_DATA
72 // 获取 Carla 默认材质,
73 //这些材质将用于需要使用 Carla 材质的地图
74 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> MarkingNodeYellowMaterial(TEXT(
75 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/RoadPainterMaterials/LargeMaps/M_Road_03_Tiled_V3.M_Road_03_Tiled_V3'"));
76 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> MarkingNodeWhiteMaterial(TEXT(
77 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/RoadPainterMaterials/M_Road_03_LMW.M_Road_03_LMW'"));
78 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> RoadNode(TEXT(
79 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/RoadPainterMaterials/LargeMaps/M_Road_03_Tiled_V2.M_Road_03_Tiled_V2'"));
80 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> TerrainNodeMaterial(TEXT(
81 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/00_MastersOpt/Large_Maps/materials/MI_LargeLandscape_Grass.MI_LargeLandscape_Grass'"));
82 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> CurbNodeMaterial(TEXT(
83 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/LargeMap_materials/largeM_curb/MI_largeM_curb01.MI_largeM_curb01'"));
84 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> GutterNodeMaterial(TEXT(
85 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/LargeMap_materials/largeM_gutter/MI_largeM_gutter01.MI_largeM_gutter01'"));
86 static ConstructorHelpers::FObjectFinder<UMaterialInstanceConstant> SidewalkNode(TEXT(
87 "MaterialInstanceConstant'/Game/Carla/Static/GenericMaterials/LargeMap_materials/largeM_sidewalk/tile01/MI_largeM_tile02.MI_largeM_tile02'"));
88
89 GutterNodeMaterialInstance = (UMaterialInstance *) GutterNodeMaterial.Object;
90 CurbNodeMaterialInstance = (UMaterialInstance *) CurbNodeMaterial.Object;
91 TerrainNodeMaterialInstance = (UMaterialInstance *) TerrainNodeMaterial.Object;
92 MarkingNodeYellow = (UMaterialInstance *)MarkingNodeYellowMaterial.Object;
93 MarkingNodeWhite = (UMaterialInstance *)MarkingNodeWhiteMaterial.Object;
94 RoadNodeMaterial = (UMaterialInstance *) RoadNode.Object;
95 SidewalkNodeMaterialInstance = (UMaterialInstance *) SidewalkNode.Object;
96#endif
97}
98#if WITH_EDITORONLY_DATA
99
100FPackageParams UPrepareAssetsForCookingCommandlet::ParseParams(const FString &InParams) const
101{
102 TArray<FString> Tokens;
103 TArray<FString> Params;
104 TMap<FString, FString> ParamVals;
105
106 ParseCommandLine(*InParams, Tokens, Params);
107
108 FPackageParams PackageParams;
109
110 // 解析并存储包名称
111 FParse::Value(*InParams, TEXT("PackageName="), PackageParams.Name);
112
113 // 解析并存储仅准备地图的标志
114 FParse::Bool(*InParams, TEXT("OnlyPrepareMaps="), PackageParams.bOnlyPrepareMaps);
115 return PackageParams;
116}
117
118void UPrepareAssetsForCookingCommandlet::LoadWorld(FAssetData &AssetData)
119{
120 // Carla 中的基础地图路径
121 const FString BaseMap = TEXT("/Game/Carla/Maps/BaseMap");
122
123 // 使用对象库加载地图文件夹
124 MapObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor);
125 MapObjectLibrary->AddToRoot();
126 MapObjectLibrary->LoadAssetDataFromPath(*BaseMap);
127 MapObjectLibrary->LoadAssetsFromAssetData();
128 MapObjectLibrary->GetAssetDataList(AssetDatas);
129
130 if (AssetDatas.Num() > 0)
131 {
132 // 提取在文件夹路径中找到的第一个资产(即基础地图)
133 AssetData = AssetDatas.Pop();
134 }
135}
136
137void UPrepareAssetsForCookingCommandlet::LoadWorldTile(FAssetData &AssetData)
138{
139 //Carla 中的基础瓦片路径
140 const FString BaseTile = TEXT("/Game/Carla/Maps/TestMaps");
141
142 // 使用对象库加载地图文件夹。
143 MapObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor);
144 MapObjectLibrary->AddToRoot();
145 MapObjectLibrary->LoadAssetDataFromPath(*BaseTile);
146 MapObjectLibrary->LoadAssetsFromAssetData();
147 MapObjectLibrary->GetAssetDataList(AssetDatas);
148
149 if (AssetDatas.Num() > 0)
150 {
151 // 提取在文件夹路径中找到的第一个资产(即基础瓦片)
152 AssetData = AssetDatas.Pop();
153 }
154}
155
156void UPrepareAssetsForCookingCommandlet::LoadLargeMapWorld(FAssetData &AssetData)
157{
158 // Carla 中的基础地图路径
159 const FString BaseMap = TEXT("/Game/Carla/Maps/BaseLargeMap");
160
161 //使用对象库加载地图文件夹。
162 MapObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor);
163 MapObjectLibrary->AddToRoot();
164 MapObjectLibrary->LoadAssetDataFromPath(*BaseMap);
165 MapObjectLibrary->LoadAssetsFromAssetData();
166 MapObjectLibrary->GetAssetDataList(AssetDatas);
167
168 if (AssetDatas.Num() > 0)
169 {
170 // 提取在文件夹路径中找到的第一个资产(即基础地图)。
171 AssetData = AssetDatas.Pop();
172 }
173}
174
175TArray<AStaticMeshActor *> UPrepareAssetsForCookingCommandlet::SpawnMeshesToWorld(
176 const TArray<FString> &AssetsPaths,
177 bool bUseCarlaMaterials,
178 int i,
179 int j)
180{
181 TArray<AStaticMeshActor *> SpawnedMeshes;
182
183 // 为所有要生成的资产创建默认变换。
184 const FTransform ZeroTransform = FTransform();
185
186 // 使用对象库加载由 “AssetsPaths” 中指定的资产,
187 //用于构建地图世界。
188 AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor);
189 AssetsObjectLibrary->AddToRoot();
190 AssetsObjectLibrary->LoadAssetDataFromPaths(AssetsPaths);
191 AssetsObjectLibrary->LoadAssetsFromAssetData();
192 MapContents.Empty();
193 AssetsObjectLibrary->GetAssetDataList(MapContents);
194
195 UStaticMesh *MeshAsset;
196 AStaticMeshActor *MeshActor;
197
198 //要烹饪的当前瓦片的名称。
199 FString TileName;
200 if (i != -1)
201 {
202 TileName = FString::Printf(TEXT("_Tile_%d_%d"), i, j);
203 }
204
205 // try to get the name of the map that precedes all assets name
206 FString AssetName;
207 for (auto MapAsset : MapContents)
208 {
209 // 生成静态网格体
210 MeshAsset = Cast<UStaticMesh>(MapAsset.GetAsset());
211 if (MeshAsset && ValidateStaticMesh(MeshAsset))
212 {
213 // 获取资产名称。
214 MapAsset.AssetName.ToString(AssetName);
215
216 //检查以忽略来自其他瓦片的网格体。
217 if (i == -1 || (i != -1 && (AssetName.EndsWith(TileName) || AssetName.Contains(TileName + "_"))))
218 {
219 MeshActor = World->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), ZeroTransform);
220 UStaticMeshComponent *MeshComponent = MeshActor->GetStaticMeshComponent();
221 MeshComponent->SetStaticMesh(CastChecked<UStaticMesh>(MeshAsset));
222 MeshActor->SetActorLabel(AssetName, true);
223
224 // 在资产中将复杂碰撞设置为简单碰撞。
225 UBodySetup *BodySetup = MeshAsset->BodySetup;
226 if (BodySetup)
227 {
228 BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
229 MeshAsset->MarkPackageDirty();
230 }
231
232 SpawnedMeshes.Add(MeshActor);
233
234 if (bUseCarlaMaterials)
235 {
236 // 根据 RoadRunner 的语义分割标签设置
237 //Carla 材质。
238 if (AssetName.Contains(SSTags::R_MARKING1) || AssetName.Contains(SSTags::R_MARKING2))
239 {
240 for (int32 i = 0; i < MeshActor->GetStaticMeshComponent()->GetStaticMesh()->StaticMaterials.Num(); ++i)
241 {
242 if (MeshActor->GetStaticMeshComponent()->GetStaticMesh()->StaticMaterials[i].ImportedMaterialSlotName.ToString().Contains("Yellow"))
243 {
244 MeshActor->GetStaticMeshComponent()->SetMaterial(i, MarkingNodeYellow);
245 }
246 else
247 {
248 MeshActor->GetStaticMeshComponent()->SetMaterial(i, MarkingNodeWhite);
249 }
250 }
251 }
252 else if (AssetName.Contains(SSTags::R_ROAD1) || AssetName.Contains(SSTags::R_ROAD2))
253 {
254 MeshActor->GetStaticMeshComponent()->SetMaterial(0, RoadNodeMaterial);
255 }
256 else if (AssetName.Contains(SSTags::R_TERRAIN))
257 {
258 MeshActor->GetStaticMeshComponent()->SetMaterial(0, TerrainNodeMaterialInstance);
259 MeshActor->GetStaticMeshComponent()->bReceivesDecals = false;
260 }
261 else if (AssetName.Contains(SSTags::R_SIDEWALK1) || AssetName.Contains(SSTags::R_SIDEWALK2))
262 {
263 MeshActor->GetStaticMeshComponent()->SetMaterial(0, SidewalkNodeMaterialInstance);
264 MeshActor->GetStaticMeshComponent()->bReceivesDecals = false;
265 }
266 else if (AssetName.Contains(SSTags::R_CURB1) || AssetName.Contains(SSTags::R_CURB2)) {
267
268 MeshActor->GetStaticMeshComponent()->SetMaterial(0, CurbNodeMaterialInstance);
269 MeshActor->GetStaticMeshComponent()->bReceivesDecals = false;
270 }
271 else if (AssetName.Contains(SSTags::R_GUTTER1) || AssetName.Contains(SSTags::R_GUTTER2)) {
272
273 MeshActor->GetStaticMeshComponent()->SetMaterial(0, GutterNodeMaterialInstance);
274 MeshActor->GetStaticMeshComponent()->bReceivesDecals = false;
275 }
276 }
277 }
278 }
279 }
280
281 // 清除库中已加载的资产。
282 AssetsObjectLibrary->ClearLoaded();
283
284 // 将包标记为已修改。
285 World->MarkPackageDirty();
286
287 return SpawnedMeshes;
288}
289
290bool UPrepareAssetsForCookingCommandlet::IsMapInTiles(const TArray<FString> &AssetsPaths)
291{
292 //使用对象库加载 “AssetsPaths” 中指定的资产,
293 //以构建地图世界
294 AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor);
295 AssetsObjectLibrary->AddToRoot();
296 AssetsObjectLibrary->LoadAssetDataFromPaths(AssetsPaths);
297 AssetsObjectLibrary->LoadAssetsFromAssetData();
298 MapContents.Empty();
299 AssetsObjectLibrary->GetAssetDataList(MapContents);
300
301 UStaticMesh *MeshAsset;
302
303 FString AssetName;
304 bool Found = false;
305 for (auto MapAsset : MapContents)
306 {
307 // 生成静态网格。
308 MeshAsset = Cast<UStaticMesh>(MapAsset.GetAsset());
309 if (MeshAsset && ValidateStaticMesh(MeshAsset))
310 {
311 // 获取资产名称。
312 MapAsset.AssetName.ToString(AssetName);
313
314 // 检查该资产是否是一个瓦片。
315 if (AssetName.Contains("_Tile_"))
316 {
317 Found = true;
318 break;
319 }
320 }
321 }
322
323 // 清除库中已加载的资产。
324 AssetsObjectLibrary->ClearLoaded();
325
326 return Found;
327}
328
329void UPrepareAssetsForCookingCommandlet::DestroySpawnedActorsInWorld(
330 TArray<AStaticMeshActor *> &SpawnedActors)
331{
332 // 销毁所有已生成的演员(角色)。
333 for (auto Actor : SpawnedActors)
334 {
335 Actor->Destroy();
336 }
337
338 // 将包标记为已修改(脏)状态。
339 World->MarkPackageDirty();
340}
341
342bool UPrepareAssetsForCookingCommandlet::SaveWorld(
343 FAssetData &AssetData,
344 const FString &PackageName,
345 const FString &DestPath,
346 const FString &WorldName,
347 bool bGenerateSpawnPoints)
348{
349 // 创建包以进行保存。
350 UPackage *Package = AssetData.GetPackage();
351 Package->SetFolderName(*WorldName);
352 Package->FullyLoad();
353 Package->MarkPackageDirty();
354 FAssetRegistryModule::AssetCreated(World);
355
356 // 重命名地图。
357 World->Rename(*WorldName, World->GetOuter());
358 const FString PackagePath = DestPath + "/" + WorldName;
359 FAssetRegistryModule::AssetRenamed(World, *PackagePath);
360 World->MarkPackageDirty();
361 World->GetOuter()->MarkPackageDirty();
362
363 // 检查 OpenDrive 文件是否存在。
364 const FString PathXODR = FPaths::ProjectContentDir() + PackageName + TEXT("/Maps/") +
365 WorldName + TEXT("/OpenDrive/") + WorldName + TEXT(".xodr");
366
367 bool bPackageSaved = false;
368 if (FPaths::FileExists(PathXODR) && bGenerateSpawnPoints)
369 {
370 // 在保存地图之前,我们需要生成 OpenDrive 资产。
371 AOpenDriveActor *OpenWorldActor = CastChecked<AOpenDriveActor>(
372 World->SpawnActor(AOpenDriveActor::StaticClass(),
373 new FVector(),
374 NULL));
375
376 OpenWorldActor->BuildRoutes(WorldName);
377 OpenWorldActor->AddSpawners();
378
379 bPackageSaved = SavePackage(PackagePath, Package);
380
381 // 一旦保存了地图,我们就需要销毁 OpenDrive 资产。
382 OpenWorldActor->RemoveRoutes();
383 OpenWorldActor->RemoveSpawners();
384 OpenWorldActor->Destroy();
385 }
386 else
387 {
388 bPackageSaved = SavePackage(PackagePath, Package);
389 }
390
391 return bPackageSaved;
392}
393
394FString UPrepareAssetsForCookingCommandlet::GetFirstPackagePath(const FString &PackageName) const
395{
396 // 获取所有包的名称。
397 TArray<FString> PackageList;
398 IFileManager::Get().FindFilesRecursive(PackageList, *(FPaths::ProjectContentDir()),
399 *(PackageName + TEXT(".Package.json")), true, false, false);
400
401 if (PackageList.Num() == 0)
402 {
403 UE_LOG(LogTemp, Error, TEXT("Package json file not found."));
404 return {};
405 }
406
407 return IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*PackageList[0]);
408}
409
410FAssetsPaths UPrepareAssetsForCookingCommandlet::GetAssetsPathFromPackage(const FString &PackageName) const
411{
412 const FString PackageJsonFilePath = GetFirstPackagePath(PackageName);
413
414 FAssetsPaths AssetsPaths;
415
416 // 得到所有地图路径
417 FString MapsFileJsonContent;
418 if (FFileHelper::LoadFileToString(MapsFileJsonContent, *PackageJsonFilePath))
419 {
420 TSharedPtr<FJsonObject> JsonParsed;
421 TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(MapsFileJsonContent);
422 if (FJsonSerializer::Deserialize(JsonReader, JsonParsed))
423 {
424 // 添加地图路径。
425 auto MapsJsonArray = JsonParsed->GetArrayField(TEXT("maps"));
426
427 for (auto &MapJsonValue : MapsJsonArray)
428 {
429 TSharedPtr<FJsonObject> MapJsonObject = MapJsonValue->AsObject();
430
431 FMapData MapData;
432 MapData.Name = MapJsonObject->GetStringField(TEXT("name"));
433 MapData.Path = MapJsonObject->GetStringField(TEXT("path"));
434 MapData.bUseCarlaMapMaterials = MapJsonObject->GetBoolField(TEXT("use_carla_materials"));
435
436 AssetsPaths.MapsPaths.Add(std::move(MapData));
437 }
438
439 // 添加地图路径
440 auto PropJsonArray = JsonParsed->GetArrayField(TEXT("props"));
441
442 for (auto &PropJsonValue : PropJsonArray)
443 {
444 TSharedPtr<FJsonObject> PropJsonObject = PropJsonValue->AsObject();
445
446 const FString PropAssetPath = PropJsonObject->GetStringField(TEXT("path"));
447
448 AssetsPaths.PropsPaths.Add(std::move(PropAssetPath));
449 }
450 }
451 }
452 return AssetsPaths;
453}
454
455bool SaveStringTextToFile(
456 FString SaveDirectory,
457 FString FileName,
458 FString SaveText,
459 bool bAllowOverWriting)
460{
461 IPlatformFile &PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
462
463 // 如果目标目录在调用之前已存在或者在调用期间已被创建,
464 //那么“CreateDirectoryTree”(创建目录树)
465 //将返回 true。
466 if (PlatformFile.CreateDirectoryTree(*SaveDirectory))
467 {
468 // 获取绝对文件路径。
469 const FString AbsoluteFilePath = SaveDirectory + "/" + FileName;
470
471
472 if (bAllowOverWriting || !PlatformFile.FileExists(*AbsoluteFilePath))
473 {
474 FFileHelper::SaveStringToFile(SaveText, *AbsoluteFilePath);
475 }
476 }
477 return true;
478}
479
480bool UPrepareAssetsForCookingCommandlet::SavePackage(const FString &PackagePath, UPackage *Package) const
481{
482 const FString PackageFileName = FPackageName::LongPackageNameToFilename(
483 PackagePath,
484 FPackageName::GetMapPackageExtension());
485
486 if (FPaths::FileExists(*PackageFileName))
487 {
488 // 如果包已存在,则不会保存该包。
489 return false;
490 }
491
492 return UPackage::SavePackage(
493 Package,
494 World,
495 EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
496 *PackageFileName,
497 GError,
498 nullptr,
499 true,
500 true,
501 SAVE_NoError);
502}
503
504// UPrepareAssetsForCookingCommandlet类的方法,用于生成地图路径文件
505void UPrepareAssetsForCookingCommandlet::GenerateMapPathsFile(
506 const FAssetsPaths &AssetsPaths,
507 const FString &PropsMapPath)
508{
509 // 存储地图路径数据,用于Windows和Linux
510 FString MapPathData;
511 FString MapPathDataLinux;
512 // 获取文件管理器
513 IFileManager &FileManager = IFileManager::Get();
514 // 遍历所有地图路径
515 for (const auto &Map : AssetsPaths.MapsPaths)
516 {
517 // 追加地图路径和名称,用于Windows和Linux
518 MapPathData.Append(Map.Path + TEXT("/") + Map.Name + TEXT("\n"));
519 MapPathDataLinux.Append(Map.Path + TEXT("/") + Map.Name + TEXT("+"));
520 // 获取地图路径下的资产数据
521 TArray<FAssetData> AssetsData;
522 UObjectLibrary* ObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), true, true);
523 ObjectLibrary->LoadAssetDataFromPath(Map.Path);
524 ObjectLibrary->GetAssetDataList(AssetsData);
525 int NumTiles = 0;
526 // 遍历资产数据,查找与地图名称匹配的tiles
527 for (FAssetData &AssetData : AssetsData)
528 {
529 FString AssetName = AssetData.AssetName.ToString();
530 if (AssetName.Contains(Map.Name + "_Tile_"))
531 {
532 // 追加tiles的路径和名称,用于Windows和Linux
533 MapPathData.Append(Map.Path + TEXT("/") + AssetName + TEXT("\n"));
534 MapPathDataLinux.Append(Map.Path + TEXT("/") + AssetName + TEXT("+"));
535 NumTiles++;
536 }
537 }
538 // 记录找到的tiles数量
539 UE_LOG(LogTemp, Warning, TEXT("Found %d tiles"), NumTiles);
540 }
541
542 // 如果提供了PropsMapPath,则追加到路径数据中
543 if (!PropsMapPath.IsEmpty())
544 {
545 MapPathData.Append(PropsMapPath + TEXT("/PropsMap"));
546 MapPathDataLinux.Append(PropsMapPath + TEXT("/PropsMap"));
547 }
548 else
549 {
550 // 否则,从Linux路径数据中移除最后一个"+"
551 MapPathDataLinux.RemoveFromEnd(TEXT("+"));
552 }
553
554 // 获取保存目录和文件名
555 const FString SaveDirectory = FPaths::ProjectContentDir();
556 const FString FileName = FString("MapPaths.txt");
557 // 将路径数据保存到文件中
558 const FString FileNameLinux = FString("MapPathsLinux.txt");
559 SaveStringTextToFile(SaveDirectory, FileName, MapPathData, true);
560 SaveStringTextToFile(SaveDirectory, FileNameLinux, MapPathDataLinux, true);
561}
562
563// UPrepareAssetsForCookingCommandlet类的方法,用于生成包路径文件
564void UPrepareAssetsForCookingCommandlet::GeneratePackagePathFile(const FString &PackageName)
565{
566 // 获取保存目录和文件名
567 FString SaveDirectory = FPaths::ProjectContentDir();
568 FString FileName = FString("PackagePath.txt");
569 // 获取包的路径
570 FString PackageJsonFilePath = GetFirstPackagePath(PackageName);
571 // 将包路径保存到文件中
572 SaveStringTextToFile(SaveDirectory, FileName, PackageJsonFilePath, true);
573}
574
575void UPrepareAssetsForCookingCommandlet::PrepareMapsForCooking(
576 const FString &PackageName,
577 const TArray<FMapData> &MapsPaths)
578{
579 // 设置基础路径
580 FString BasePath = TEXT("/Game/") + PackageName + TEXT("/Static/");
581
582 // 遍历所有地图路径
583 for (const auto &Map : MapsPaths)
584 {
585 // 设置地图路径
586 const FString MapPath = TEXT("/") + Map.Name;
587
588 // 设置默认路径、道路路径、道路线路径、地形路径和人行道路径
589 const FString DefaultPath = TEXT("/Game/") + PackageName + TEXT("/Maps/") + Map.Name;
590 const FString RoadsPath = BasePath + SSTags::ROAD + MapPath;
591 const FString RoadLinesPath = BasePath + SSTags::ROADLINE + MapPath;
592 const FString TerrainPath = BasePath + SSTags::TERRAIN + MapPath;
593 const FString SidewalkPath = BasePath + SSTags::SIDEWALK + MapPath;
594
595 // 生成位于语义分割文件夹中的资产。
596 TArray<FString> DataPath = {DefaultPath, RoadsPath, RoadLinesPath, TerrainPath, SidewalkPath};
597
598 // 检查我们是有一个单一地图还是有一个分块地图。
599 if (!IsMapInTiles(DataPath))
600 {
601 UE_LOG(LogTemp, Log, TEXT("Cooking map"));
602 // 加载世界。
603 FAssetData AssetData;
604 LoadWorld(AssetData);
605 UObjectRedirector *BaseMapRedirector = Cast<UObjectRedirector>(AssetData.GetAsset());
606 if (BaseMapRedirector != nullptr) {
607 World = CastChecked<UWorld>(BaseMapRedirector->DestinationObject);
608 }
609 else {
610 World = CastChecked<UWorld>(AssetData.GetAsset());
611 }
612 // 尝试烘培(处理)整个地图(非分块地图)。
613 TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(DataPath, Map.bUseCarlaMapMaterials, -1, -1);
614 // 在指定路径中保存世界。
615 SaveWorld(AssetData, PackageName, Map.Path, Map.Name);
616 // 从世界中移除已生成的参与者,以使其与基础地图保持一致。
617 DestroySpawnedActorsInWorld(SpawnedActors);
618 }
619 else
620 {
621 // 定义一个数组,用于存储地图路径和对应的ID
622 TArray<TPair<FString, FIntVector>> MapPathsIds;
623
624 // 定义一个FVector变量,用于存储第0个瓦片的位置
625 FVector PositionTile0 = FVector();
626 // 定义瓦片的大小(以米为单位)
627 float TileSize = 200000.f;
628 // 定义一个字符串变量,用于存储从文件中读取的文本
629 FString TxtFile;
630 // 定义瓦片信息文件的路径
631 FString TilesInfoPath = FPaths::ProjectContentDir() + PackageName + TEXT("/Maps/") + Map.Name + "/TilesInfo.txt";
632 // 记录日志,表示正在加载瓦片信息文件
633 UE_LOG(LogTemp, Warning, TEXT("Loading %s ..."), *TilesInfoPath);
634 // 尝试从文件中加载文本内容到TxtFile变量中
635 if (FFileHelper::LoadFileToString(TxtFile, *(TilesInfoPath)) == true) {
636
637 // 将文件内容按逗号分隔,并存储到Out数组中
638 TArray<FString> Out;
639 TxtFile.ParseIntoArray(Out, TEXT(","), true);
640 // 检查数组中是否至少有3个元素(X, Y, TileSize)
641 if (Out.Num() >= 3)
642 {
643 // 定义从米到厘米的转换系数
644 const float METERSTOCM = 100.f;
645 // 从文件中读取第0个瓦片的X和Y坐标,并转换为厘米
646 PositionTile0.X = METERSTOCM * FCString::Atof(*Out[0]);
647 PositionTile0.Y = METERSTOCM * FCString::Atof(*Out[1]);
648 // 从文件中读取瓦片的大小,并转换为厘米
649 TileSize = METERSTOCM * FCString::Atof(*Out[2]);
650 }
651 else
652 {
653 // 如果数组元素不足3个,则记录日志表示瓦片信息文件格式无效
654 UE_LOG(LogTemp, Warning, TEXT("TilesInfo.txt format is invalid file"));
655 }
656 }
657 else {
658 // 如果无法加载文件,则记录日志表示找不到瓦片信息文件
659 UE_LOG(LogTemp, Warning, TEXT("Could not find TilesInfo.txt file"));
660 }
661
662 UE_LOG(LogTemp, Log, TEXT("Cooking tiles:"));
663 // 加载世界。
664 FAssetData AssetData;
665 LoadWorldTile(AssetData);
666 UObjectRedirector *BaseMapRedirector = Cast<UObjectRedirector>(AssetData.GetAsset());
667 if (BaseMapRedirector != nullptr) {
668 World = CastChecked<UWorld>(BaseMapRedirector->DestinationObject);
669 }
670 else {
671 World = CastChecked<UWorld>(AssetData.GetAsset());
672 }
673 // 尝试创建地图的每一种可能的分块。
674 int i, j;
675 bool Res;
676 j = 0;
677 do
678 {
679 i = 0;
680 do
681 {
682 // Spawn
683 TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(DataPath, Map.bUseCarlaMapMaterials, i, j);
684 Res = SpawnedActors.Num() > 0;
685 if (Res)
686 {
687 UE_LOG(LogTemp, Log, TEXT(" Tile %d,%d found"), i, j);
688 FString TileName;
689 TileName = FString::Printf(TEXT("%s_Tile_%d_%d"), *Map.Name, i, j);
690 // 在指定路径中保存世界。
691 // UE_LOG(LogTemp, Log, TEXT("Saving as %s to %s"), *TileName, *Map.Path);
692 SaveWorld(AssetData, PackageName, Map.Path, TileName);
693 MapPathsIds.Add(
694 TPair<FString, FIntVector>(
695 Map.Path + "/" + TileName, FIntVector(i, j, 0)));
696 // 从世界中移除生成的参与者,以使其与基础地图保持一致。
697 DestroySpawnedActorsInWorld(SpawnedActors);
698 ++i;
699 }
700 }
701 while (Res);
702 ++j;
703 }
704 while (i > 0);
705
706 #if WITH_EDITOR
707 UEditorLoadingAndSavingUtils::SaveDirtyPackages(true, true);
708 #endif
709 // 加载分块地图的基础地图。
710 LoadLargeMapWorld(AssetData);
711 BaseMapRedirector = Cast<UObjectRedirector>(AssetData.GetAsset());
712 if (BaseMapRedirector != nullptr) {
713 World = CastChecked<UWorld>(BaseMapRedirector->DestinationObject);
714 }
715 else {
716 World = CastChecked<UWorld>(AssetData.GetAsset());
717 }
718
719 // 生成大型地图管理器。
720 ALargeMapManager* LargeMapManager = World->SpawnActor<ALargeMapManager>(
721 ALargeMapManager::StaticClass(), FTransform());
722 LargeMapManager->LargeMapTilePath = Map.Path;
723 LargeMapManager->LargeMapName = Map.Name;
724 LargeMapManager->SetTile0Offset(PositionTile0);
725 LargeMapManager->SetTileSize(TileSize);
726 LargeMapManager->GenerateMap(MapPathsIds);
727
728 SaveWorld(AssetData, PackageName, Map.Path, Map.Name, false);
729
730 UE_LOG(LogTemp, Log, TEXT("End cooking tiles"));
731 }
732 }
733}
734
735void UPrepareAssetsForCookingCommandlet::PreparePropsForCooking(
736 FString &PackageName,
737 const TArray<FString> &PropsPaths,
738 FString &MapDestPath)
739{
740 // 生成大型地图管理器。
741 FAssetData AssetData;
742 // 加载基础地图。
743 LoadWorld(AssetData);
744 UObjectRedirector *BaseMapRedirector = Cast<UObjectRedirector>(AssetData.GetAsset());
745 if (BaseMapRedirector != nullptr) {
746 World = CastChecked<UWorld>(BaseMapRedirector->DestinationObject);
747 }
748 else {
749 World = CastChecked<UWorld>(AssetData.GetAsset());
750 }
751
752 //从道具的原始路径中移除网格名称,
753 //以便我们可以加载文件夹内的道具。
754 TArray<FString> PropPathDirs = PropsPaths;
755
756 for (auto &PropPath : PropPathDirs)
757 {
758 PropPath.Split(TEXT("/"), &PropPath, nullptr,
759 ESearchCase::Type::IgnoreCase, ESearchDir::Type::FromEnd);
760 }
761
762 // 在单个基础地图中添加道具。
763 TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(PropPathDirs, false);
764
765 const FString MapName("PropsMap");
766 SaveWorld(AssetData, PackageName, MapDestPath, MapName);
767
768 DestroySpawnedActorsInWorld(SpawnedActors);
769 MapObjectLibrary->ClearLoaded();
770}
771
772int32 UPrepareAssetsForCookingCommandlet::Main(const FString &Params)
773{
774 FPackageParams PackageParams = ParseParams(Params);
775
776 // 获取道具和地图的路径。
777 FAssetsPaths AssetsPaths = GetAssetsPathFromPackage(PackageParams.Name);
778
779 if (PackageParams.bOnlyPrepareMaps)
780 {
781 PrepareMapsForCooking(PackageParams.Name, AssetsPaths.MapsPaths);
782 }
783 else
784 {
785 FString PropsMapPath("");
786
787 if (AssetsPaths.PropsPaths.Num() > 0)
788 {
789 PropsMapPath = TEXT("/Game/") + PackageParams.Name + TEXT("/Maps/PropsMap");
790 PreparePropsForCooking(PackageParams.Name, AssetsPaths.PropsPaths, PropsMapPath);
791 }
792
793 // 保存地图路径文件以供进一步使用
794 GenerateMapPathsFile(AssetsPaths, PropsMapPath);
795
796 // 保存包路径以供进一步使用
797 GeneratePackagePathFile(PackageParams.Name);
798 }
799
800#if WITH_EDITOR
801 UEditorLoadingAndSavingUtils::SaveDirtyPackages(true, true);
802#endif
803
804 return 0;
805}
806#endif
UE_LOG(LogCarla, Log, TEXT("UActorDispatcher::Destroying actor: '%s' %x"), *Id, Actor)
TSharedPtr< const FActorInfo > carla::rpc::ActorState UWorld Actor
static bool ValidateStaticMesh(UStaticMesh *Mesh)
void SetTile0Offset(const FVector &Offset)
void SetTileSize(float Size)
void GenerateMap(FString InAssetsPath)
void RemoveRoutes()
根据地图名称构建路径
void BuildRoutes()
构造函数
地图类的前向声明,用于在LaneInvasionSensor类中可能的引用。
UMaterialInstance * RoadNodeMaterial
道路节点网格的替代材料
UMaterialInstance * CurbNodeMaterialInstance
用于道路路缘的材料
FString GetFirstPackagePath(const FString &PackageName) const
获取在虚幻内容目录中找到的第一个 .Package.json 文件,文件名为 PackageName
UObjectLibrary * AssetsObjectLibrary
用于在对象库中加载资源。加载的数据存储在 AssetDatas 中
UWorld * World
基础地图世界从Carla内容中加载
UObjectLibrary * MapObjectLibrary
用于加载对象库中的地图。加载的数据存储在AssetDatas中
UMaterialInstance * GutterNodeMaterialInstance
用于道路排水沟的材料
UMaterialInstance * MarkingNodeYellow
中心车道标线的替代材料
UMaterialInstance * MarkingNodeWhite
用于外部车道的标线材料
TArray< FAssetData > AssetDatas
从任何对象库加载的资源
UMaterialInstance * SidewalkNodeMaterialInstance
用于SidewalkNodes的解决方法材料
bool SavePackage(const FString &PackagePath, UPackage *Package) const
将Package 保存为 .umap 格式的文件,路径为 Unreal 内容文件夹内的 PackagePath
TArray< FAssetData > MapContents
从任何对象库加载地图内容
UMaterialInstance * TerrainNodeMaterialInstance
地形节点的替代材料
包含从 .Package.json 文件中读取的所有资产数据的结构
包含从 .Package.json 文件中读取的地图数据的结构体
包含带有 Name 的 Package 和 bOnlyPrepareMaps 标志的结构体,用于 在不同阶段分离地图和道具的烹饪过程(地图将在 make import 命令期间导入,而道具将在 make...