diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.cpp new file mode 100644 index 000000000..6cc0b2b50 --- /dev/null +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.cpp @@ -0,0 +1,141 @@ +// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. + + +#include "FBXExporterPreparatorCommandlet.h" +#include "GameFramework/WorldSettings.h" +#include "UObject/MetaData.h" +//#include "CommandletPluginPrivate.h" + +UFBXExporterPreparatorCommandlet::UFBXExporterPreparatorCommandlet() +{ + IsClient = false; + IsEditor = false; + IsServer = false; + LogToConsole = true; +} +#if WITH_EDITORONLY_DATA + +bool UFBXExporterPreparatorCommandlet::ParseParams(const FString& InParams) +{ + Params = InParams; + ParseCommandLine(*Params, Tokens, Switches); + + return true; +} + +void UFBXExporterPreparatorCommandlet::LoadWorld(FAssetData &AssetData) +{ + FString SeedMap; + FParse::Value( *Params, TEXT("SourceMap="), SeedMap); + UE_LOG(LogTemp, Display, TEXT("DANIEL: %s"), *SeedMap); + + MapObjectLibrary = UObjectLibrary::CreateLibrary(UWorld::StaticClass(), false, GIsEditor); + MapObjectLibrary->AddToRoot(); + MapObjectLibrary->LoadAssetDataFromPath(*SeedMap); + MapObjectLibrary->LoadAssetsFromAssetData(); + MapObjectLibrary->GetAssetDataList(AssetDatas); + + if (AssetDatas.Num() > 0) + { + AssetData = AssetDatas.Pop(); + } +} + +void UFBXExporterPreparatorCommandlet::AddMeshesToWorld() +{ + TArray CmdLineDirEntries; + + const FString CookDirPrefix = TEXT("MESHESDIR="); + for (int32 SwitchIdx = 0; SwitchIdx < Switches.Num(); SwitchIdx++) + { + const FString& Switch = Switches[SwitchIdx]; + if (Switch.StartsWith(CookDirPrefix)) + { + FString DirToCook = Switch.Right(Switch.Len() - 10); + + // Allow support for -COOKDIR=Dir1+Dir2+Dir3 as well as -COOKDIR=Dir1 -COOKDIR=Dir2 + for (int32 PlusIdx = DirToCook.Find(TEXT("+")); PlusIdx != INDEX_NONE; PlusIdx = DirToCook.Find(TEXT("+"))) + { + FString DirName = DirToCook.Left(PlusIdx); + + // The dir may be contained within quotes + DirName = DirName.TrimQuotes(); + FPaths::NormalizeDirectoryName(DirName); + CmdLineDirEntries.Add(DirName); + + DirToCook = DirToCook.Right(DirToCook.Len() - (PlusIdx + 1)); + DirToCook = DirToCook.TrimQuotes(); + UE_LOG(LogTemp, Display, TEXT("DANIEL: %s"), *DirName); + } + + // The dir may be contained within quotes + + } + } + + AssetsObjectLibrary = UObjectLibrary::CreateLibrary(UStaticMesh::StaticClass(), false, GIsEditor); + AssetsObjectLibrary->AddToRoot(); + AssetsObjectLibrary->LoadAssetDataFromPaths(CmdLineDirEntries); + 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) + { + MeshAsset = CastChecked(MapAsset.GetAsset()); + MeshActor = World->SpawnActor(AStaticMeshActor::StaticClass(), + initialVector, + initialRotator); + MeshActor->GetStaticMeshComponent()->SetStaticMesh(CastChecked(MeshAsset)); + } + World->MarkPackageDirty(); + +} + +bool UFBXExporterPreparatorCommandlet::SaveWorld(FAssetData &AssetData) +{ + FString DestPath; + FParse::Value( *Params, TEXT("DestMapPath="), DestPath); + FString WorldName; + FParse::Value( *Params, TEXT("DestMapName="), 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(); + + // 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); +} + +int32 UFBXExporterPreparatorCommandlet::Main(const FString &ParamsIn) +{ + ParseParams(ParamsIn); + FAssetData AssetData; + LoadWorld(AssetData); + World = CastChecked(AssetData.GetAsset()); + AddMeshesToWorld(); + SaveWorld(AssetData); + return 0; +} + +#endif \ No newline at end of file diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.h new file mode 100644 index 000000000..8117d4e32 --- /dev/null +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/FBXImport/FBXExporterPreparatorCommandlet.h @@ -0,0 +1,107 @@ +// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "Commandlets/Commandlet.h" +#include +#include +#include +#include "CoreMinimal.h" +#include +#include +#if WITH_EDITORONLY_DATA +#include +#include +#include +#endif //WITH_EDITORONLY_DATA +#include +#include "FBXExporterPreparatorCommandlet.generated.h" + +UCLASS() +class UFBXExporterPreparatorCommandlet + : public UCommandlet +{ + GENERATED_BODY() + +public: + /** Default constructor. */ + UFBXExporterPreparatorCommandlet(); + #if WITH_EDITORONLY_DATA + + /** + * Parses the command line parameters + * @param InParams - The parameters to parse + */ + bool ParseParams(const FString& InParams); + + /** + * 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 MoveMeshes(const FString &SrcPath, const TArray &DestPath); + + /** + * Loads a UWorld object from a given path into a asset data structure. + * @param SrcPath - Folder in which the world is located. + * @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(); + + /** + * 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); + +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() + bool bOverrideMaterials; + + //UProperties are necesary or else the GC will eat everything up + UPROPERTY() + FString MapName; + + UPROPERTY() + UObjectLibrary* MapObjectLibrary; + + UPROPERTY() + TArray AssetDatas; + + UPROPERTY() + UWorld* World; + + UPROPERTY() + UObjectLibrary* AssetsObjectLibrary; + + UPROPERTY() + TArray MapContents; + + /** All commandline tokens */ + TArray Tokens; + /** All commandline switches */ + TArray Switches; + /** All commandline params */ + FString Params; +}; diff --git a/Util/BuildTools/ExportFBX.sh b/Util/BuildTools/ExportFBX.sh new file mode 100755 index 000000000..7ea377138 --- /dev/null +++ b/Util/BuildTools/ExportFBX.sh @@ -0,0 +1,125 @@ +#! /bin/bash + +# ============================================================================== +# -- Set up environment -------------------------------------------------------- +# ============================================================================== + +source $(dirname "$0")/Environment.sh + +if [ ! -d "${UE4_ROOT}" ]; then + fatal_error "UE4_ROOT is not defined, or points to a non-existant directory, please set this environment variable." +else + log "Using Unreal Engine at '$UE4_ROOT'" +fi + +# ============================================================================== +# -- Parse arguments ----------------------------------------------------------- +# ============================================================================== + +DOC_STRING="Build and packs CarlaUE4's ExportedMaps" + +USAGE_STRING="Usage: $0 [--help] [-d|--dir] [-f|--file] [-p|--maps] " + +OUTPUT_DIRECTORY="" +FILE_NAME="" +PATHS_TO_COOK="" + +OPTS=`getopt -o h --long help,dir:,file:,maps: -n 'parse-options' -- "$@"` + +if [ $? != 0 ] ; then echo "$USAGE_STRING" ; exit 2; fi + +eval set -- "$OPTS" + +while true; do + case "$1" in + --dir ) + OUTPUT_DIRECTORY="$2" + shift 2;; + --file ) + FILE_NAME="$2" + shift 2;; + --maps ) + MAP_TO_COOK="$2" + shift 2;; + --help ) + echo "$DOC_STRING" + echo "$USAGE_STRING" + exit 1 + ;; + * ) + break ;; + esac +done + +if [ -z "${OUTPUT_DIRECTORY}" ]; then + OUTPUT_DIRECTORY="${PWD}/ExportedMaps" +fi +if [ -z "${FILE_NAME}" ]; then + FILE_NAME="CookedExportedMaps" +fi + + + +# ============================================================================== +# -- Package project ----------------------------------------------------------- +# ============================================================================== + +REPOSITORY_TAG=$(get_git_repository_version) + +BUILD_FOLDER=${OUTPUT_DIRECTORY} + +log "Packaging user content from version '$REPOSITORY_TAG'." + +#rm -Rf ${BUILD_FOLDER} +mkdir -p ${BUILD_FOLDER} + +pushd "${CARLAUE4_ROOT_FOLDER}" > /dev/null + +log "Current project directory: '${PWD}'" + +${UE4_ROOT}/Engine/Binaries/Linux/UE4Editor "${PWD}/CarlaUE4.uproject" -run=cook -cooksinglepackage -Map=${MAP_TO_COOK} -targetplatform="LinuxNoEditor" -OutputDir="${BUILD_FOLDER}/Cooked" + +popd >/dev/null + + +# ============================================================================== +# -- Zip the project ----------------------------------------------------------- +# ============================================================================== + +DESTINATION=${BUILD_FOLDER}/${FILE_NAME}.tar.gz +SOURCE=${BUILD_FOLDER}/Cooked + +pushd "${SOURCE}" >/dev/null + +log "Packaging build." + +#rm -Rf ./Engine +rm -Rf ./CarlaUE4/Metadata +rm -Rf ./CarlaUE4/Plugins +#@TODO: Instead of this hardcoded path, we need to pick the ${MAPS_TO_COOK} path +#rm -Rf ./CarlaUE4/${MAPS_TO_COOK_But_instead_of_"game"_the_folder_is_content}.uexp +#rm -Rf ./CarlaUE4/${MAPS_TO_COOK_But_instead_of_"game"_the_folder_is_content}.umap +#rm -Rf ./CarlaUE4/Content/Carla/Maps/BaseMap/TEMPMAP.uexp +#rm -Rf ./CarlaUE4/Content/Carla/Maps/BaseMap/TEMPMAP.umap + +rm ./CarlaUE4/AssetRegistry.bin + +tar -czvf ${DESTINATION} * + +popd > /dev/null + +# ============================================================================== +# -- Remove intermediate files and return everything to normal------------------ +# ============================================================================== + + +log "Removing intermediate build." + +rm -Rf ${BUILD_FOLDER}/Cooked + +# ============================================================================== +# -- ...and we are done -------------------------------------------------------- +# ============================================================================== + +log "ExportedMaps created at ${DESTINATION}" +log "Success!" diff --git a/Util/import_fbx.py b/Util/import_fbx.py index 754c03827..e73646352 100755 --- a/Util/import_fbx.py +++ b/Util/import_fbx.py @@ -13,6 +13,7 @@ import json import subprocess import shutil import argparse +import re if os.name == 'nt': @@ -24,20 +25,38 @@ elif os.name == 'posix': def main(): try: args = parse_arguments() - import_all_fbx_in_folder(args.folder) + folder_list = [] + import_all_fbx_in_folder(args.folder, folder_list) finally: print('\ndone.') -def import_all_fbx_in_folder(fbx_folder): +def import_all_fbx_in_folder(fbx_folder, folder_list): dirname = os.getcwd() fbx_place = os.path.join(dirname, "..", fbx_folder) for file in os.listdir(fbx_place): if file.endswith(".PropRegistry.json"): with open(os.path.join(dirname, "..", fbx_folder, file)) as json_file: data = json.load(json_file) - import_assets_commandlet(data, fbx_folder) - + # This will take all the fbx registerd in the provided json files + # and place it inside unreal in the provided path (by the json file) + import_assets_commandlet(data, fbx_folder, folder_list) + # This part will just gather all the folders in which an fbx has been placed + if(len(folder_list)>0): + final_list = "" + for folder in folder_list: + if folder not in final_list: + final_list += folder + "+" + final_list = final_list[:-1] + print(final_list) + #Destination map (the one that will be cooked) + dest_map_path = "/Game/Carla/Maps/BaseMap" + dest_map_name = "TEMPMAP" + # This should be a folder, because the commandlet will take anything inside. + # It is better if there is only one map inside + src_map_folder = "/Game/Carla/Maps/EmptyMap" + prepare_cook_commandlet(final_list, src_map_folder, dest_map_path, dest_map_name) + launch_bash_script("BuildTools/ExportFBX.sh", "--maps=%s/%s" % (dest_map_path, dest_map_name)) def parse_arguments(): argparser = argparse.ArgumentParser( @@ -50,12 +69,17 @@ def parse_arguments(): help='FBX containing folder') return argparser.parse_args() +def prepare_cook_commandlet(folder_list, source_map, dest_map_path, dest_map_name): + commandlet_name = "FBXExporterPreparator" + commandlet_arguments = "-MeshesDir=%s -SourceMap=%s -DestMapPath=%s -DestMapName=%s" % (folder_list, source_map, dest_map_path, dest_map_name) + invoke_commandlet(commandlet_name, commandlet_arguments) -def import_assets_commandlet(json_data, fbx_folder): + +def import_assets_commandlet(json_data, fbx_folder, folder_list): importfile = "importsetting.json" if os.path.exists(importfile): os.remove(importfile) - generate_json(json_data, fbx_folder, importfile) + populate_json_and_data(json_data, fbx_folder, importfile, folder_list) dirname = os.getcwd() commandlet_name = "ImportAssets" import_settings = os.path.join(dirname, importfile) @@ -73,8 +97,13 @@ def invoke_commandlet(name, arguments): full_command = "%s %s -run=%s %s" % (editor_path, uproject_path, name, arguments) subprocess.check_call([full_command], shell=True) +def launch_bash_script(script_path, arguments): + dirname = os.getcwd() + full_command = "%s %s" % (os.path.join(dirname, script_path), arguments) + print("Executing: " + full_command) + subprocess.check_call([full_command], shell=True) -def generate_json(json_data, fbx_folder, json_file): +def populate_json_and_data(json_data, fbx_folder, json_file, folder_list): with open(json_file, "w+") as fh: import_groups = [] file_names = [] @@ -104,12 +133,18 @@ def generate_json(json_data, fbx_folder, json_file): import_groups.append({ "ImportSettings": import_settings, "FactoryName": "FbxFactory", - "DestinationPath": os.path.dirname(prop["path"]), + "DestinationPath": prop["path"], "bReplaceExisting": "true", "FileNames": file_names }) + #second_folder = os.path.dirname(prop["path"]).split("/Game")[1].split("/")[1] + #folder_list.append("/Game/" + second_folder) + folder_list.append(prop["path"]) fh.write(json.dumps({"ImportGroups": import_groups})) fh.close() + #result = re.search('/Game(.*)', os.path.dirname(prop["path"])) + + #print(result) if __name__ == '__main__':