Merge pull request #3122 from pypeclub/feature/OP-3130_unreal-5-support
Support for Unreal 5
|
|
@ -1,15 +1,19 @@
|
|||
import os
|
||||
import openpype.hosts
|
||||
from openpype.lib.applications import Application
|
||||
|
||||
|
||||
def add_implementation_envs(env, _app):
|
||||
def add_implementation_envs(env: dict, _app: Application) -> None:
|
||||
"""Modify environments to contain all required for implementation."""
|
||||
# Set OPENPYPE_UNREAL_PLUGIN required for Unreal implementation
|
||||
|
||||
ue_plugin = "UE_5.0" if _app.name[:1] == "5" else "UE_4.7"
|
||||
unreal_plugin_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(openpype.hosts.__file__)),
|
||||
"unreal", "integration"
|
||||
"unreal", "integration", ue_plugin
|
||||
)
|
||||
env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path
|
||||
if not env.get("OPENPYPE_UNREAL_PLUGIN"):
|
||||
env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path
|
||||
|
||||
# Set default environments if are not set via settings
|
||||
defaults = {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.signature = "( {} )".format(self.__class__.__name__)
|
||||
self.signature = f"( {self.__class__.__name__} )"
|
||||
|
||||
def _get_work_filename(self):
|
||||
# Use last workfile if was found
|
||||
|
|
@ -71,7 +71,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
if int(engine_version.split(".")[0]) < 4 and \
|
||||
int(engine_version.split(".")[1]) < 26:
|
||||
raise ApplicationLaunchFailed((
|
||||
f"{self.signature} Old unsupported version of UE4 "
|
||||
f"{self.signature} Old unsupported version of UE "
|
||||
f"detected - {engine_version}"))
|
||||
except ValueError:
|
||||
# there can be string in minor version and in that case
|
||||
|
|
@ -99,18 +99,19 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
f"character ({unreal_project_name}). Appending 'P'"
|
||||
))
|
||||
unreal_project_name = f"P{unreal_project_name}"
|
||||
unreal_project_filename = f'{unreal_project_name}.uproject'
|
||||
|
||||
project_path = Path(os.path.join(workdir, unreal_project_name))
|
||||
|
||||
self.log.info((
|
||||
f"{self.signature} requested UE4 version: "
|
||||
f"{self.signature} requested UE version: "
|
||||
f"[ {engine_version} ]"
|
||||
))
|
||||
|
||||
detected = unreal_lib.get_engine_versions(self.launch_context.env)
|
||||
detected_str = ', '.join(detected.keys()) or 'none'
|
||||
self.log.info((
|
||||
f"{self.signature} detected UE4 versions: "
|
||||
f"{self.signature} detected UE versions: "
|
||||
f"[ {detected_str} ]"
|
||||
))
|
||||
if not detected:
|
||||
|
|
@ -123,10 +124,10 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
f"detected [ {engine_version} ]"
|
||||
))
|
||||
|
||||
ue4_path = unreal_lib.get_editor_executable_path(
|
||||
Path(detected[engine_version]))
|
||||
ue_path = unreal_lib.get_editor_executable_path(
|
||||
Path(detected[engine_version]), engine_version)
|
||||
|
||||
self.launch_context.launch_args = [ue4_path.as_posix()]
|
||||
self.launch_context.launch_args = [ue_path.as_posix()]
|
||||
project_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
project_file = project_path / unreal_project_filename
|
||||
|
|
@ -138,6 +139,11 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
))
|
||||
# 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]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# OpenPype Unreal Integration plugin
|
||||
# OpenPype Unreal Integration plugin - UE 4.x
|
||||
|
||||
This is plugin for Unreal Editor, creating menu for [OpenPype](https://github.com/getavalon) tools to run.
|
||||
|
||||
|
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 |
35
openpype/hosts/unreal/integration/UE_5.0/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
/Binaries
|
||||
/Intermediate
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import unreal
|
||||
|
||||
openpype_detected = True
|
||||
try:
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.unreal import api as openpype_host
|
||||
except ImportError as exc:
|
||||
openpype_host = None
|
||||
openpype_detected = False
|
||||
unreal.log_error("OpenPype: cannot load OpenPype [ {} ]".format(exc))
|
||||
|
||||
if openpype_detected:
|
||||
install_host(openpype_host)
|
||||
|
||||
|
||||
@unreal.uclass()
|
||||
class OpenPypeIntegration(unreal.OpenPypePythonBridge):
|
||||
@unreal.ufunction(override=True)
|
||||
def RunInPython_Popup(self):
|
||||
unreal.log_warning("OpenPype: showing tools popup")
|
||||
if openpype_detected:
|
||||
openpype_host.show_tools_popup()
|
||||
|
||||
@unreal.ufunction(override=True)
|
||||
def RunInPython_Dialog(self):
|
||||
unreal.log_warning("OpenPype: showing tools dialog")
|
||||
if openpype_detected:
|
||||
openpype_host.show_tools_dialog()
|
||||
24
openpype/hosts/unreal/integration/UE_5.0/OpenPype.uplugin
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "OpenPype",
|
||||
"Description": "OpenPype Integration",
|
||||
"Category": "OpenPype.Integration",
|
||||
"CreatedBy": "Ondrej Samohel",
|
||||
"CreatedByURL": "https://openpype.io",
|
||||
"DocsURL": "https://openpype.io/docs/artist_hosts_unreal",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://pype.club/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "OpenPype",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
openpype/hosts/unreal/integration/UE_5.0/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# OpenPype Unreal Integration plugin - UE 5.x
|
||||
|
||||
This is plugin for Unreal Editor, creating menu for [OpenPype](https://github.com/getavalon) tools to run.
|
||||
|
||||
## How does this work
|
||||
|
||||
Plugin is creating basic menu items in **Window/OpenPype** section of Unreal Editor main menu and a button
|
||||
on the main toolbar with associated menu. Clicking on those menu items is calling callbacks that are
|
||||
declared in C++ but needs to be implemented during Unreal Editor
|
||||
startup in `Plugins/OpenPype/Content/Python/init_unreal.py` - this should be executed by Unreal Editor
|
||||
automatically.
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class OpenPype : ModuleRules
|
||||
{
|
||||
public OpenPype(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Projects",
|
||||
"InputCore",
|
||||
"EditorFramework",
|
||||
"UnrealEd",
|
||||
"ToolMenus",
|
||||
"LevelEditor",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#include "OpenPype.h"
|
||||
#include "OpenPypeStyle.h"
|
||||
#include "OpenPypeCommands.h"
|
||||
#include "OpenPypePythonBridge.h"
|
||||
#include "LevelEditor.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
#include "ToolMenus.h"
|
||||
|
||||
|
||||
static const FName OpenPypeTabName("OpenPype");
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FOpenPypeModule"
|
||||
|
||||
// This function is triggered when the plugin is staring up
|
||||
void FOpenPypeModule::StartupModule()
|
||||
{
|
||||
FOpenPypeStyle::Initialize();
|
||||
FOpenPypeStyle::ReloadTextures();
|
||||
FOpenPypeCommands::Register();
|
||||
|
||||
PluginCommands = MakeShareable(new FUICommandList);
|
||||
|
||||
PluginCommands->MapAction(
|
||||
FOpenPypeCommands::Get().OpenPypeTools,
|
||||
FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuPopup),
|
||||
FCanExecuteAction());
|
||||
PluginCommands->MapAction(
|
||||
FOpenPypeCommands::Get().OpenPypeToolsDialog,
|
||||
FExecuteAction::CreateRaw(this, &FOpenPypeModule::MenuDialog),
|
||||
FCanExecuteAction());
|
||||
|
||||
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FOpenPypeModule::RegisterMenus));
|
||||
}
|
||||
|
||||
void FOpenPypeModule::ShutdownModule()
|
||||
{
|
||||
UToolMenus::UnRegisterStartupCallback(this);
|
||||
|
||||
UToolMenus::UnregisterOwner(this);
|
||||
|
||||
FOpenPypeStyle::Shutdown();
|
||||
|
||||
FOpenPypeCommands::Unregister();
|
||||
}
|
||||
|
||||
void FOpenPypeModule::RegisterMenus()
|
||||
{
|
||||
// Owner will be used for cleanup in call to UToolMenus::UnregisterOwner
|
||||
FToolMenuOwnerScoped OwnerScoped(this);
|
||||
|
||||
{
|
||||
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools");
|
||||
{
|
||||
// FToolMenuSection& Section = Menu->FindOrAddSection("OpenPype");
|
||||
FToolMenuSection& Section = Menu->AddSection(
|
||||
"OpenPype",
|
||||
TAttribute<FText>(FText::FromString("OpenPype")),
|
||||
FToolMenuInsert("Programming", EToolMenuInsertType::Before)
|
||||
);
|
||||
Section.AddMenuEntryWithCommandList(FOpenPypeCommands::Get().OpenPypeTools, PluginCommands);
|
||||
Section.AddMenuEntryWithCommandList(FOpenPypeCommands::Get().OpenPypeToolsDialog, PluginCommands);
|
||||
}
|
||||
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
|
||||
{
|
||||
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("PluginTools");
|
||||
{
|
||||
FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FOpenPypeCommands::Get().OpenPypeTools));
|
||||
Entry.SetCommandList(PluginCommands);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FOpenPypeModule::MenuPopup() {
|
||||
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
|
||||
bridge->RunInPython_Popup();
|
||||
}
|
||||
|
||||
void FOpenPypeModule::MenuDialog() {
|
||||
UOpenPypePythonBridge* bridge = UOpenPypePythonBridge::Get();
|
||||
bridge->RunInPython_Dialog();
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FOpenPypeModule, OpenPype)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OpenPypeCommands.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FOpenPypeModule"
|
||||
|
||||
void FOpenPypeCommands::RegisterCommands()
|
||||
{
|
||||
UI_COMMAND(OpenPypeTools, "OpenPype Tools", "Pipeline tools", EUserInterfaceActionType::Button, FInputChord());
|
||||
UI_COMMAND(OpenPypeToolsDialog, "OpenPype Tools Dialog", "Pipeline tools dialog", EUserInterfaceActionType::Button, FInputChord());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#include "OpenPypeLib.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
/**
|
||||
* Sets color on folder icon on given path
|
||||
* @param InPath - path to folder
|
||||
* @param InFolderColor - color of the folder
|
||||
* @warning This color will appear only after Editor restart. Is there a better way?
|
||||
*/
|
||||
|
||||
void UOpenPypeLib::CSetFolderColor(FString FolderPath, FLinearColor FolderColor, bool bForceAdd)
|
||||
{
|
||||
auto SaveColorInternal = [](FString InPath, FLinearColor InFolderColor)
|
||||
{
|
||||
// Saves the color of the folder to the config
|
||||
if (FPaths::FileExists(GEditorPerProjectIni))
|
||||
{
|
||||
GConfig->SetString(TEXT("PathColor"), *InPath, *InFolderColor.ToString(), GEditorPerProjectIni);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SaveColorInternal(FolderPath, FolderColor);
|
||||
|
||||
}
|
||||
/**
|
||||
* Returns all poperties on given object
|
||||
* @param cls - class
|
||||
* @return TArray of properties
|
||||
*/
|
||||
TArray<FString> UOpenPypeLib::GetAllProperties(UClass* cls)
|
||||
{
|
||||
TArray<FString> Ret;
|
||||
if (cls != nullptr)
|
||||
{
|
||||
for (TFieldIterator<FProperty> It(cls); It; ++It)
|
||||
{
|
||||
FProperty* Property = *It;
|
||||
if (Property->HasAnyPropertyFlags(EPropertyFlags::CPF_Edit))
|
||||
{
|
||||
Ret.Add(Property->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include "OpenPypePublishInstance.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
|
||||
|
||||
UOpenPypePublishInstance::UOpenPypePublishInstance(const FObjectInitializer& ObjectInitializer)
|
||||
: UObject(ObjectInitializer)
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
FString path = UOpenPypePublishInstance::GetPathName();
|
||||
FARFilter Filter;
|
||||
Filter.PackagePaths.Add(FName(*path));
|
||||
|
||||
AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UOpenPypePublishInstance::OnAssetAdded);
|
||||
AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UOpenPypePublishInstance::OnAssetRemoved);
|
||||
AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UOpenPypePublishInstance::OnAssetRenamed);
|
||||
}
|
||||
|
||||
void UOpenPypePublishInstance::OnAssetAdded(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UOpenPypePublishInstance::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 != "OpenPypePublishInstance")
|
||||
{
|
||||
assets.Add(assetPath);
|
||||
UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOpenPypePublishInstance::OnAssetRemoved(const FAssetData& AssetData)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UOpenPypePublishInstance::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 = UOpenPypePublishInstance::GetPathName();
|
||||
FString lpp = FPackageName::GetLongPackagePath(*path);
|
||||
|
||||
if (assetDir.StartsWith(*selfDir))
|
||||
{
|
||||
// exclude self
|
||||
if (assetFName != "OpenPypePublishInstance")
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp);
|
||||
assets.Remove(assetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOpenPypePublishInstance::OnAssetRenamed(const FAssetData& AssetData, const FString& str)
|
||||
{
|
||||
TArray<FString> split;
|
||||
|
||||
// get directory of current container
|
||||
FString selfFullPath = UOpenPypePublishInstance::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);
|
||||
// UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include "OpenPypePublishInstanceFactory.h"
|
||||
#include "OpenPypePublishInstance.h"
|
||||
|
||||
UOpenPypePublishInstanceFactory::UOpenPypePublishInstanceFactory(const FObjectInitializer& ObjectInitializer)
|
||||
: UFactory(ObjectInitializer)
|
||||
{
|
||||
SupportedClass = UOpenPypePublishInstance::StaticClass();
|
||||
bCreateNew = false;
|
||||
bEditorImport = true;
|
||||
}
|
||||
|
||||
UObject* UOpenPypePublishInstanceFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
UOpenPypePublishInstance* OpenPypePublishInstance = NewObject<UOpenPypePublishInstance>(InParent, Class, Name, Flags);
|
||||
return OpenPypePublishInstance;
|
||||
}
|
||||
|
||||
bool UOpenPypePublishInstanceFactory::ShouldShowInNewMenu() const {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#include "OpenPypePythonBridge.h"
|
||||
|
||||
UOpenPypePythonBridge* UOpenPypePythonBridge::Get()
|
||||
{
|
||||
TArray<UClass*> OpenPypePythonBridgeClasses;
|
||||
GetDerivedClasses(UOpenPypePythonBridge::StaticClass(), OpenPypePythonBridgeClasses);
|
||||
int32 NumClasses = OpenPypePythonBridgeClasses.Num();
|
||||
if (NumClasses > 0)
|
||||
{
|
||||
return Cast<UOpenPypePythonBridge>(OpenPypePythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#include "OpenPype.h"
|
||||
#include "OpenPypeStyle.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "Slate/SlateGameResources.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "Styling/SlateStyleMacros.h"
|
||||
|
||||
#define RootToContentDir Style->RootToContentDir
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FOpenPypeStyle::OpenPypeStyleInstance = nullptr;
|
||||
|
||||
void FOpenPypeStyle::Initialize()
|
||||
{
|
||||
if (!OpenPypeStyleInstance.IsValid())
|
||||
{
|
||||
OpenPypeStyleInstance = Create();
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*OpenPypeStyleInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void FOpenPypeStyle::Shutdown()
|
||||
{
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*OpenPypeStyleInstance);
|
||||
ensure(OpenPypeStyleInstance.IsUnique());
|
||||
OpenPypeStyleInstance.Reset();
|
||||
}
|
||||
|
||||
FName FOpenPypeStyle::GetStyleSetName()
|
||||
{
|
||||
static FName StyleSetName(TEXT("OpenPypeStyle"));
|
||||
return StyleSetName;
|
||||
}
|
||||
|
||||
const FVector2D Icon16x16(16.0f, 16.0f);
|
||||
const FVector2D Icon20x20(20.0f, 20.0f);
|
||||
const FVector2D Icon40x40(40.0f, 40.0f);
|
||||
|
||||
TSharedRef< FSlateStyleSet > FOpenPypeStyle::Create()
|
||||
{
|
||||
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("OpenPypeStyle"));
|
||||
Style->SetContentRoot(IPluginManager::Get().FindPlugin("OpenPype")->GetBaseDir() / TEXT("Resources"));
|
||||
|
||||
Style->Set("OpenPype.OpenPypeTools", new IMAGE_BRUSH(TEXT("openpype40"), Icon40x40));
|
||||
Style->Set("OpenPype.OpenPypeToolsDialog", new IMAGE_BRUSH(TEXT("openpype40"), Icon40x40));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
||||
void FOpenPypeStyle::ReloadTextures()
|
||||
{
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
|
||||
}
|
||||
}
|
||||
|
||||
const ISlateStyle& FOpenPypeStyle::Get()
|
||||
{
|
||||
return *OpenPypeStyleInstance;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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;
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
|
||||
class FOpenPypeModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
void RegisterMenus();
|
||||
|
||||
void MenuPopup();
|
||||
void MenuDialog();
|
||||
|
||||
private:
|
||||
TSharedPtr<class FUICommandList> PluginCommands;
|
||||
};
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
#include "OpenPypeStyle.h"
|
||||
|
||||
class FOpenPypeCommands : public TCommands<FOpenPypeCommands>
|
||||
{
|
||||
public:
|
||||
|
||||
FOpenPypeCommands()
|
||||
: TCommands<FOpenPypeCommands>(TEXT("OpenPype"), NSLOCTEXT("Contexts", "OpenPype", "OpenPype Tools"), NAME_None, FOpenPypeStyle::GetStyleSetName())
|
||||
{
|
||||
}
|
||||
|
||||
// TCommands<> interface
|
||||
virtual void RegisterCommands() override;
|
||||
|
||||
public:
|
||||
TSharedPtr< FUICommandInfo > OpenPypeTools;
|
||||
TSharedPtr< FUICommandInfo > OpenPypeToolsDialog;
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "Engine.h"
|
||||
#include "OpenPypeLib.generated.h"
|
||||
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class OPENPYPE_API UOpenPypeLib : public UObject
|
||||
{
|
||||
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = Python)
|
||||
static void CSetFolderColor(FString FolderPath, FLinearColor FolderColor, bool bForceAdd);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = Python)
|
||||
static TArray<FString> GetAllProperties(UClass* cls);
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "Engine.h"
|
||||
#include "OpenPypePublishInstance.generated.h"
|
||||
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class OPENPYPE_API UOpenPypePublishInstance : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UOpenPypePublishInstance(const FObjectInitializer& ObjectInitalizer);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TArray<FString> assets;
|
||||
private:
|
||||
void OnAssetAdded(const FAssetData& AssetData);
|
||||
void OnAssetRemoved(const FAssetData& AssetData);
|
||||
void OnAssetRenamed(const FAssetData& AssetData, const FString& str);
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "OpenPypePublishInstanceFactory.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class OPENPYPE_API UOpenPypePublishInstanceFactory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UOpenPypePublishInstanceFactory(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
virtual bool ShouldShowInNewMenu() const override;
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "Engine.h"
|
||||
#include "OpenPypePythonBridge.generated.h"
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class UOpenPypePythonBridge : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = Python)
|
||||
static UOpenPypePythonBridge* Get();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = Python)
|
||||
void RunInPython_Popup() const;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = Python)
|
||||
void RunInPython_Dialog() const;
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
|
||||
class FOpenPypeStyle
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Shutdown();
|
||||
static void ReloadTextures();
|
||||
static const ISlateStyle& Get();
|
||||
static FName GetStyleSetName();
|
||||
|
||||
|
||||
private:
|
||||
static TSharedRef< class FSlateStyleSet > Create();
|
||||
static TSharedPtr< class FSlateStyleSet > OpenPypeStyleInstance;
|
||||
};
|
||||
|
|
@ -70,19 +70,22 @@ def get_engine_versions(env=None):
|
|||
return OrderedDict()
|
||||
|
||||
|
||||
def get_editor_executable_path(engine_path: Path) -> Path:
|
||||
"""Get UE4 Editor executable path."""
|
||||
ue4_path = engine_path / "Engine/Binaries"
|
||||
def get_editor_executable_path(engine_path: Path, engine_version: str) -> Path:
|
||||
"""Get UE Editor executable path."""
|
||||
ue_path = engine_path / "Engine/Binaries"
|
||||
if platform.system().lower() == "windows":
|
||||
ue4_path /= "Win64/UE4Editor.exe"
|
||||
if engine_version.split(".")[0] == "4":
|
||||
ue_path /= "Win64/UE4Editor.exe"
|
||||
elif engine_version.split(".")[0] == "5":
|
||||
ue_path /= "Win64/UnrealEditor.exe"
|
||||
|
||||
elif platform.system().lower() == "linux":
|
||||
ue4_path /= "Linux/UE4Editor"
|
||||
ue_path /= "Linux/UE4Editor"
|
||||
|
||||
elif platform.system().lower() == "darwin":
|
||||
ue4_path /= "Mac/UE4Editor"
|
||||
ue_path /= "Mac/UE4Editor"
|
||||
|
||||
return ue4_path
|
||||
return ue_path
|
||||
|
||||
|
||||
def _win_get_engine_versions():
|
||||
|
|
@ -208,22 +211,26 @@ 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.
|
||||
ue4_modules = Path()
|
||||
ue_modules = Path()
|
||||
if platform.system().lower() == "windows":
|
||||
ue4_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Win64", "UE4Editor.modules"))
|
||||
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":
|
||||
ue4_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Linux", "UE4Editor.modules"))
|
||||
|
||||
if platform.system().lower() == "darwin":
|
||||
ue4_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries",
|
||||
"Mac", "UE4Editor.modules"))
|
||||
|
||||
if ue4_modules.exists():
|
||||
if ue_modules.exists():
|
||||
print("--- Loading Engine ID from modules file ...")
|
||||
with open(ue4_modules, "r") as mp:
|
||||
with open(ue_modules, "r") as mp:
|
||||
loaded_modules = json.load(mp)
|
||||
|
||||
if loaded_modules.get("BuildId"):
|
||||
|
|
@ -280,7 +287,7 @@ def create_unreal_project(project_name: str,
|
|||
python_path = None
|
||||
if platform.system().lower() == "windows":
|
||||
python_path = engine_path / ("Engine/Binaries/ThirdParty/"
|
||||
"Python3/Win64/pythonw.exe")
|
||||
"Python3/Win64/python.exe")
|
||||
|
||||
if platform.system().lower() == "linux":
|
||||
python_path = engine_path / ("Engine/Binaries/ThirdParty/"
|
||||
|
|
@ -294,14 +301,15 @@ def create_unreal_project(project_name: str,
|
|||
raise NotImplementedError("Unsupported platform")
|
||||
if not python_path.exists():
|
||||
raise RuntimeError(f"Unreal Python not found at {python_path}")
|
||||
subprocess.run(
|
||||
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)
|
||||
_prepare_cpp_project(project_file, engine_path, ue_version)
|
||||
|
||||
|
||||
def _prepare_cpp_project(project_file: Path, engine_path: Path) -> None:
|
||||
def _prepare_cpp_project(
|
||||
project_file: Path, engine_path: Path, ue_version: str) -> None:
|
||||
"""Prepare CPP Unreal Project.
|
||||
|
||||
This function will add source files needed for project to be
|
||||
|
|
@ -420,8 +428,12 @@ class {1}_API A{0}GameModeBase : public AGameModeBase
|
|||
with open(sources_dir / f"{project_name}GameModeBase.h", mode="w") as f:
|
||||
f.write(game_mode_h)
|
||||
|
||||
u_build_tool = Path(
|
||||
engine_path / "Engine/Binaries/DotNET/UnrealBuildTool.exe")
|
||||
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"
|
||||
u_build_tool = Path(u_build_tool_path)
|
||||
u_header_tool = None
|
||||
|
||||
arch = "Win64"
|
||||
|
|
|
|||
|
|
@ -22,17 +22,24 @@ class CreateRender(Creator):
|
|||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
# The asset name is the the third element of the path which contains
|
||||
# the map.
|
||||
# The index of the split path is 3 because the first element is an
|
||||
# empty string, as the path begins with "/Content".
|
||||
a = unreal.EditorUtilityLibrary.get_selected_assets()[0]
|
||||
asset_name = a.get_path_name().split("/")[3]
|
||||
|
||||
# Get the master sequence and the master level.
|
||||
# There should be only one sequence and one level in the directory.
|
||||
filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[f"/Game/OpenPype/{self.data['asset']}"],
|
||||
package_paths=[f"/Game/OpenPype/{asset_name}"],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
ms = sequences[0].get_editor_property('object_path')
|
||||
filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"/Game/OpenPype/{self.data['asset']}"],
|
||||
package_paths=[f"/Game/OpenPype/{asset_name}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(filter)
|
||||
ml = levels[0].get_editor_property('object_path')
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from openpype.pipeline import (
|
|||
)
|
||||
from openpype.hosts.unreal.api import plugin
|
||||
from openpype.hosts.unreal.api import pipeline as unreal_pipeline
|
||||
from openpype.api import get_asset
|
||||
|
||||
|
||||
class AnimationFBXLoader(plugin.Loader):
|
||||
|
|
@ -77,13 +78,15 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_meshes_in_bone_hierarchy', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'use_default_sample_rate', True)
|
||||
'use_default_sample_rate', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'custom_sample_rate', get_asset()["data"].get("fps"))
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_custom_attribute', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_bone_tracks', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'remove_redundant_keys', True)
|
||||
'remove_redundant_keys', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'convert_scene', True)
|
||||
|
||||
|
|
@ -139,22 +142,18 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
root = "/Game/OpenPype"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
|
||||
asset_name = f"{asset}_{name}" if asset else f"{name}"
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
f"{root}/Animations/{asset}/{name}", suffix="")
|
||||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{hierarchy[0]}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(filter)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_editor_property('object_path')
|
||||
|
||||
hierarchy_dir = root
|
||||
|
|
@ -162,11 +161,11 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
hierarchy_dir = f"{hierarchy_dir}/{h}"
|
||||
hierarchy_dir = f"{hierarchy_dir}/{asset}"
|
||||
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{hierarchy_dir}/"],
|
||||
recursive_paths=True)
|
||||
levels = ar.get_assets(filter)
|
||||
levels = ar.get_assets(_filter)
|
||||
level = levels[0].get_editor_property('object_path')
|
||||
|
||||
unreal.EditorLevelLibrary.save_all_dirty_levels()
|
||||
|
|
@ -233,8 +232,7 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
unreal_pipeline.imprint(f"{asset_dir}/{container_name}", data)
|
||||
|
||||
imported_content = EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=False)
|
||||
|
|
@ -279,13 +277,15 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_meshes_in_bone_hierarchy', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'use_default_sample_rate', True)
|
||||
'use_default_sample_rate', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'custom_sample_rate', get_asset()["data"].get("fps"))
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_custom_attribute', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_bone_tracks', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'remove_redundant_keys', True)
|
||||
'remove_redundant_keys', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'convert_scene', True)
|
||||
|
||||
|
|
@ -296,8 +296,7 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
|
||||
# do import fbx and replace existing data
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
container_path = "{}/{}".format(container["namespace"],
|
||||
container["objectName"])
|
||||
container_path = f'{container["namespace"]}/{container["objectName"]}'
|
||||
# update metadata
|
||||
unreal_pipeline.imprint(
|
||||
container_path,
|
||||
|
|
|
|||
|
|
@ -58,6 +58,33 @@ class CameraLoader(plugin.Loader):
|
|||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
|
||||
def _import_camera(
|
||||
self, world, sequence, bindings, import_fbx_settings, import_filename
|
||||
):
|
||||
ue_version = unreal.SystemLibrary.get_engine_version().split('.')
|
||||
ue_major = int(ue_version[0])
|
||||
ue_minor = int(ue_version[1])
|
||||
|
||||
if ue_major == 4 and ue_minor <= 26:
|
||||
unreal.SequencerTools.import_fbx(
|
||||
world,
|
||||
sequence,
|
||||
bindings,
|
||||
import_fbx_settings,
|
||||
import_filename
|
||||
)
|
||||
elif (ue_major == 4 and ue_minor >= 27) or ue_major == 5:
|
||||
unreal.SequencerTools.import_level_sequence_fbx(
|
||||
world,
|
||||
sequence,
|
||||
bindings,
|
||||
import_fbx_settings,
|
||||
import_filename
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"Unreal version {ue_major} not supported")
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
|
@ -242,7 +269,7 @@ class CameraLoader(plugin.Loader):
|
|||
settings.set_editor_property('reduce_keys', False)
|
||||
|
||||
if cam_seq:
|
||||
unreal.SequencerTools.import_fbx(
|
||||
self._import_camera(
|
||||
EditorLevelLibrary.get_editor_world(),
|
||||
cam_seq,
|
||||
cam_seq.get_bindings(),
|
||||
|
|
@ -402,7 +429,7 @@ class CameraLoader(plugin.Loader):
|
|||
|
||||
sub_scene.set_sequence(new_sequence)
|
||||
|
||||
unreal.SequencerTools.import_fbx(
|
||||
self._import_camera(
|
||||
EditorLevelLibrary.get_editor_world(),
|
||||
new_sequence,
|
||||
new_sequence.get_bindings(),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from openpype.pipeline import (
|
|||
AVALON_CONTAINER_ID,
|
||||
legacy_io,
|
||||
)
|
||||
from openpype.api import get_asset
|
||||
from openpype.hosts.unreal.api import plugin
|
||||
from openpype.hosts.unreal.api import pipeline as unreal_pipeline
|
||||
|
||||
|
|
@ -87,7 +88,8 @@ class LayoutLoader(plugin.Loader):
|
|||
|
||||
return None
|
||||
|
||||
def _get_data(self, asset_name):
|
||||
@staticmethod
|
||||
def _get_data(asset_name):
|
||||
asset_doc = legacy_io.find_one({
|
||||
"type": "asset",
|
||||
"name": asset_name
|
||||
|
|
@ -95,8 +97,9 @@ class LayoutLoader(plugin.Loader):
|
|||
|
||||
return asset_doc.get("data")
|
||||
|
||||
@staticmethod
|
||||
def _set_sequence_hierarchy(
|
||||
self, seq_i, seq_j, max_frame_i, min_frame_j, max_frame_j, map_paths
|
||||
seq_i, seq_j, max_frame_i, min_frame_j, max_frame_j, map_paths
|
||||
):
|
||||
# Get existing sequencer tracks or create them if they don't exist
|
||||
tracks = seq_i.get_master_tracks()
|
||||
|
|
@ -165,8 +168,9 @@ class LayoutLoader(plugin.Loader):
|
|||
hid_section.set_row_index(index)
|
||||
hid_section.set_level_names(maps)
|
||||
|
||||
@staticmethod
|
||||
def _process_family(
|
||||
self, assets, class_name, transform, sequence, inst_name=None
|
||||
assets, class_name, transform, sequence, inst_name=None
|
||||
):
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
|
|
@ -262,13 +266,15 @@ class LayoutLoader(plugin.Loader):
|
|||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_meshes_in_bone_hierarchy', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'use_default_sample_rate', True)
|
||||
'use_default_sample_rate', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'custom_sample_rate', get_asset()["data"].get("fps"))
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_custom_attribute', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_bone_tracks', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'remove_redundant_keys', True)
|
||||
'remove_redundant_keys', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'convert_scene', True)
|
||||
|
||||
|
|
@ -311,11 +317,8 @@ class LayoutLoader(plugin.Loader):
|
|||
for binding in bindings:
|
||||
tracks = binding.get_tracks()
|
||||
track = None
|
||||
if not tracks:
|
||||
track = binding.add_track(
|
||||
unreal.MovieSceneSkeletalAnimationTrack)
|
||||
else:
|
||||
track = tracks[0]
|
||||
track = tracks[0] if tracks else binding.add_track(
|
||||
unreal.MovieSceneSkeletalAnimationTrack)
|
||||
|
||||
sections = track.get_sections()
|
||||
section = None
|
||||
|
|
@ -335,11 +338,11 @@ class LayoutLoader(plugin.Loader):
|
|||
curr_anim.get_path_name()).parent
|
||||
).replace('\\', '/')
|
||||
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["AssetContainer"],
|
||||
package_paths=[anim_path],
|
||||
recursive_paths=False)
|
||||
containers = ar.get_assets(filter)
|
||||
containers = ar.get_assets(_filter)
|
||||
|
||||
if len(containers) > 0:
|
||||
return
|
||||
|
|
@ -350,6 +353,7 @@ class LayoutLoader(plugin.Loader):
|
|||
sec_params = section.get_editor_property('params')
|
||||
sec_params.set_editor_property('animation', animation)
|
||||
|
||||
@staticmethod
|
||||
def _generate_sequence(self, h, h_dir):
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
|
|
@ -583,10 +587,7 @@ class LayoutLoader(plugin.Loader):
|
|||
hierarchy_dir_list.append(hierarchy_dir)
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
asset_name = f"{asset}_{name}" if asset else name
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
|
|
@ -800,7 +801,7 @@ class LayoutLoader(plugin.Loader):
|
|||
lc for lc in layout_containers
|
||||
if asset in lc.get('loaded_assets')]
|
||||
|
||||
if len(layouts) == 0:
|
||||
if not layouts:
|
||||
EditorAssetLibrary.delete_directory(str(Path(asset).parent))
|
||||
|
||||
# Remove the Level Sequence from the parent.
|
||||
|
|
@ -810,17 +811,17 @@ class LayoutLoader(plugin.Loader):
|
|||
namespace = container.get('namespace').replace(f"{root}/", "")
|
||||
ms_asset = namespace.split('/')[0]
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
sequences = ar.get_assets(_filter)
|
||||
master_sequence = sequences[0].get_asset()
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(filter)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_editor_property('object_path')
|
||||
|
||||
sequences = [master_sequence]
|
||||
|
|
|
|||
|
|
@ -1243,9 +1243,19 @@
|
|||
"host_name": "unreal",
|
||||
"environment": {},
|
||||
"variants": {
|
||||
"4-26": {
|
||||
"4-27": {
|
||||
"use_python_2": false,
|
||||
"environment": {}
|
||||
},
|
||||
"5-0": {
|
||||
"use_python_2": false,
|
||||
"environment": {
|
||||
"UE_PYTHONPATH": "{PYTHONPATH}"
|
||||
}
|
||||
},
|
||||
"__dynamic_keys_labels__": {
|
||||
"4-27": "4.27",
|
||||
"5-0": "5.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||