Import maps in tiles

This commit is contained in:
bernat 2021-04-15 11:17:37 +02:00
parent 1aa53798a2
commit bc4d23a5e9
4 changed files with 248 additions and 102 deletions

View File

@ -5,6 +5,7 @@
* Upgrading to Unreal Engine 4.26
* Added Lincoln 2020 vehicle dimensions for CarSim integration
* Enabling the **no_delay** option to RPC and stream sockets
* The special nomenclature to define roads (ROAD_ROAD), sidewalks (ROAD_SIDEWALK)... can be at any position of the asset name
* Improved performance bencharmark script: sync, map and sensor selection, ...
* Improved performance, destroyed PhysX state for vehicles when physics are disable
* Improved parallelism of raycast sensors in system with large number of cores

View File

@ -105,28 +105,55 @@ void UPrepareAssetsForCookingCommandlet::LoadWorld(FAssetData &AssetData)
}
}
void UPrepareAssetsForCookingCommandlet::LoadWorldTile(FAssetData &AssetData)
{
// BaseTile path inside Carla
const FString BaseTile = TEXT("/Game/Carla/Maps/TestMaps");
// Load Map folder using object library
MapObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor);
MapObjectLibrary->AddToRoot();
MapObjectLibrary->LoadAssetDataFromPath(*BaseTile);
MapObjectLibrary->LoadAssetsFromAssetData();
MapObjectLibrary->GetAssetDataList(AssetDatas);
if (AssetDatas.Num() > 0)
{
// Extract first asset found in folder path (i.e. the BaseTile)
AssetData = AssetDatas.Pop();
}
}
TArray<AStaticMeshActor *> UPrepareAssetsForCookingCommandlet::SpawnMeshesToWorld(
const TArray<FString> &AssetsPaths,
bool bUseCarlaMaterials)
bool bUseCarlaMaterials,
int i,
int j)
{
TArray<AStaticMeshActor *> SpawnedMeshes;
// Create default Transform for all assets to spawn
const FTransform zeroTransform = FTransform();
// Load assets specified in AssetsPaths by using an object library
// for building map world
AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor);
AssetsObjectLibrary->AddToRoot();
AssetsObjectLibrary->LoadAssetDataFromPaths(AssetsPaths);
AssetsObjectLibrary->LoadAssetsFromAssetData();
MapContents.Empty();
AssetsObjectLibrary->GetAssetDataList(MapContents);
// Create default Transform for all assets to spawn
const FTransform zeroTransform = FTransform();
UStaticMesh *MeshAsset;
AStaticMeshActor *MeshActor;
// name of current tile to cook
FString TileName;
if (i != -1)
{
TileName = FString::Printf(TEXT("_Tile_%d_%d"), i, j);
}
// try to get the name of the map that precedes all assets name
FString AssetName;
for (auto MapAsset : MapContents)
@ -135,43 +162,48 @@ TArray<AStaticMeshActor *> UPrepareAssetsForCookingCommandlet::SpawnMeshesToWorl
MeshAsset = Cast<UStaticMesh>(MapAsset.GetAsset());
if (MeshAsset && ValidateStaticMesh(MeshAsset))
{
MeshActor = World->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), zeroTransform);
UStaticMeshComponent *MeshComponent = MeshActor->GetStaticMeshComponent();
MeshComponent->SetStaticMesh(CastChecked<UStaticMesh>(MeshAsset));
// Rename asset
// get asset name
MapAsset.AssetName.ToString(AssetName);
// set complex collision as simple in asset
UBodySetup *BodySetup = MeshAsset->BodySetup;
if (BodySetup)
// check to ignore meshes from other tiles
if (i == -1 || (i != -1 && AssetName.Contains(TileName)))
{
BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
MeshAsset->MarkPackageDirty();
}
MeshActor = World->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), zeroTransform);
UStaticMeshComponent *MeshComponent = MeshActor->GetStaticMeshComponent();
MeshComponent->SetStaticMesh(CastChecked<UStaticMesh>(MeshAsset));
MeshActor->SetActorLabel(AssetName, true);
SpawnedMeshes.Add(MeshActor);
// set complex collision as simple in asset
UBodySetup *BodySetup = MeshAsset->BodySetup;
if (BodySetup)
{
BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
MeshAsset->MarkPackageDirty();
}
if (bUseCarlaMaterials)
{
// Set Carla Materials depending on RoadRunner's Semantic Segmentation
// tag
if (AssetName.Contains(SSTags::R_MARKING1) || AssetName.Contains(SSTags::R_MARKING2))
SpawnedMeshes.Add(MeshActor);
if (bUseCarlaMaterials)
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, MarkingNodeMaterial);
MeshActor->GetStaticMeshComponent()->SetMaterial(1, MarkingNodeMaterialAux);
}
else if (AssetName.Contains(SSTags::R_ROAD1) || AssetName.Contains(SSTags::R_ROAD2))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, RoadNodeMaterial);
}
else if (AssetName.Contains(SSTags::R_TERRAIN))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, TerrainNodeMaterial);
}
else if (AssetName.Contains(SSTags::R_SIDEWALK1) || AssetName.Contains(SSTags::R_SIDEWALK2))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, SidewalkNodeMaterial);
// Set Carla Materials depending on RoadRunner's Semantic Segmentation
// tag
if (AssetName.Contains(SSTags::R_MARKING1) || AssetName.Contains(SSTags::R_MARKING2))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, MarkingNodeMaterial);
MeshActor->GetStaticMeshComponent()->SetMaterial(1, MarkingNodeMaterialAux);
}
else if (AssetName.Contains(SSTags::R_ROAD1) || AssetName.Contains(SSTags::R_ROAD2))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, RoadNodeMaterial);
}
else if (AssetName.Contains(SSTags::R_TERRAIN))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, TerrainNodeMaterial);
}
else if (AssetName.Contains(SSTags::R_SIDEWALK1) || AssetName.Contains(SSTags::R_SIDEWALK2))
{
MeshActor->GetStaticMeshComponent()->SetMaterial(0, SidewalkNodeMaterial);
}
}
}
}
@ -186,6 +218,45 @@ TArray<AStaticMeshActor *> UPrepareAssetsForCookingCommandlet::SpawnMeshesToWorl
return SpawnedMeshes;
}
bool UPrepareAssetsForCookingCommandlet::IsMapInTiles(const TArray<FString> &AssetsPaths)
{
// Load assets specified in AssetsPaths by using an object library
// for building map world
AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor);
AssetsObjectLibrary->AddToRoot();
AssetsObjectLibrary->LoadAssetDataFromPaths(AssetsPaths);
AssetsObjectLibrary->LoadAssetsFromAssetData();
MapContents.Empty();
AssetsObjectLibrary->GetAssetDataList(MapContents);
UStaticMesh *MeshAsset;
FString AssetName;
bool Found = false;
for (auto MapAsset : MapContents)
{
// Spawn Static Mesh
MeshAsset = Cast<UStaticMesh>(MapAsset.GetAsset());
if (MeshAsset && ValidateStaticMesh(MeshAsset))
{
// get asset name
MapAsset.AssetName.ToString(AssetName);
// check if the asset is a tile
if (AssetName.Contains("_Tile_"))
{
Found = true;
break;
}
}
}
// Clear loaded assets in library
AssetsObjectLibrary->ClearLoaded();
return Found;
}
void UPrepareAssetsForCookingCommandlet::DestroySpawnedActorsInWorld(
TArray<AStaticMeshActor *> &SpawnedActors)
{
@ -397,11 +468,6 @@ void UPrepareAssetsForCookingCommandlet::PrepareMapsForCooking(
const FString &PackageName,
const TArray<FMapData> &MapsPaths)
{
// Load World
FAssetData AssetData;
LoadWorld(AssetData);
World = CastChecked<UWorld>(AssetData.GetAsset());
FString BasePath = TEXT("/Game/") + PackageName + TEXT("/Static/");
for (const auto &Map : MapsPaths)
@ -417,13 +483,59 @@ void UPrepareAssetsForCookingCommandlet::PrepareMapsForCooking(
// Spawn assets located in semantic segmentation folders
TArray<FString> DataPath = {DefaultPath, RoadsPath, RoadLinesPath, TerrainPath, SidewalkPath};
TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(DataPath, Map.bUseCarlaMapMaterials);
// Save the World in specified path
SaveWorld(AssetData, PackageName, Map.Path, Map.Name);
// Remove spawned actors from world to keep equal as BaseMap
DestroySpawnedActorsInWorld(SpawnedActors);
// check whether we have a single map or a map in tiles
if (!IsMapInTiles(DataPath))
{
UE_LOG(LogTemp, Log, TEXT("Cooking map"));
// Load World
FAssetData AssetData;
LoadWorld(AssetData);
World = CastChecked<UWorld>(AssetData.GetAsset());
// try to cook the whole map (no tiles)
TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(DataPath, Map.bUseCarlaMapMaterials, -1, -1);
// Save the World in specified path
SaveWorld(AssetData, PackageName, Map.Path, Map.Name);
// Remove spawned actors from world to keep equal as BaseMap
DestroySpawnedActorsInWorld(SpawnedActors);
}
else
{
UE_LOG(LogTemp, Log, TEXT("Cooking tiles:"));
// Load World
FAssetData AssetData;
LoadWorldTile(AssetData);
World = CastChecked<UWorld>(AssetData.GetAsset());
// try to create each possible tile of the map
int i, j;
bool Res;
j = 0;
do
{
i = 0;
do
{
// Spawn
TArray<AStaticMeshActor *> SpawnedActors = SpawnMeshesToWorld(DataPath, Map.bUseCarlaMapMaterials, i, j);
Res = SpawnedActors.Num() > 0;
if (Res)
{
UE_LOG(LogTemp, Log, TEXT(" Tile %d,%d found"), i, j);
FString TileName;
TileName = FString::Printf(TEXT("%s_Tile_%d_%d"), *Map.Name, i, j);
// Save the World in specified path
// UE_LOG(LogTemp, Log, TEXT("Saving as %s to %s"), *TileName, *Map.Path);
SaveWorld(AssetData, PackageName, Map.Path, TileName);
// Remove spawned actors from world to keep equal as BaseMap
DestroySpawnedActorsInWorld(SpawnedActors);
++i;
}
}
while (Res);
++j;
}
while (i > 0);
UE_LOG(LogTemp, Log, TEXT("End cooking tiles"));
}
}
}

View File

@ -74,6 +74,10 @@ public:
/// structure.
void LoadWorld(FAssetData &AssetData);
/// Loads a UWorld object contained in Carla BaseTile into @a AssetData data
/// structure.
void LoadWorldTile(FAssetData &AssetData);
/// Spawns all the static meshes located in @a AssetsPaths inside the World.
/// There is an option to use Carla materials by setting @a bUseCarlaMaterials
/// to true, otherwise it will use RoadRunner materials.
@ -82,7 +86,9 @@ public:
/// @pre World is expected to be previously loaded
TArray<AStaticMeshActor *> SpawnMeshesToWorld(
const TArray<FString> &AssetsPaths,
bool bUseCarlaMaterials);
bool bUseCarlaMaterials,
int i = -1,
int j = -1);
/// Saves the current World, contained in @a AssetData, into @a DestPath
/// composed of @a PackageName and with @a WorldName.
@ -116,6 +122,9 @@ public:
/// in a destination path built from @a PackageName and @a MapDestPath.
void PreparePropsForCooking(FString &PackageName, const TArray<FString> &PropsPaths, FString &MapDestPath);
/// Return if there is any tile between the assets to cook
bool IsMapInTiles(const TArray<FString> &AssetsPaths);
public:
/// Main method and entry of the commandlet, taking as input parameters @a

View File

@ -56,12 +56,17 @@ def generate_json_package(folder, package_name, use_carla_materials):
# search for all .fbx and .xodr pair of files
maps = []
for root, _, filenames in os.walk(folder):
files = fnmatch.filter(filenames, "*.fbx")
files = fnmatch.filter(filenames, "*.xodr")
for file_name in files:
fbx = file_name[:-4]
# check if exist the .xodr file
if os.path.exists("%s/%s.xodr" % (root, fbx)):
maps.append([os.path.relpath(root, folder), fbx])
xodr = file_name[:-5]
# check if exist the .fbx file
if os.path.exists("%s/%s.fbx" % (root, xodr)):
maps.append([os.path.relpath(root, folder), xodr, ["%s.fbx" % xodr]])
else:
# check if exist the map by tiles
tiles = fnmatch.filter(filenames, "*_Tile_*.fbx")
if (len(tiles) > 0):
maps.append([os.path.relpath(root, folder), xodr, tiles])
# write the json
if (len(maps) > 0):
@ -70,12 +75,20 @@ def generate_json_package(folder, package_name, use_carla_materials):
for map_name in maps:
path = map_name[0].replace('\\', '/')
name = map_name[1]
json_maps.append({
tiles = map_name[2]
tiles = ["%s/%s" % (path, x) for x in tiles]
map_dict = {
'name': name,
'source': '%s/%s.fbx' % (path, name),
'xodr': '%s/%s.xodr' % (path, name),
'use_carla_materials': use_carla_materials
})
}
# check for only one 'source' or map in 'tiles'
if (len(tiles) == 1):
map_dict['source'] = tiles[0]
else:
map_dict['tiles'] = tiles
# write
json_maps.append(map_dict)
# build and write the .json
f = open("%s/%s.json" % (folder, package_name), "w")
my_json = {'maps': json_maps, 'props': []}
@ -194,8 +207,6 @@ def invoke_commandlet(name, arguments):
subprocess.call([full_command], shell=True)
def generate_import_setting_file(package_name, json_dirname, props, maps):
"""Creates the PROPS and MAPS import_setting.json file needed
as an argument for using the ImportAssets commandlet
@ -241,13 +252,16 @@ def generate_import_setting_file(package_name, json_dirname, props, maps):
for umap in maps:
maps_dest = "/" + "/".join(["Game", package_name, "Maps", umap["name"]])
file_names = [os.path.join(json_dirname, umap["source"])]
if "source" in umap:
tiles = [os.path.join(json_dirname, umap["source"])]
else:
tiles = ["%s" % (os.path.join(json_dirname, x)) for x in umap["tiles"]]
import_groups.append({
"ImportSettings": import_settings,
"FactoryName": "FbxFactory",
"DestinationPath": maps_dest,
"bReplaceExisting": "true",
"FileNames": file_names
"FileNames": tiles
})
fh.write(json.dumps({"ImportGroups": import_groups}))
@ -389,10 +403,11 @@ def import_assets_from_json_list(json_list):
build_binary_for_navigation(package_name, dirname, maps)
# We prepare only the maps for cooking after moving them. Props cooking will be done from Package.sh script.
prepare_maps_commandlet_for_cooking(package_name, only_prepare_maps=True)
if len(maps) > 0:
prepare_maps_commandlet_for_cooking(package_name, only_prepare_maps=True)
# We apply the carla materials to the imported maps
load_asset_materials_commandlet(package_name)
# We apply the carla materials to the imported maps
load_asset_materials_commandlet(package_name)
def load_asset_materials_commandlet(package_name):
@ -400,7 +415,6 @@ def load_asset_materials_commandlet(package_name):
commandlet_arguments = ["-PackageName=%s" % package_name]
invoke_commandlet(commandlet_name, commandlet_arguments)
def prepare_maps_commandlet_for_cooking(package_name, only_prepare_maps):
commandlet_name = "PrepareAssetsForCooking"
commandlet_arguments = ["-PackageName=%s" % package_name]
@ -427,58 +441,68 @@ def build_binary_for_navigation(package_name, dirname, maps):
# process each map
for umap in maps:
# get the sources for the map (single or tiles)
if ("source" in umap):
tiles = [umap["source"]]
elif ("tiles" in umap):
tiles = umap["tiles"]
else:
continue
# get the target name
target_name = umap["name"]
xodr_filename = os.path.basename(umap["xodr"])
# copy the XODR file into docker utils folder
if "xodr" in umap and umap["xodr"] and os.path.isfile(os.path.join(dirname, umap["xodr"])):
# Make sure the `.xodr` file have the same name than the `.umap`
xodr_path_source = os.path.abspath(os.path.join(dirname, umap["xodr"]))
xodr_name = '.'.join([target_name, "xodr"])
xodr_path_target = os.path.join(folder, xodr_name)
xodr_path_target = os.path.join(folder, xodr_filename)
# copy
print('Copying "' + xodr_path_source + '" to "' + xodr_path_target + '"')
shutil.copy2(xodr_path_source, xodr_path_target)
for tile in tiles:
# copy the FBX file into docker utils folder
if "source" in umap and umap["source"] and os.path.isfile(os.path.join(dirname, umap["source"])):
# Make sure the `.fbx` file have the same name than the `.umap`
fbx_path_source = os.path.abspath(os.path.join(dirname, umap["source"]))
fbx_name = '.'.join([target_name, "fbx"])
fbx_path_target = os.path.join(folder, fbx_name)
# copy
print('Copying "' + fbx_path_source + '" to "' + fbx_path_target + '"')
shutil.copy2(fbx_path_source, fbx_path_target)
fbx_filename = os.path.basename(tile)
fbx_name_no_ext = os.path.splitext(fbx_filename)[0]
# make the conversion
if os.name == "nt":
subprocess.call(["build.bat", target_name], cwd=folder, shell=True)
else:
subprocess.call(["chmod +x build.sh"], cwd=folder, shell=True)
subprocess.call("./build.sh %s" % target_name, cwd=folder, shell=True)
# copy the FBX file into docker utils folder
if os.path.isfile(os.path.join(dirname, tile)):
# Make sure the `.fbx` file have the same name than the `.umap`
fbx_path_source = os.path.abspath(os.path.join(dirname, tile))
fbx_path_target = os.path.join(folder, fbx_filename)
# copy
print('Copying "' + fbx_path_source + '" to "' + fbx_path_target + '"')
shutil.copy2(fbx_path_source, fbx_path_target)
# copy the binary file
nav_folder_target = os.path.join(
CARLA_ROOT_PATH,
"Unreal",
"CarlaUE4",
"Content",
package_name,
"Maps",
target_name,
"Nav")
# rename the xodr with the same name of the source/tile
os.rename(os.path.join(folder, xodr_filename), os.path.join(folder, "%s.xodr" % fbx_name_no_ext))
nav_path_source = os.path.join(folder, "%s.bin" % target_name)
if os.path.exists(nav_path_source):
# Skip this step for maps that do not use ped navmesh
if not os.path.exists(nav_folder_target):
os.makedirs(nav_folder_target)
nav_path_target = os.path.join(nav_folder_target, "%s.bin" % target_name)
print('Copying "' + nav_path_source + '" to "' + nav_path_target + '"')
shutil.copy2(nav_path_source, nav_path_target)
# make the conversion
if os.name == "nt":
subprocess.call(["build.bat", fbx_name_no_ext], cwd=folder, shell=True)
else:
subprocess.call(["chmod +x build.sh"], cwd=folder, shell=True)
subprocess.call("./build.sh %s" % fbx_name_no_ext, cwd=folder, shell=True)
# rename the xodr with the original name
os.rename(os.path.join(folder, "%s.xodr" % fbx_name_no_ext), os.path.join(folder, xodr_filename))
# copy the binary file
nav_path_source = os.path.join(folder, "%s.bin" % fbx_name_no_ext)
nav_folder_target = os.path.join(CARLA_ROOT_PATH, "Unreal", "CarlaUE4", "Content", package_name, "Maps", target_name, "Nav")
if os.path.exists(nav_path_source):
if not os.path.exists(nav_folder_target):
os.makedirs(nav_folder_target)
nav_path_target = os.path.join(nav_folder_target, "%s.bin" % fbx_name_no_ext)
print('Copying "' + nav_path_source + '" to "' + nav_path_target + '"')
shutil.copy2(nav_path_source, nav_path_target)
# remove files
os.remove(nav_path_source)
os.remove(fbx_path_target)
# remove files
os.remove(fbx_path_target)
os.remove(xodr_path_target)
def main():