Refactored the generation of UE projects, plugin is now being installed in the engine.
|
|
@ -17,7 +17,7 @@ class UnrealAddon(OpenPypeModule, IHostAddon):
|
|||
|
||||
ue_plugin = "UE_5.0" if app.name[:1] == "5" else "UE_4.7"
|
||||
unreal_plugin_path = os.path.join(
|
||||
UNREAL_ROOT_DIR, "integration", ue_plugin
|
||||
UNREAL_ROOT_DIR, "integration", ue_plugin, "OpenPype"
|
||||
)
|
||||
if not env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path
|
||||
|
|
|
|||
|
|
@ -119,29 +119,33 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
f"detected [ {engine_version} ]"
|
||||
))
|
||||
|
||||
ue_path = unreal_lib.get_editor_executable_path(
|
||||
ue_path = unreal_lib.get_editor_exe_path(
|
||||
Path(detected[engine_version]), engine_version)
|
||||
|
||||
self.launch_context.launch_args = [ue_path.as_posix()]
|
||||
project_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
|
||||
# execution of `create_unreal_project`
|
||||
if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
self.log.info((
|
||||
f"{self.signature} using OpenPype plugin from "
|
||||
f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}"
|
||||
))
|
||||
env_key = "OPENPYPE_UNREAL_PLUGIN"
|
||||
if self.launch_context.env.get(env_key):
|
||||
os.environ[env_key] = self.launch_context.env[env_key]
|
||||
|
||||
engine_path = detected[engine_version]
|
||||
|
||||
unreal_lib.try_installing_plugin(Path(engine_path), engine_version)
|
||||
|
||||
project_file = project_path / unreal_project_filename
|
||||
if not project_file.is_file():
|
||||
engine_path = detected[engine_version]
|
||||
self.log.info((
|
||||
f"{self.signature} creating unreal "
|
||||
f"project [ {unreal_project_name} ]"
|
||||
))
|
||||
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
|
||||
# execution of `create_unreal_project`
|
||||
if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
self.log.info((
|
||||
f"{self.signature} using OpenPype plugin from "
|
||||
f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}"
|
||||
))
|
||||
env_key = "OPENPYPE_UNREAL_PLUGIN"
|
||||
if self.launch_context.env.get(env_key):
|
||||
os.environ[env_key] = self.launch_context.env[env_key]
|
||||
|
||||
unreal_lib.create_unreal_project(
|
||||
unreal_project_name,
|
||||
|
|
|
|||
6
openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/Saved
|
||||
/DerivedDataCache
|
||||
/Intermediate
|
||||
/Binaries
|
||||
/.idea
|
||||
/.vs
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "4.27",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
GameDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default
|
||||
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Desktop
|
||||
AppliedTargetedHardwareClass=Desktop
|
||||
DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject")
|
||||
+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject")
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=95AED0BF45A918DF73ABB3BB27D25356
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
"DocsURL": "https://openpype.io/docs/artist_hosts_unreal",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://pype.club/",
|
||||
"EngineVersion": "4.27",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
|
@ -6,8 +6,8 @@ public class OpenPype : ModuleRules
|
|||
{
|
||||
public OpenPype(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
|
|
@ -34,6 +34,7 @@ public class OpenPype : ModuleRules
|
|||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"GameProjectGeneration",
|
||||
"Projects",
|
||||
"InputCore",
|
||||
"UnrealEd",
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h"
|
||||
|
||||
#include "Editor.h"
|
||||
#include "GameProjectUtils.h"
|
||||
#include "OPConstants.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
|
||||
int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams)
|
||||
{
|
||||
//Parses command line parameters & creates structure FProjectInformation
|
||||
const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams);
|
||||
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
|
||||
|
||||
//Creates .uproject & other UE files
|
||||
EVALUATE_OP_ACTION_RESULT(TryCreateProject());
|
||||
|
||||
//Loads created .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor());
|
||||
|
||||
//Adds needed plugin to .uproject
|
||||
AttachPluginsToProjectDescriptor();
|
||||
|
||||
//Saves .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TrySave());
|
||||
|
||||
//When we are here, there should not be problems in generating Unreal Project for OpenPype
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("")
|
||||
{
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
|
||||
CommandLineParams)
|
||||
{
|
||||
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
|
||||
}
|
||||
|
||||
FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const
|
||||
{
|
||||
FProjectInformation ProjectInformation = FProjectInformation();
|
||||
ProjectInformation.ProjectFilename = GetProjectFileName();
|
||||
|
||||
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
|
||||
|
||||
return ProjectInformation;
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const
|
||||
{
|
||||
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::GetProjectFileName() const
|
||||
{
|
||||
return TryGetToken(0);
|
||||
}
|
||||
|
||||
bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
|
||||
{
|
||||
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
|
||||
{
|
||||
return Item.Equals(Switch);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet()
|
||||
{
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const
|
||||
{
|
||||
FText FailReason;
|
||||
FText FailLog;
|
||||
TArray<FString> OutCreatedFiles;
|
||||
|
||||
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
|
||||
return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason);
|
||||
return FOP_ActionResult();
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason);
|
||||
}
|
||||
|
||||
void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
|
||||
{
|
||||
FPluginReferenceDescriptor OPPluginDescriptor;
|
||||
OPPluginDescriptor.bEnabled = true;
|
||||
OPPluginDescriptor.Name = OPConstants::OP_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(OPPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor PythonPluginDescriptor;
|
||||
PythonPluginDescriptor.bEnabled = true;
|
||||
PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
|
||||
SequencerScriptingPluginDescriptor.bEnabled = true;
|
||||
SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
|
||||
MovieRenderPipelinePluginDescriptor.bEnabled = true;
|
||||
MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
|
||||
EditorScriptingPluginDescriptor.bEnabled = true;
|
||||
EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TrySave()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason);
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const
|
||||
{
|
||||
FOPGenerateProjectParams ParamsResult;
|
||||
|
||||
TArray<FString> Tokens, Switches;
|
||||
ParseCommandLine(*Params, Tokens, Switches);
|
||||
|
||||
return ParamsResult;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "Logging/OP_Log.h"
|
||||
|
||||
EOP_ActionResult::Type& FOP_ActionResult::GetStatus()
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
FText& FOP_ActionResult::GetReason()
|
||||
{
|
||||
return Reason;
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum)
|
||||
{
|
||||
TryLog();
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason)
|
||||
{
|
||||
TryLog();
|
||||
};
|
||||
|
||||
bool FOP_ActionResult::IsProblem() const
|
||||
{
|
||||
return Status != EOP_ActionResult::Ok;
|
||||
}
|
||||
|
||||
void FOP_ActionResult::TryLog() const
|
||||
{
|
||||
if(IsProblem())
|
||||
UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString());
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "Logging/OP_Log.h"
|
||||
|
|
@ -16,32 +16,34 @@ static const FName OpenPypeTabName("OpenPype");
|
|||
// This function is triggered when the plugin is staring up
|
||||
void FOpenPypeModule::StartupModule()
|
||||
{
|
||||
FOpenPypeStyle::Initialize();
|
||||
FOpenPypeStyle::SetIcon("Logo", "openpype40");
|
||||
if (!IsRunningCommandlet()) {
|
||||
FOpenPypeStyle::Initialize();
|
||||
FOpenPypeStyle::SetIcon("Logo", "openpype40");
|
||||
|
||||
// Create the Extender that will add content to the menu
|
||||
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||
// Create the Extender that will add content to the menu
|
||||
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||
|
||||
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender());
|
||||
|
||||
MenuExtender->AddMenuExtension(
|
||||
"LevelEditor",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry)
|
||||
);
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Settings",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry));
|
||||
MenuExtender->AddMenuExtension(
|
||||
"LevelEditor",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry)
|
||||
);
|
||||
ToolbarExtender->AddToolBarExtension(
|
||||
"Settings",
|
||||
EExtensionHook::After,
|
||||
NULL,
|
||||
FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry));
|
||||
|
||||
|
||||
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
|
||||
RegisterSettings();
|
||||
RegisterSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void FOpenPypeModule::ShutdownModule()
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
#include "OpenPypePublishInstance.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "NotificationManager.h"
|
||||
#include "OpenPypeLib.h"
|
||||
#include "OpenPypeSettings.h"
|
||||
#include "SNotificationList.h"
|
||||
#include "Framework/Notifications/NotificationManager.h"
|
||||
#include "Widgets/Notifications/SNotificationList.h"
|
||||
|
||||
//Moves all the invalid pointers to the end to prepare them for the shrinking
|
||||
#define REMOVE_INVALID_ENTRIES(VAR) VAR.CompactStable(); \
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include "OpenPypeSettings.h"
|
||||
|
||||
#include "IPluginManager.h"
|
||||
#include "UObjectGlobals.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
/**
|
||||
* Mainly is used for initializing default values if the DefaultOpenPypeSettings.ini file does not exist in the saved config
|
||||
|
|
@ -43,7 +43,7 @@ const FVector2D Icon40x40(40.0f, 40.0f);
|
|||
TUniquePtr< FSlateStyleSet > FOpenPypeStyle::Create()
|
||||
{
|
||||
TUniquePtr< FSlateStyleSet > Style = MakeUnique<FSlateStyleSet>(GetStyleSetName());
|
||||
Style->SetContentRoot(FPaths::ProjectPluginsDir() / TEXT("OpenPype/Resources"));
|
||||
Style->SetContentRoot(FPaths::EnginePluginsDir() / TEXT("Marketplace/OpenPype/Resources"));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
|
@ -66,5 +66,4 @@ const ISlateStyle& FOpenPypeStyle::Get()
|
|||
{
|
||||
check(OpenPypeStyleInstance);
|
||||
return *OpenPypeStyleInstance;
|
||||
return *OpenPypeStyleInstance;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "GameProjectUtils.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "OPGenerateProjectCommandlet.generated.h"
|
||||
|
||||
struct FProjectDescriptor;
|
||||
struct FProjectInformation;
|
||||
|
||||
/**
|
||||
* @brief Structure which parses command line parameters and generates FProjectInformation
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOPGenerateProjectParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FString CommandLineParams;
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
|
||||
public:
|
||||
FOPGenerateProjectParams();
|
||||
FOPGenerateProjectParams(const FString& CommandLineParams);
|
||||
|
||||
FProjectInformation GenerateUEProjectInformation() const;
|
||||
|
||||
private:
|
||||
FString TryGetToken(const int32 Index) const;
|
||||
FString GetProjectFileName() const;
|
||||
|
||||
bool IsSwitchPresent(const FString& Switch) const;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FProjectInformation ProjectInformation;
|
||||
FProjectDescriptor ProjectDescriptor;
|
||||
|
||||
public:
|
||||
UOPGenerateProjectCommandlet();
|
||||
|
||||
virtual int32 Main(const FString& CommandLineParams) override;
|
||||
|
||||
private:
|
||||
FOPGenerateProjectParams ParseParameters(const FString& Params) const;
|
||||
FOP_ActionResult TryCreateProject() const;
|
||||
FOP_ActionResult TryLoadProjectDescriptor();
|
||||
void AttachPluginsToProjectDescriptor();
|
||||
FOP_ActionResult TrySave();
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OPActionResult.generated.h"
|
||||
|
||||
/**
|
||||
* @brief This macro returns error code when is problem or does nothing when there is no problem.
|
||||
* @param ActionResult FOP_ActionResult structure
|
||||
*/
|
||||
#define EVALUATE_OP_ACTION_RESULT(ActionResult) \
|
||||
if(ActionResult.IsProblem()) \
|
||||
return ActionResult.GetStatus();
|
||||
|
||||
/**
|
||||
* @brief This enum values are humanly readable mapping of error codes.
|
||||
* Here should be all error codes to be possible find what went wrong.
|
||||
* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it...
|
||||
*/
|
||||
UENUM()
|
||||
namespace EOP_ActionResult
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Ok,
|
||||
ProjectNotCreated,
|
||||
ProjectNotLoaded,
|
||||
ProjectNotSaved,
|
||||
//....Here insert another values
|
||||
|
||||
//Do not remove!
|
||||
//Usable for looping through enum values
|
||||
__Last UMETA(Hidden)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief This struct holds action result enum and optionally reason of fail
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOP_ActionResult
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief Default constructor usable when there is no problem */
|
||||
FOP_ActionResult();
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum);
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
* @param InReason Reason of potential fail
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason);
|
||||
|
||||
private:
|
||||
/** @brief Action status */
|
||||
EOP_ActionResult::Type Status;
|
||||
|
||||
/** @brief Optional reason of fail */
|
||||
FText Reason;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if there is problematic state
|
||||
* @return true when status is not equal to EOP_ActionResult::Ok
|
||||
*/
|
||||
bool IsProblem() const;
|
||||
EOP_ActionResult::Type& GetStatus();
|
||||
FText& GetReason();
|
||||
|
||||
private:
|
||||
void TryLog() const;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace OPConstants
|
||||
{
|
||||
const FString OP_PluginName = "OpenPype";
|
||||
const FString PythonScript_PluginName = "PythonScriptPlugin";
|
||||
const FString SequencerScripting_PluginName = "SequencerScripting";
|
||||
const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline";
|
||||
const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ public:
|
|||
*
|
||||
* @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetInternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -33,7 +33,7 @@ public:
|
|||
*
|
||||
* @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetExternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -53,7 +53,7 @@ public:
|
|||
*
|
||||
* @attention If the bAddExternalAssets variable is false, external assets won't be included!
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetAllAssets() const
|
||||
{
|
||||
const TSet<TSoftObjectPtr<UObject>>& IteratedSet = bAddExternalAssets
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Object.h"
|
||||
#include "OpenPypeSettings.generated.h"
|
||||
|
||||
#define OPENPYPE_SETTINGS_FILEPATH IPluginManager::Get().FindPlugin("OpenPype")->GetBaseDir() / TEXT("Config") / TEXT("DefaultOpenPypeSettings.ini")
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "AssetContainer.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "Engine.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
|
||||
UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer)
|
||||
: UAssetUserData(ObjectInitializer)
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path);
|
||||
FARFilter Filter;
|
||||
Filter.PackagePaths.Add(FName(*path));
|
||||
|
||||
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded);
|
||||
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved);
|
||||
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed);
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
assets.Add(assetPath);
|
||||
assetsData.Add(AssetData);
|
||||
UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
FString lpp = FPackageName::GetLongPackagePath(*path);
|
||||
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp);
|
||||
assets.Remove(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClass.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
|
||||
assets.Remove(str);
|
||||
assets.Add(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#include "AssetContainerFactory.h"
|
||||
#include "AssetContainer.h"
|
||||
|
||||
UAssetContainerFactory::UAssetContainerFactory(const FObjectInitializer& ObjectInitializer)
|
||||
: UFactory(ObjectInitializer)
|
||||
{
|
||||
SupportedClass = UAssetContainer::StaticClass();
|
||||
bCreateNew = false;
|
||||
bEditorImport = true;
|
||||
}
|
||||
|
||||
UObject* UAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
UAssetContainer* AssetContainer = NewObject<UAssetContainer>(InParent, Class, Name, Flags);
|
||||
return AssetContainer;
|
||||
}
|
||||
|
||||
bool UAssetContainerFactory::ShouldShowInNewMenu() const {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Engine/AssetUserData.h"
|
||||
#include "AssetData.h"
|
||||
#include "AssetContainer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Blueprintable)
|
||||
class OPENPYPE_API UAssetContainer : public UAssetUserData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UAssetContainer(const FObjectInitializer& ObjectInitalizer);
|
||||
// ~UAssetContainer();
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TArray<FString> assets;
|
||||
|
||||
// There seems to be no reflection option to expose array of FAssetData
|
||||
/*
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data"))
|
||||
TArray<FAssetData> assetsData;
|
||||
*/
|
||||
private:
|
||||
TArray<FAssetData> assetsData;
|
||||
void OnAssetAdded(const FAssetData& AssetData);
|
||||
void OnAssetRemoved(const FAssetData& AssetData);
|
||||
void OnAssetRenamed(const FAssetData& AssetData, const FString& str);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "AssetContainerFactory.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class OPENPYPE_API UAssetContainerFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UAssetContainerFactory(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
virtual bool ShouldShowInNewMenu() const override;
|
||||
};
|
||||
6
openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/Saved
|
||||
/DerivedDataCache
|
||||
/Intermediate
|
||||
/Binaries
|
||||
/.idea
|
||||
/.vs
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "5.0",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ModelingToolsEditorMode",
|
||||
"Enabled": true,
|
||||
"TargetAllowList": [
|
||||
"Editor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
"Enabled": true,
|
||||
"Type": "Editor"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
GameDefaultMap=/Engine/Maps/Templates/OpenWorld
|
||||
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Desktop
|
||||
AppliedTargetedHardwareClass=Desktop
|
||||
DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.GenerateMeshDistanceFields=True
|
||||
r.DynamicGlobalIlluminationMethod=1
|
||||
r.ReflectionMethod=1
|
||||
r.Shadow.Virtual.Enable=1
|
||||
|
||||
[/Script/WorldPartitionEditor.WorldPartitionEditorSettings]
|
||||
CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet'
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject")
|
||||
+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject")
|
||||
|
||||
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
|
||||
bEnablePlugin=True
|
||||
bAllowNetworkConnection=True
|
||||
SecurityToken=684C16AF4BD96F1D6828A6B067693175
|
||||
bIncludeInShipping=False
|
||||
bAllowExternalStartInShipping=False
|
||||
bCompileAFSProject=False
|
||||
bUseCompression=False
|
||||
bLogFiles=False
|
||||
bReportStats=False
|
||||
ConnectionType=USBOnly
|
||||
bUseManualIPAddress=False
|
||||
ManualIPAddress=
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=D528076140C577E5807BA5BA135366BB
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://pype.club/",
|
||||
"CanContainContent": true,
|
||||
"EngineVersion": "5.0",
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
|
@ -10,7 +10,7 @@ public class OpenPype : ModuleRules
|
|||
bLegacyPublicIncludePaths = false;
|
||||
ShadowVariableWarningLevel = WarningLevel.Error;
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0;
|
||||
//IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
|
|
@ -30,14 +30,15 @@ public class OpenPype : ModuleRules
|
|||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"GameProjectGeneration",
|
||||
"Projects",
|
||||
"InputCore",
|
||||
"EditorFramework",
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h"
|
||||
|
||||
#include "Editor.h"
|
||||
#include "GameProjectUtils.h"
|
||||
#include "OPConstants.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
|
||||
int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams)
|
||||
{
|
||||
//Parses command line parameters & creates structure FProjectInformation
|
||||
const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams);
|
||||
ProjectInformation = ParsedParams.GenerateUEProjectInformation();
|
||||
|
||||
//Creates .uproject & other UE files
|
||||
EVALUATE_OP_ACTION_RESULT(TryCreateProject());
|
||||
|
||||
//Loads created .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor());
|
||||
|
||||
//Adds needed plugin to .uproject
|
||||
AttachPluginsToProjectDescriptor();
|
||||
|
||||
//Saves .uproject
|
||||
EVALUATE_OP_ACTION_RESULT(TrySave());
|
||||
|
||||
//When we are here, there should not be problems in generating Unreal Project for OpenPype
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("")
|
||||
{
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams(
|
||||
CommandLineParams)
|
||||
{
|
||||
UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches);
|
||||
}
|
||||
|
||||
FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const
|
||||
{
|
||||
FProjectInformation ProjectInformation = FProjectInformation();
|
||||
ProjectInformation.ProjectFilename = GetProjectFileName();
|
||||
|
||||
ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode");
|
||||
|
||||
return ProjectInformation;
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const
|
||||
{
|
||||
return Tokens.IsValidIndex(Index) ? Tokens[Index] : "";
|
||||
}
|
||||
|
||||
FString FOPGenerateProjectParams::GetProjectFileName() const
|
||||
{
|
||||
return TryGetToken(0);
|
||||
}
|
||||
|
||||
bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const
|
||||
{
|
||||
return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool
|
||||
{
|
||||
return Item.Equals(Switch);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet()
|
||||
{
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const
|
||||
{
|
||||
FText FailReason;
|
||||
FText FailLog;
|
||||
TArray<FString> OutCreatedFiles;
|
||||
|
||||
if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles))
|
||||
return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason);
|
||||
return FOP_ActionResult();
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason);
|
||||
}
|
||||
|
||||
void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor()
|
||||
{
|
||||
FPluginReferenceDescriptor OPPluginDescriptor;
|
||||
OPPluginDescriptor.bEnabled = true;
|
||||
OPPluginDescriptor.Name = OPConstants::OP_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(OPPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor PythonPluginDescriptor;
|
||||
PythonPluginDescriptor.bEnabled = true;
|
||||
PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(PythonPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor SequencerScriptingPluginDescriptor;
|
||||
SequencerScriptingPluginDescriptor.bEnabled = true;
|
||||
SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor;
|
||||
MovieRenderPipelinePluginDescriptor.bEnabled = true;
|
||||
MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor);
|
||||
|
||||
FPluginReferenceDescriptor EditorScriptingPluginDescriptor;
|
||||
EditorScriptingPluginDescriptor.bEnabled = true;
|
||||
EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName;
|
||||
ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor);
|
||||
}
|
||||
|
||||
FOP_ActionResult UOPGenerateProjectCommandlet::TrySave()
|
||||
{
|
||||
FText FailReason;
|
||||
const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason);
|
||||
|
||||
return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason);
|
||||
}
|
||||
|
||||
FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const
|
||||
{
|
||||
FOPGenerateProjectParams ParamsResult;
|
||||
|
||||
TArray<FString> Tokens, Switches;
|
||||
ParseCommandLine(*Params, Tokens, Switches);
|
||||
|
||||
return ParamsResult;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "Logging/OP_Log.h"
|
||||
|
||||
EOP_ActionResult::Type& FOP_ActionResult::GetStatus()
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
FText& FOP_ActionResult::GetReason()
|
||||
{
|
||||
return Reason;
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum)
|
||||
{
|
||||
TryLog();
|
||||
}
|
||||
|
||||
FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason)
|
||||
{
|
||||
TryLog();
|
||||
};
|
||||
|
||||
bool FOP_ActionResult::IsProblem() const
|
||||
{
|
||||
return Status != EOP_ActionResult::Ok;
|
||||
}
|
||||
|
||||
void FOP_ActionResult::TryLog() const
|
||||
{
|
||||
if(IsProblem())
|
||||
UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString());
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "Logging/OP_Log.h"
|
||||
|
|
@ -55,7 +55,7 @@ void UOpenPypePublishInstance::OnAssetCreated(const FAssetData& InAssetData)
|
|||
if (!IsValid(Asset))
|
||||
{
|
||||
UE_LOG(LogAssetData, Warning, TEXT("Asset \"%s\" is not valid! Skipping the addition."),
|
||||
*InAssetData.GetObjectPathString());
|
||||
*InAssetData.ObjectPath.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "GameProjectUtils.h"
|
||||
#include "Commandlets/OPActionResult.h"
|
||||
#include "ProjectDescriptor.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "OPGenerateProjectCommandlet.generated.h"
|
||||
|
||||
struct FProjectDescriptor;
|
||||
struct FProjectInformation;
|
||||
|
||||
/**
|
||||
* @brief Structure which parses command line parameters and generates FProjectInformation
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOPGenerateProjectParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FString CommandLineParams;
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
|
||||
public:
|
||||
FOPGenerateProjectParams();
|
||||
FOPGenerateProjectParams(const FString& CommandLineParams);
|
||||
|
||||
FProjectInformation GenerateUEProjectInformation() const;
|
||||
|
||||
private:
|
||||
FString TryGetToken(const int32 Index) const;
|
||||
FString GetProjectFileName() const;
|
||||
|
||||
bool IsSwitchPresent(const FString& Switch) const;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
FProjectInformation ProjectInformation;
|
||||
FProjectDescriptor ProjectDescriptor;
|
||||
|
||||
public:
|
||||
UOPGenerateProjectCommandlet();
|
||||
|
||||
virtual int32 Main(const FString& CommandLineParams) override;
|
||||
|
||||
private:
|
||||
FOPGenerateProjectParams ParseParameters(const FString& Params) const;
|
||||
FOP_ActionResult TryCreateProject() const;
|
||||
FOP_ActionResult TryLoadProjectDescriptor();
|
||||
void AttachPluginsToProjectDescriptor();
|
||||
FOP_ActionResult TrySave();
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OPActionResult.generated.h"
|
||||
|
||||
/**
|
||||
* @brief This macro returns error code when is problem or does nothing when there is no problem.
|
||||
* @param ActionResult FOP_ActionResult structure
|
||||
*/
|
||||
#define EVALUATE_OP_ACTION_RESULT(ActionResult) \
|
||||
if(ActionResult.IsProblem()) \
|
||||
return ActionResult.GetStatus();
|
||||
|
||||
/**
|
||||
* @brief This enum values are humanly readable mapping of error codes.
|
||||
* Here should be all error codes to be possible find what went wrong.
|
||||
* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it...
|
||||
*/
|
||||
UENUM()
|
||||
namespace EOP_ActionResult
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Ok,
|
||||
ProjectNotCreated,
|
||||
ProjectNotLoaded,
|
||||
ProjectNotSaved,
|
||||
//....Here insert another values
|
||||
|
||||
//Do not remove!
|
||||
//Usable for looping through enum values
|
||||
__Last UMETA(Hidden)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief This struct holds action result enum and optionally reason of fail
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FOP_ActionResult
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief Default constructor usable when there is no problem */
|
||||
FOP_ActionResult();
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum);
|
||||
|
||||
/**
|
||||
* @brief This constructor initializes variables & attempts to log when is error
|
||||
* @param InEnum Status
|
||||
* @param InReason Reason of potential fail
|
||||
*/
|
||||
FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason);
|
||||
|
||||
private:
|
||||
/** @brief Action status */
|
||||
EOP_ActionResult::Type Status;
|
||||
|
||||
/** @brief Optional reason of fail */
|
||||
FText Reason;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if there is problematic state
|
||||
* @return true when status is not equal to EOP_ActionResult::Ok
|
||||
*/
|
||||
bool IsProblem() const;
|
||||
EOP_ActionResult::Type& GetStatus();
|
||||
FText& GetReason();
|
||||
|
||||
private:
|
||||
void TryLog() const;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace OPConstants
|
||||
{
|
||||
const FString OP_PluginName = "OpenPype";
|
||||
const FString PythonScript_PluginName = "PythonScriptPlugin";
|
||||
const FString SequencerScripting_PluginName = "SequencerScripting";
|
||||
const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline";
|
||||
const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ public:
|
|||
*
|
||||
* @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetInternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -34,7 +34,7 @@ public:
|
|||
*
|
||||
* @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetExternalAssets() const
|
||||
{
|
||||
//For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed.
|
||||
|
|
@ -54,7 +54,7 @@ public:
|
|||
*
|
||||
* @attention If the bAddExternalAssets variable is false, external assets won't be included!
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python")
|
||||
TSet<UObject*> GetAllAssets() const
|
||||
{
|
||||
const TSet<TSoftObjectPtr<UObject>>& IteratedSet = bAddExternalAssets
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "AssetContainer.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "Engine.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
|
||||
UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer)
|
||||
: UAssetUserData(ObjectInitializer)
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path);
|
||||
FARFilter Filter;
|
||||
Filter.PackagePaths.Add(FName(*path));
|
||||
|
||||
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded);
|
||||
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved);
|
||||
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed);
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetAdded(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClassPath.ToString();
|
||||
UE_LOG(LogTemp, Log, TEXT("asset name %s"), *assetFName);
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
assets.Add(assetPath);
|
||||
assetsData.Add(AssetData);
|
||||
UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClassPath.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
|
||||
// take interest only in paths starting with path of current container
|
||||
FString path = UAssetContainer::GetPathName();
|
||||
FString lpp = FPackageName::GetLongPackagePath(*path);
|
||||
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp);
|
||||
assets.Remove(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UAssetContainer::GetPathName();
|
||||
FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath);
|
||||
|
||||
// get asset path and class
|
||||
FString assetPath = AssetData.GetFullName();
|
||||
FString assetFName = AssetData.AssetClassPath.ToString();
|
||||
|
||||
// split path
|
||||
assetPath.ParseIntoArray(split, TEXT(" "), true);
|
||||
|
||||
FString assetDir = FPackageName::GetLongPackagePath(*split[1]);
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "AssetContainer")
|
||||
{
|
||||
|
||||
assets.Remove(str);
|
||||
assets.Add(assetPath);
|
||||
assetsData.Remove(AssetData);
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#include "AssetContainerFactory.h"
|
||||
#include "AssetContainer.h"
|
||||
|
||||
UAssetContainerFactory::UAssetContainerFactory(const FObjectInitializer& ObjectInitializer)
|
||||
: UFactory(ObjectInitializer)
|
||||
{
|
||||
SupportedClass = UAssetContainer::StaticClass();
|
||||
bCreateNew = false;
|
||||
bEditorImport = true;
|
||||
}
|
||||
|
||||
UObject* UAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
UAssetContainer* AssetContainer = NewObject<UAssetContainer>(InParent, Class, Name, Flags);
|
||||
return AssetContainer;
|
||||
}
|
||||
|
||||
bool UAssetContainerFactory::ShouldShowInNewMenu() const {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Engine/AssetUserData.h"
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetContainer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Blueprintable)
|
||||
class OPENPYPE_API UAssetContainer : public UAssetUserData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UAssetContainer(const FObjectInitializer& ObjectInitalizer);
|
||||
// ~UAssetContainer();
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TArray<FString> assets;
|
||||
|
||||
// There seems to be no reflection option to expose array of FAssetData
|
||||
/*
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data"))
|
||||
TArray<FAssetData> assetsData;
|
||||
*/
|
||||
private:
|
||||
TArray<FAssetData> assetsData;
|
||||
void OnAssetAdded(const FAssetData& AssetData);
|
||||
void OnAssetRemoved(const FAssetData& AssetData);
|
||||
void OnAssetRenamed(const FAssetData& AssetData, const FString& str);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "AssetContainerFactory.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class OPENPYPE_API UAssetContainerFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UAssetContainerFactory(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
virtual bool ShouldShowInNewMenu() const override;
|
||||
};
|
||||
|
|
@ -4,6 +4,10 @@
|
|||
import os
|
||||
import platform
|
||||
import json
|
||||
|
||||
from typing import List
|
||||
|
||||
import openpype
|
||||
from distutils import dir_util
|
||||
import subprocess
|
||||
import re
|
||||
|
|
@ -73,7 +77,7 @@ def get_engine_versions(env=None):
|
|||
return OrderedDict()
|
||||
|
||||
|
||||
def get_editor_executable_path(engine_path: Path, engine_version: str) -> Path:
|
||||
def get_editor_exe_path(engine_path: Path, engine_version: str) -> Path:
|
||||
"""Get UE Editor executable path."""
|
||||
ue_path = engine_path / "Engine/Binaries"
|
||||
if platform.system().lower() == "windows":
|
||||
|
|
@ -214,77 +218,58 @@ def create_unreal_project(project_name: str,
|
|||
# created in different UE4 version. When user convert such project
|
||||
# to his UE4 version, Engine ID is replaced in uproject file. If some
|
||||
# other user tries to open it, it will present him with similar error.
|
||||
ue_modules = Path()
|
||||
if platform.system().lower() == "windows":
|
||||
ue_modules_path = engine_path / "Engine/Binaries/Win64"
|
||||
if ue_version.split(".")[0] == "4":
|
||||
ue_modules_path /= "UE4Editor.modules"
|
||||
elif ue_version.split(".")[0] == "5":
|
||||
ue_modules_path /= "UnrealEditor.modules"
|
||||
ue_modules = Path(ue_modules_path)
|
||||
|
||||
if platform.system().lower() == "linux":
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Linux", "UE4Editor.modules"))
|
||||
# engine_path should be the location of UE_X.X folder
|
||||
|
||||
if platform.system().lower() == "darwin":
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Mac", "UE4Editor.modules"))
|
||||
ue_editor_exe_path: Path = get_editor_exe_path(engine_path, ue_version)
|
||||
cmdlet_project_path = get_path_to_cmdlet_project(ue_version)
|
||||
|
||||
if ue_modules.exists():
|
||||
print("--- Loading Engine ID from modules file ...")
|
||||
with open(ue_modules, "r") as mp:
|
||||
loaded_modules = json.load(mp)
|
||||
project_file = pr_dir / f"{project_name}.uproject"
|
||||
|
||||
if loaded_modules.get("BuildId"):
|
||||
ue_id = "{" + loaded_modules.get("BuildId") + "}"
|
||||
|
||||
plugins_path = None
|
||||
if os.path.isdir(env.get("OPENPYPE_UNREAL_PLUGIN", "")):
|
||||
# copy plugin to correct path under project
|
||||
plugins_path = pr_dir / "Plugins"
|
||||
openpype_plugin_path = plugins_path / "OpenPype"
|
||||
if not openpype_plugin_path.is_dir():
|
||||
openpype_plugin_path.mkdir(parents=True, exist_ok=True)
|
||||
dir_util._path_created = {}
|
||||
dir_util.copy_tree(os.environ.get("OPENPYPE_UNREAL_PLUGIN"),
|
||||
openpype_plugin_path.as_posix())
|
||||
|
||||
if not (openpype_plugin_path / "Binaries").is_dir() \
|
||||
or not (openpype_plugin_path / "Intermediate").is_dir():
|
||||
dev_mode = True
|
||||
|
||||
# data for project file
|
||||
data = {
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": ue_id,
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{"Name": "PythonScriptPlugin", "Enabled": True},
|
||||
{"Name": "EditorScriptingUtilities", "Enabled": True},
|
||||
{"Name": "SequencerScripting", "Enabled": True},
|
||||
{"Name": "MovieRenderPipeline", "Enabled": True},
|
||||
{"Name": "OpenPype", "Enabled": True}
|
||||
]
|
||||
}
|
||||
print("--- Generating a new project ...")
|
||||
commandlet_cmd = [f'{ue_editor_exe_path.as_posix()}',
|
||||
f'{cmdlet_project_path.as_posix()}',
|
||||
f'-run=OPGenerateProject',
|
||||
f'{project_file.resolve().as_posix()}']
|
||||
|
||||
if dev_mode or preset["dev_mode"]:
|
||||
# this will add the project module and necessary source file to
|
||||
# make it a C++ project and to (hopefully) make Unreal Editor to
|
||||
# compile all # sources at start
|
||||
commandlet_cmd.append('-GenerateCode')
|
||||
|
||||
data["Modules"] = [{
|
||||
"Name": project_name,
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"AdditionalDependencies": ["Engine"],
|
||||
}]
|
||||
subprocess.run(commandlet_cmd)
|
||||
|
||||
# write project file
|
||||
project_file = pr_dir / f"{project_name}.uproject"
|
||||
with open(project_file, mode="w") as pf:
|
||||
json.dump(data, pf, indent=4)
|
||||
with open(project_file, mode="r+") as pf:
|
||||
pf_json = json.load(pf)
|
||||
pf_json["EngineAssociation"] = _get_build_id(engine_path, ue_version)
|
||||
pf.seek(0)
|
||||
json.dump(pf_json, pf, indent=4)
|
||||
pf.truncate()
|
||||
print(f'--- Engine ID has been writen into the project file')
|
||||
|
||||
if dev_mode or preset["dev_mode"]:
|
||||
u_build_tool = get_path_to_ubt(engine_path, ue_version)
|
||||
|
||||
arch = "Win64"
|
||||
if platform.system().lower() == "windows":
|
||||
arch = "Win64"
|
||||
elif platform.system().lower() == "linux":
|
||||
arch = "Linux"
|
||||
elif platform.system().lower() == "darwin":
|
||||
# we need to test this out
|
||||
arch = "Mac"
|
||||
|
||||
command1 = [u_build_tool.as_posix(), "-projectfiles",
|
||||
f"-project={project_file}", "-progress"]
|
||||
|
||||
subprocess.run(command1)
|
||||
|
||||
command2 = [u_build_tool.as_posix(),
|
||||
f"-ModuleWithSuffix={project_name},3555", arch,
|
||||
"Development", "-TargetType=Editor",
|
||||
f'-Project={project_file}',
|
||||
f'{project_file}',
|
||||
"-IgnoreJunk"]
|
||||
|
||||
subprocess.run(command2)
|
||||
|
||||
# ensure we have PySide2 installed in engine
|
||||
python_path = None
|
||||
|
|
@ -307,8 +292,121 @@ def create_unreal_project(project_name: str,
|
|||
subprocess.check_call(
|
||||
[python_path.as_posix(), "-m", "pip", "install", "pyside2"])
|
||||
|
||||
if dev_mode or preset["dev_mode"]:
|
||||
_prepare_cpp_project(project_file, engine_path, ue_version)
|
||||
|
||||
def get_path_to_uat(engine_path: Path) -> Path:
|
||||
if platform.system().lower() == "windows":
|
||||
return engine_path / "Engine/Build/BatchFiles/RunUAT.bat"
|
||||
|
||||
if platform.system().lower() == "linux" or platform.system().lower() == "darwin":
|
||||
return engine_path / "Engine/Build/BatchFiles/RunUAT.sh"
|
||||
|
||||
|
||||
def get_path_to_cmdlet_project(ue_version: str) -> Path:
|
||||
commandlet_project_path: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__)))
|
||||
|
||||
# For now, only tested on Windows (For Linux and Mac it has to be implemented)
|
||||
if ue_version.split(".")[0] == "4":
|
||||
return commandlet_project_path / "hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject"
|
||||
elif ue_version.split(".")[0] == "5":
|
||||
return commandlet_project_path / "hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject"
|
||||
|
||||
|
||||
def get_path_to_ubt(engine_path: Path, ue_version: str) -> Path:
|
||||
u_build_tool_path = engine_path / "Engine/Binaries/DotNET"
|
||||
|
||||
if ue_version.split(".")[0] == "4":
|
||||
u_build_tool_path /= "UnrealBuildTool.exe"
|
||||
elif ue_version.split(".")[0] == "5":
|
||||
u_build_tool_path /= "UnrealBuildTool/UnrealBuildTool.exe"
|
||||
|
||||
return Path(u_build_tool_path)
|
||||
|
||||
|
||||
def _get_build_id(engine_path: Path, ue_version: str) -> str:
|
||||
ue_modules = Path()
|
||||
if platform.system().lower() == "windows":
|
||||
ue_modules_path = engine_path / "Engine/Binaries/Win64"
|
||||
if ue_version.split(".")[0] == "4":
|
||||
ue_modules_path /= "UE4Editor.modules"
|
||||
elif ue_version.split(".")[0] == "5":
|
||||
ue_modules_path /= "UnrealEditor.modules"
|
||||
ue_modules = Path(ue_modules_path)
|
||||
|
||||
if platform.system().lower() == "linux":
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Linux", "UE4Editor.modules"))
|
||||
|
||||
if platform.system().lower() == "darwin":
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Mac", "UE4Editor.modules"))
|
||||
|
||||
if ue_modules.exists():
|
||||
print("--- Loading Engine ID from modules file ...")
|
||||
with open(ue_modules, "r") as mp:
|
||||
loaded_modules = json.load(mp)
|
||||
|
||||
if loaded_modules.get("BuildId"):
|
||||
return "{" + loaded_modules.get("BuildId") + "}"
|
||||
|
||||
|
||||
def try_installing_plugin(engine_path: Path,
|
||||
ue_version: str,
|
||||
env: dict = None) -> None:
|
||||
env = env or os.environ
|
||||
|
||||
integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", ""))
|
||||
|
||||
if not os.path.isdir(integration_plugin_path):
|
||||
raise RuntimeError("Path to the integration plugin is null!")
|
||||
|
||||
# Create a path to the plugin in the engine
|
||||
openpype_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype"
|
||||
|
||||
if not openpype_plugin_path.is_dir():
|
||||
print("--- OpenPype Plugin is not present. Creating a new plugin directory ...")
|
||||
openpype_plugin_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
engine_plugin_config_path: Path = openpype_plugin_path / "Config"
|
||||
engine_plugin_config_path.mkdir(exist_ok=True)
|
||||
|
||||
dir_util._path_created = {}
|
||||
|
||||
if not (openpype_plugin_path / "Binaries").is_dir() \
|
||||
or not (openpype_plugin_path / "Intermediate").is_dir():
|
||||
print("--- Binaries are not present. Building the plugin ...")
|
||||
_build_and_move_integration_plugin(engine_path, openpype_plugin_path, env)
|
||||
|
||||
|
||||
def _build_and_move_integration_plugin(engine_path: Path,
|
||||
plugin_build_path: Path,
|
||||
env: dict = None) -> None:
|
||||
uat_path: Path = get_path_to_uat(engine_path)
|
||||
|
||||
env = env or os.environ
|
||||
integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", ""))
|
||||
|
||||
if uat_path.is_file():
|
||||
temp_dir: Path = integration_plugin_path.parent / "Temp"
|
||||
temp_dir.mkdir(exist_ok=True)
|
||||
uplugin_path: Path = integration_plugin_path / "OpenPype.uplugin"
|
||||
|
||||
# in order to successfully build the plugin, It must be built outside the Engine directory and then moved
|
||||
build_plugin_cmd: List[str] = [f'{uat_path.as_posix()}',
|
||||
'BuildPlugin',
|
||||
f'-Plugin={uplugin_path.as_posix()}',
|
||||
f'-Package={temp_dir.as_posix()}']
|
||||
subprocess.run(build_plugin_cmd)
|
||||
|
||||
# Copy the contents of the 'Temp' dir into the 'OpenPype' directory in the engine
|
||||
dir_util.copy_tree(temp_dir.as_posix(), plugin_build_path.as_posix())
|
||||
|
||||
# We need to also copy the config folder. The UAT doesn't include the Config folder in the build
|
||||
plugin_install_config_path: Path = plugin_build_path / "Config"
|
||||
integration_plugin_config_path = integration_plugin_path / "Config"
|
||||
|
||||
dir_util.copy_tree(integration_plugin_config_path.as_posix(), plugin_install_config_path.as_posix())
|
||||
|
||||
dir_util.remove_tree(temp_dir.as_posix())
|
||||
|
||||
|
||||
def _prepare_cpp_project(
|
||||
|
|
|
|||