Working on new commandlet that unifies importing of props and maps

This commit is contained in:
Manish 2019-06-20 18:32:56 +02:00 committed by Néstor Subirón
parent 808e2c2e12
commit 8a2a86bb32
3 changed files with 388 additions and 30 deletions

View File

@ -0,0 +1,229 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "CookAssetsCommandlet.h"
#include "GameFramework/WorldSettings.h"
#include "UObject/MetaData.h"
// #include "CommandletPluginPrivate.h"
UCookAssetsCommandlet::UCookAssetsCommandlet()
{
IsClient = false;
IsEditor = true;
IsServer = false;
LogToConsole = true;
// #endif
}
// #if WITH_EDITORONLY_DATA
FPackageParams UCookAssetsCommandlet::ParseParams(const FString &InParams) const
{
TArray<FString> Tokens;
TArray<FString> Params;
TMap<FString, FString> ParamVals;
ParseCommandLine(*InParams, Tokens, Params);
FPackageParams PackageParams;
FParse::Value(*InParams, TEXT("PackageName="), PackageParams.Name);
PackageParams.bUseCarlaMapMaterials = Params.Contains(TEXT("use-carla-map-materials"));
return PackageParams;
}
void UCookAssetsCommandlet::MoveMeshesToMap(const FString &SrcPath, const TArray<FString> &DestPath)
{}
void UCookAssetsCommandlet::LoadWorld(FAssetData &AssetData)
{
FString BaseMap = TEXT("/Game/Carla/Maps/BaseMap");
AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor);
AssetsObjectLibrary->AddToRoot();
AssetsObjectLibrary->LoadAssetDataFromPath(*BaseMap);
AssetsObjectLibrary->LoadAssetsFromAssetData();
AssetsObjectLibrary->GetAssetDataList(AssetDatas);
if (AssetDatas.Num() > 0)
{
AssetData = AssetDatas.Pop();
}
}
void UCookAssetsCommandlet::AddMeshesToWorld(
const TArray<FString> &AssetsPaths,
bool bUseCarlaMaterials)
{
AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor);
AssetsObjectLibrary->AddToRoot();
AssetsObjectLibrary->LoadAssetDataFromPaths(AssetsPaths);
AssetsObjectLibrary->LoadAssetsFromAssetData();
const FTransform zeroTransform = FTransform();
FVector initialVector = FVector(0, 0, 0);
FRotator initialRotator = FRotator(0, 180, 0);
FActorSpawnParameters SpawnInfo;
MapContents.Empty();
AssetsObjectLibrary->GetAssetDataList(MapContents);
UStaticMesh *MeshAsset;
AStaticMeshActor *MeshActor;
for (auto MapAsset : MapContents)
{
UE_LOG(LogTemp, Log, TEXT("Add Assets"));
MeshAsset = CastChecked<UStaticMesh>(MapAsset.GetAsset());
MeshActor = World->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(),
initialVector,
initialRotator);
MeshActor->GetStaticMeshComponent()->SetStaticMesh(CastChecked<UStaticMesh>(MeshAsset));
if (bUseCarlaMaterials)
{
// FString AssetName;
// MapAsset.AssetName.ToString(AssetName);
// if (AssetName.Contains("MarkingNode"))
// {
// MeshActor->GetStaticMeshComponent()->SetMaterial(0,
// MarkingNodeMaterial);
// MeshActor->GetStaticMeshComponent()->SetMaterial(1,
// MarkingNodeMaterialAux);
// }
// else if (AssetName.Contains("RoadNode"))
// {
// MeshActor->GetStaticMeshComponent()->SetMaterial(0,
// RoadNodeMaterial);
// } else if (AssetName.Contains("Terrain"))
// {
// MeshActor->GetStaticMeshComponent()->SetMaterial(0,
// TerrainNodeMaterial);
// }
}
}
World->MarkPackageDirty();
}
bool UCookAssetsCommandlet::SaveWorld(FAssetData &AssetData, FString &DestPath, FString &WorldName)
{
FString PackageName = DestPath + "/" + WorldName;
UPackage *Package = AssetData.GetPackage();
Package->SetFolderName(*DestPath);
Package->FullyLoad();
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(World);
// Renaming stuff for the map
World->Rename(*WorldName, World->GetOuter());
FAssetRegistryModule::AssetRenamed(World, *PackageName);
World->MarkPackageDirty();
World->GetOuter()->MarkPackageDirty();
// Filling the map stuff (Code only applied for maps)
// AOpenDriveActor *OpenWorldActor =
//
// CastChecked<AOpenDriveActor>(World->SpawnActor(AOpenDriveActor::StaticClass(),
// new FVector(), NULL));
// OpenWorldActor->BuildRoutes(WorldName);
// OpenWorldActor->AddSpawners();
// Saving the package
FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName,
FPackageName::GetMapPackageExtension());
return UPackage::SavePackage(Package, World, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*PackageFileName, GError, nullptr, true, true, SAVE_NoError);
}
FAssetsPaths UCookAssetsCommandlet::GetAssetsPathFromPackage(const FString &PackageName) const
{
FString PackageConfigPath = FPaths::ProjectContentDir() + PackageName + TEXT("/Config/");
FString PackageJsonFilePath = PackageConfigPath + PackageName + TEXT(".Package.json");
FAssetsPaths AssetsPaths;
// Get All Maps Path
FString MapsFileJsonContent;
if (FFileHelper::LoadFileToString(MapsFileJsonContent, *PackageJsonFilePath))
{
TSharedPtr<FJsonObject> JsonParsed;
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(MapsFileJsonContent);
if (FJsonSerializer::Deserialize(JsonReader, JsonParsed))
{
// Add Maps Path
auto MapsJsonArray = JsonParsed->GetArrayField(TEXT("maps"));
for (auto &MapJsonValue : MapsJsonArray)
{
TSharedPtr<FJsonObject> MapJsonObject = MapJsonValue->AsObject();
FString MapAssetPath = MapJsonObject->GetStringField(TEXT("path"));
bool bUseCarlaMaterials = MapJsonObject->GetBoolField(TEXT("use_carla_materials"));
// AssetsPaths.MapsPaths.Add(TPair<FString, bool>(MapAssetPath,
// bUseCarlaMaterials));
}
// Add Props Path
auto PropJsonArray = JsonParsed->GetArrayField(TEXT("props"));
for (auto &PropJsonValue : PropJsonArray)
{
TSharedPtr<FJsonObject> PropJsonObject = PropJsonValue->AsObject();
FString PropAssetPath = PropJsonObject->GetStringField(TEXT("path"));
AssetsPaths.PropsPaths.Add(PropAssetPath);
}
}
}
return AssetsPaths;
}
int32 UCookAssetsCommandlet::Main(const FString &Params)
{
FPackageParams PackageParams = ParseParams(Params);
UE_LOG(LogTemp, Log, TEXT("Call to Cook Assets Commandlet"));
UE_LOG(LogTemp, Log, TEXT("Package Name: %s"), *PackageParams.Name);
// Get Props and Maps Path
UE_LOG(LogTemp, Log, TEXT("------ GET ASSETS PATH ------"));
FAssetsPaths AssetsPaths = GetAssetsPathFromPackage(PackageParams.Name);
// Load World
UE_LOG(LogTemp, Log, TEXT("------ ADDING MAPS ------"));
FString RoadsPath = TEXT("/Game/Carla/Static/Road/") + PackageParams.Name;
FString MarkingLinePath = TEXT("/Game/Carla/Static/RoadLines/") + PackageParams.Name;
FString TerrainPath = TEXT("/Game/Carla/Static/Terrain/") + PackageParams.Name;
TArray<FString> DataPath = {RoadsPath, MarkingLinePath, TerrainPath};
// DataPath.Add(RoadsPath);
// DataPath.Add(MarkingLinePath);
// DataPath.Add(TerrainPath);
UE_LOG(LogTemp, Log, TEXT("------ ADDING MAPS TO WORLD ------"));
// for (auto Map : AssetsPaths.MapsPaths) {
// FAssetData AssetData;
// LoadWorld(AssetData);
// World = CastChecked<UWorld>(AssetData.GetAsset());
// AddMeshesToWorld(Map.Key, Map.Value);
// }
UE_LOG(LogTemp, Log, TEXT("------ ADDING PROPS TO WORLD ------"));
FAssetData AssetData;
LoadWorld(AssetData);
World = CastChecked<UWorld>(AssetData.GetAsset());
// Add props in a single base map
AddMeshesToWorld(AssetsPaths.PropsPaths, false);
UE_LOG(LogTemp, Log, TEXT("------ SAVING BASEMAP WORLD ------"));
FString WorldDestPath = TEXT("/Game/") + PackageParams.Name + "/Maps/MapName";
FString MapName("MapName");
SaveWorld(AssetData, WorldDestPath, MapName);
return 0;
}
// #endif

View File

@ -0,0 +1,123 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Commandlets/Commandlet.h"
#include <Engine/World.h>
#include <UObject/Package.h>
#include <Misc/PackageName.h>
#include "CoreMinimal.h"
#include <Runtime/Engine/Classes/Engine/ObjectLibrary.h>
#include "Carla/OpenDrive/OpenDriveActor.h"
#include "Containers/Map.h"
#if WITH_EDITORONLY_DATA
#include <Developer/AssetTools/Public/IAssetTools.h>
#include <Developer/AssetTools/Public/AssetToolsModule.h>
#include <AssetRegistry/Public/AssetRegistryModule.h>
#endif // WITH_EDITORONLY_DATA
#include <Runtime/Engine/Classes/Engine/StaticMeshActor.h>
#include "CookAssetsCommandlet.generated.h"
USTRUCT()
struct CARLA_API FPackageParams
{
GENERATED_USTRUCT_BODY()
FString Name;
bool bUseCarlaMapMaterials;
};
USTRUCT()
struct CARLA_API FAssetsPaths
{
GENERATED_USTRUCT_BODY()
TPair<TArray<FString>, bool> MapsPaths;
TArray<FString> PropsPaths;
};
UCLASS()
class UCookAssetsCommandlet
: public UCommandlet
{
GENERATED_BODY()
public:
/** Default constructor. */
UCookAssetsCommandlet();
#if WITH_EDITORONLY_DATA
/**
* Parses the command line parameters
* @param InParams - The parameters to parse
*/
FPackageParams ParseParams(const FString &InParams) const;
/**
* Move meshes from one folder to another. It only works with StaticMeshes
* @param SrcPath - Source folder from which the StaticMeshes will be obtained
* @param DestPath - Posible folder in which the Meshes will be ordered
*following
* the semantic segmentation. It follows ROAD_INDEX, MARKINGLINE_INDEX,
*TERRAIN_INDEX
* for the position in which each path will be stored.
*/
void MoveMeshesToMap(const FString &SrcPath, const TArray<FString> &DestPath);
/**
* Loads a UWorld object from a given path into a asset data structure.
* @param AssetData - Structure in which the loaded UWorld will be saved.
*/
void LoadWorld(FAssetData &AssetData);
/**
* Add StaticMeshes from a folder into the World loaded as UPROPERTY.
* @param SrcPath - Array containing the folders from which the Assets will be
*loaded
* @param bMaterialWorkaround - Flag that will trigger a change in the
*materials to fix a known bug
* in RoadRunner.
*/
void AddMeshesToWorld(const TArray<FString> &AssetsPaths, bool bUseCarlaMaterials);
/**
* Save a given Asset containing a World into a given path with a given name.
* @param AssetData - Contains all the info about the World to be saved
* @param DestPath - Path in which the asset will be saved.
* @param WorldName - Name for the saved world.
*/
bool SaveWorld(FAssetData &AssetData, FString &DestPath, FString &WorldName);
/**
* Get Path of all the Assets contained in the package to cook
* @param PackageName - The name of the package to cook
*/
FAssetsPaths GetAssetsPathFromPackage(const FString &PackageName) const;
public:
/**
* Main method and entry of the commandlet
* @param Params - Parameters of the commandlet.
*/
virtual int32 Main(const FString &Params) override;
#endif // WITH_EDITORONLY_DATA
private:
UPROPERTY()
UObjectLibrary *AssetsObjectLibrary;
UPROPERTY()
TArray<FAssetData> AssetDatas;
UPROPERTY()
UWorld *World;
UPROPERTY()
TArray<FAssetData> MapContents;
};

View File

@ -14,6 +14,9 @@ import json
import ntpath
import subprocess
# Global variables
IMPORT_SETTING_FILENAME = "importsetting.json"
# Returns a list with the paths of each package's json files that has been found recursively in the input folder
def get_packages_json_list(folder):
json_files = []
@ -38,7 +41,7 @@ def invoke_commandlet(name, arguments):
# Creates the PROPS and MAPS import_setting.json file needed as an argument for using the ImportAssets commandlet
def generate_import_setting_file(package_name, json_dirname, props, maps):
importfile = os.path.join(os.getcwd(), "importsetting.json")
importfile = os.path.join(os.getcwd(), IMPORT_SETTING_FILENAME)
if os.path.exists(importfile):
os.remove(importfile)
@ -93,45 +96,44 @@ def generate_import_setting_file(package_name, json_dirname, props, maps):
fh.close()
return importfile
# Creates the PackageName.Props.json file for the package
def generate_prop_file(package_name, props):
# Creates the PackageName.Package.json file for the package
def generate_package_file(package_name, props, maps):
output_json = {}
output_json["props"] = []
for prop in props:
name = prop["name"]
size = prop["size"]
fbx_name = ntpath.basename(prop["source"]).replace(".fbx", "")
path = os.path.join("Game", package_name, "Static", prop["tag"], prop["name"], fbx_name)
path = "/" + "/".join(["Game", package_name, "Static", prop["tag"], prop["name"]])
output_json["props"].append({
"name": name,
"path": path,
"size": size,
"path": path
})
prop_file_path = os.path.join(os.getcwd(), "..", "Unreal", "CarlaUE4", "Content", package_name, "Config")
if not os.path.exists(prop_file_path):
try:
os.makedirs(prop_file_path)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
with open(os.path.join(prop_file_path, package_name + ".Props.json"), 'w+') as fh:
json.dump(output_json, fh)
# Creates the PackageName.Package.json file for the package
def generate_package_file(package_name, props):
output_json = {}
output_json["maps"] = []
output_json["props"] = []
for map in maps:
fbx_name = ntpath.basename(map["source"]).replace(".fbx", "")
path = "/" + "/".join(["Game", package_name, "Maps", map["name"], fbx_name])
use_carla_materials = map["use_carla_materials"] if "use_carla_materials" in map else False
output_json["maps"].append( {
"name" : map["name"],
"path" : path,
"use_carla_materials": use_carla_materials
})
prop_json_dir = os.path.join(os.getcwd(), "..", "Unreal", "CarlaUE4", "Content", package_name, "Config")
package_config_path = os.path.join(os.getcwd(), "..", "Unreal", "CarlaUE4", "Content", package_name, "Config")
if not os.path.exists(package_config_path):
try:
os.makedirs(package_config_path)
except OSError as exc:
if exc.errno != errno.EEXISTS:
raise
output_json["props"].append(os.path.join(prop_json_dir, package_name + ".Props.json"))
with open(os.path.join(prop_json_dir, package_name + ".Package.json"), 'w+') as fh:
with open(os.path.join(package_config_path, package_name + ".Package.json"), "w+") as fh:
json.dump(output_json, fh)
@ -142,16 +144,13 @@ def import_assets(package_name, json_dirname, props, maps):
# Import Props
import_setting_file = generate_import_setting_file(package_name, json_dirname, props, maps)
commandlet_arguments = "-importSettings=\"%s\" -AllowCommandletRendering -nosourcecontrol -replaceexisting" % import_setting_file
# invoke_commandlet(commandlet_name, commandlet_arguments)
invoke_commandlet(commandlet_name, commandlet_arguments)
os.remove(import_setting_file)
# TODO: Move maps XODR files if any
# Create files
generate_prop_file (package_name, props)
generate_package_file (package_name, props)
# Clean up
# Create package file
generate_package_file (package_name, props, maps)
def import_assets_from_json_list(json_list):
@ -167,6 +166,13 @@ def import_assets_from_json_list(json_list):
import_assets(package_name, dirname, props, maps)
prepare_cook_commandlet(package_name)
def prepare_cook_commandlet(package_name):
commandlet_name = "CookAssets"
commandlet_arguments = "-PackageName=%s" % package_name
invoke_commandlet(commandlet_name, commandlet_arguments)
def main():
import_folder = os.path.join(os.getcwd(), "..", "Import")