Merge main to mp_session_integ
This commit is contained in:
@@ -97,16 +97,19 @@ class Cdk:
|
||||
env=self._cdk_env,
|
||||
shell=True)
|
||||
|
||||
def deploy(self, context_variable: str = '') -> List[str]:
|
||||
def deploy(self, context_variable: str = '', additonal_params: List[str] = None) -> List[str]:
|
||||
"""
|
||||
Deploys all the CDK stacks.
|
||||
:param context_variable: Context variable for enabling optional features.
|
||||
:param additonal_params: Additonal parameters like --all can be passed in this way.
|
||||
:return List of deployed stack arns.
|
||||
"""
|
||||
if not self._cdk_path:
|
||||
return []
|
||||
|
||||
deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never']
|
||||
if additonal_params:
|
||||
deploy_cdk_application_cmd.extend(additonal_params)
|
||||
if context_variable:
|
||||
deploy_cdk_application_cmd.extend(['-c', f'{context_variable}'])
|
||||
|
||||
|
||||
@@ -137,5 +137,6 @@ enum class AnimParamType
|
||||
Invalid = static_cast<int>(0xFFFFFFFF)
|
||||
};
|
||||
|
||||
static const int OLD_APARAM_USER = 100;
|
||||
|
||||
#endif // CRYINCLUDE_CRYCOMMON_MAESTRO_TYPES_ANIMPARAMTYPE_H
|
||||
|
||||
@@ -561,7 +561,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
|
||||
|
||||
if (pex)
|
||||
{
|
||||
MINIDUMP_TYPE mdumpValue;
|
||||
MINIDUMP_TYPE mdumpValue = MiniDumpNormal;
|
||||
bool bDump = true;
|
||||
switch (g_cvars.sys_dump_type)
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
}
|
||||
if (pSystem && !pSystem->IsQuitting())
|
||||
{
|
||||
LRESULT result;
|
||||
LRESULT result = 0;
|
||||
bool bAny = false;
|
||||
for (std::vector<IWindowMessageHandler*>::const_iterator it = pSystem->m_windowMessageHandlers.begin(); it != pSystem->m_windowMessageHandlers.end(); ++it)
|
||||
{
|
||||
|
||||
@@ -634,7 +634,7 @@ ICVar* CSystem::attachVariable (const char* szVarName, int* pContainer, const ch
|
||||
IConsole* pConsole = GetIConsole();
|
||||
|
||||
ICVar* pOldVar = pConsole->GetCVar (szVarName);
|
||||
int nDefault;
|
||||
int nDefault = 0;
|
||||
if (pOldVar)
|
||||
{
|
||||
nDefault = pOldVar->GetIVal();
|
||||
|
||||
@@ -127,13 +127,14 @@ namespace AZ
|
||||
*/
|
||||
class EditContext
|
||||
{
|
||||
public:
|
||||
/// @cond EXCLUDE_DOCS
|
||||
class ClassBuilder;
|
||||
class EnumBuilder;
|
||||
using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder
|
||||
using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder
|
||||
/// @endcond
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0);
|
||||
|
||||
/**
|
||||
@@ -186,6 +187,7 @@ namespace AZ
|
||||
* look at the unit tests and example to see use cases.
|
||||
*
|
||||
*/
|
||||
public:
|
||||
class ClassBuilder
|
||||
{
|
||||
friend EditContext;
|
||||
@@ -399,6 +401,7 @@ namespace AZ
|
||||
EnumBuilder* Value(const char* name, E value);
|
||||
};
|
||||
|
||||
private:
|
||||
typedef AZStd::list<Edit::ClassData> ClassDataListType;
|
||||
typedef AZStd::unordered_map<AZ::Uuid, Edit::ElementData> EnumDataMapType;
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ namespace AZ
|
||||
class SerializeContext
|
||||
: public ReflectContext
|
||||
{
|
||||
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
|
||||
|
||||
public:
|
||||
/// @cond EXCLUDE_DOCS
|
||||
friend class EditContext;
|
||||
class ClassBuilder;
|
||||
@@ -108,9 +111,6 @@ namespace AZ
|
||||
/// @endcond
|
||||
class EnumBuilder;
|
||||
|
||||
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
|
||||
|
||||
public:
|
||||
class ClassData;
|
||||
struct EnumerateInstanceCallContext;
|
||||
struct ClassElement;
|
||||
@@ -1131,6 +1131,7 @@ namespace AZ
|
||||
* ->Version(3,&MyVersionConverter)
|
||||
* ->Field("data",&MyStruct::m_data);
|
||||
*/
|
||||
public:
|
||||
class ClassBuilder
|
||||
{
|
||||
friend class SerializeContext;
|
||||
@@ -1330,7 +1331,8 @@ namespace AZ
|
||||
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator>* m_currentAttributes = nullptr;
|
||||
};
|
||||
|
||||
EditContext* m_editContext; ///< Pointer to optional edit context.
|
||||
private:
|
||||
EditContext* m_editContext; ///< Pointer to optional edit context.
|
||||
UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context
|
||||
AZStd::unordered_multimap<AZ::Crc32, AZ::Uuid> m_classNameToUuid; /// Map all class names to their uuid
|
||||
AZStd::unordered_multimap<Uuid, GenericClassInfo*> m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo
|
||||
|
||||
@@ -971,6 +971,10 @@ namespace AZ
|
||||
*/
|
||||
void RestoreCachedInstances();
|
||||
|
||||
/// Returns data flags for use when instantiating an instance of this slice.
|
||||
/// These data flags include those harvested from the entire slice ancestry.
|
||||
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1004,9 +1008,6 @@ namespace AZ
|
||||
DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId);
|
||||
const DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId) const;
|
||||
|
||||
/// Returns data flags for use when instantiating an instance of this slice.
|
||||
/// These data flags include those harvested from the entire slice ancestry.
|
||||
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
|
||||
void BuildDataFlagsForInstances();
|
||||
|
||||
/**
|
||||
|
||||
+16
-7
@@ -13,9 +13,10 @@
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <libgen.h>
|
||||
|
||||
@@ -61,10 +62,11 @@ namespace AZ
|
||||
// If it doesn't attempt to append the path to the executable path
|
||||
if (!AZ::IO::SystemFile::Exists(fullFilePath.c_str()))
|
||||
{
|
||||
auto candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
AZ::IO::FixedMaxPath candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
fullFilePath = candidatePath;
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,19 +76,26 @@ namespace AZ
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(projectModulePath.c_str()))
|
||||
{
|
||||
fullFilePath = projectModulePath;
|
||||
m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_fileName = AZStd::string_view{fullFilePath.Native()};
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleUnixLike() override
|
||||
|
||||
+13
-3
@@ -24,9 +24,9 @@ namespace AZ
|
||||
: public DynamicModuleHandle
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0)
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0);
|
||||
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
: DynamicModuleHandle(fullFileName)
|
||||
, m_handle(nullptr)
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace AZ
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +66,7 @@ namespace AZ
|
||||
// Therefore an existence check is needed
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= AZStd::string_view(m_fileName);
|
||||
@@ -76,6 +77,15 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleWindows() override
|
||||
|
||||
@@ -1914,7 +1914,7 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareIsConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
@@ -1922,7 +1922,7 @@ namespace UnitTest
|
||||
{
|
||||
return "HelloPearl";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
constexpr basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
constexpr basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -1937,11 +1937,11 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareOperatorsAreConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> compareView(compileTimeString1);
|
||||
static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed");
|
||||
static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed");
|
||||
@@ -1955,7 +1955,7 @@ namespace UnitTest
|
||||
{
|
||||
auto swap_test_func = []() constexpr -> basic_string_view<TypeParam>
|
||||
{
|
||||
constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -1977,7 +1977,7 @@ namespace UnitTest
|
||||
return L"InuWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -2001,7 +2001,7 @@ namespace UnitTest
|
||||
|
||||
TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr)
|
||||
{
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -2012,7 +2012,7 @@ namespace UnitTest
|
||||
return L"HelloWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> hashView(compileTimeString1);
|
||||
constexpr size_t compileHash = AZStd::hash<basic_string_view<TypeParam>>{}(hashView);
|
||||
static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0");
|
||||
|
||||
@@ -395,7 +395,8 @@ namespace UnitTest
|
||||
}
|
||||
else
|
||||
{
|
||||
int result1, result2;
|
||||
int result1 = 0;
|
||||
int result2 = 0;
|
||||
Job* job1 = aznew FibonacciJob2(m_n - 1, &result1, m_context);
|
||||
Job* job2 = aznew FibonacciJob2(m_n - 2, &result2, m_context);
|
||||
StartAsChild(job1);
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Matrix4x4, TestCreateFrom)
|
||||
{
|
||||
float testFloats[] =
|
||||
float thisTestFloats[] =
|
||||
{
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f,
|
||||
@@ -67,20 +67,20 @@ namespace UnitTest
|
||||
13.0f, 14.0f, 15.0f, 16.0f
|
||||
};
|
||||
float testFloatMtx[16];
|
||||
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats);
|
||||
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(thisTestFloats);
|
||||
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f));
|
||||
m1.StoreToRowMajorFloat16(testFloatMtx);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
|
||||
m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
|
||||
m1 = Matrix4x4::CreateFromColumnMajorFloat16(thisTestFloats);
|
||||
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f));
|
||||
m1.StoreToColumnMajorFloat16(testFloatMtx);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
|
||||
}
|
||||
|
||||
TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4)
|
||||
|
||||
@@ -119,10 +119,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, Contains)
|
||||
{
|
||||
const Vector3 position(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 halfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
// test some pairs of points which should be just either side of the Obb boundary
|
||||
EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f)));
|
||||
EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f)));
|
||||
@@ -134,10 +134,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistance)
|
||||
{
|
||||
const Vector3 position(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 halfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f);
|
||||
@@ -146,10 +146,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistanceSq)
|
||||
{
|
||||
const Vector3 position(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 halfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f);
|
||||
|
||||
+1
@@ -353,6 +353,7 @@ namespace AzFramework
|
||||
|
||||
// Get the dimensions of the display device on which the window is currently displayed.
|
||||
MONITORINFO monitorInfo;
|
||||
memset(&monitorInfo, 0, sizeof(MONITORINFO)); // C4701 potentially uninitialized local variable 'monitorInfo' used
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
const BOOL success = monitor ? GetMonitorInfo(monitor, &monitorInfo) : FALSE;
|
||||
if (!success)
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace AzNetworking
|
||||
}
|
||||
else if (m_updateRate < updateTimeMs)
|
||||
{
|
||||
AZLOG_INFO("TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
|
||||
AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
|
||||
}
|
||||
}
|
||||
OnStop();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
|
||||
|
||||
@@ -49,8 +50,7 @@ namespace AzToolsFramework
|
||||
m_alias = GenerateInstanceAlias();
|
||||
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
|
||||
: AZStd::make_unique<AZ::Entity>();
|
||||
EntityAlias containerEntityAlias = GenerateEntityAlias();
|
||||
RegisterEntity(m_containerEntity->GetId(), containerEntityAlias);
|
||||
RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName);
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
@@ -311,8 +311,15 @@ namespace AzToolsFramework
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = GenerateInstanceAlias();
|
||||
return AddInstance(AZStd::move(instance), newInstanceAlias);
|
||||
}
|
||||
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias newInstanceAlias)
|
||||
{
|
||||
AZ_Assert(instance.get(), "instance argument is nullptr");
|
||||
AZ_Assert(m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), "InstanceAlias' unique id collision, this should never happen.");
|
||||
AZ_Assert(
|
||||
m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(),
|
||||
"InstanceAlias' unique id collision, this should never happen.");
|
||||
instance->m_parent = this;
|
||||
instance->m_alias = newInstanceAlias;
|
||||
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
|
||||
@@ -613,6 +620,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachContainerEntity()
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
return AZStd::move(m_containerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace AzToolsFramework
|
||||
using EntityAliasOptionalReference = AZStd::optional<AZStd::reference_wrapper<EntityAlias>>;
|
||||
using InstanceOptionalReference = AZStd::optional<AZStd::reference_wrapper<Instance>>;
|
||||
using InstanceOptionalConstReference = AZStd::optional<AZStd::reference_wrapper<const Instance>>;
|
||||
|
||||
using InstanceSet = AZStd::unordered_set<Instance*>;
|
||||
using InstanceSetConstReference = AZStd::optional<AZStd::reference_wrapper<const InstanceSet>>;
|
||||
using EntityOptionalReference = AZStd::optional<AZStd::reference_wrapper<AZ::Entity>>;
|
||||
@@ -85,12 +86,14 @@ namespace AzToolsFramework
|
||||
bool AddEntity(AZ::Entity& entity);
|
||||
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveNestedEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
void Reset();
|
||||
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
|
||||
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
|
||||
|
||||
/**
|
||||
@@ -182,7 +185,6 @@ namespace AzToolsFramework
|
||||
|
||||
void ClearEntities();
|
||||
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
@@ -191,24 +192,7 @@ namespace AzToolsFramework
|
||||
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
|
||||
{
|
||||
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
previousPatch.Accept(writer);
|
||||
QString previousPatchString(buffer.GetString());
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
AZ::EntityId entityId = entity->GetId();
|
||||
AZStd::string oldEntityAlias = oldEntityAliases[entityId];
|
||||
EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId);
|
||||
AZ_Assert(
|
||||
newEntityAlias.has_value(),
|
||||
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
previousPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
UpdateLinkPatchesWithNewEntityAliases(previousPatch, oldEntityAliases, instanceToCreate->get());
|
||||
}
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
@@ -243,11 +227,33 @@ namespace AzToolsFramework
|
||||
instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
|
||||
AZStd::move(patch));
|
||||
|
||||
// Reset the transform of the container entity so that the new values aren't saved in the new prefab's dom.
|
||||
// The new values were saved in the link, so propagation will apply them correctly.
|
||||
{
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
|
||||
PrefabDom containerBeforeReset;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerBeforeReset, *containerEntity);
|
||||
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, AZ::EntityId());
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTM, AZ::Transform::CreateIdentity());
|
||||
|
||||
PrefabDom containerAfterReset;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerAfterReset, *containerEntity);
|
||||
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(containerEntityId)));
|
||||
state->SetParent(undoBatch.GetUndoBatch());
|
||||
state->Capture(containerBeforeReset, containerAfterReset, containerEntityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
// This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab.
|
||||
// We are doing this so that the changes in those enities are not queued up twice for propagation.
|
||||
// We are doing this so that the changes in those entities are not queued up twice for propagation.
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
|
||||
|
||||
// Select Container Entity
|
||||
{
|
||||
auto selectionUndo = aznew SelectionCommand({containerEntityId}, "Select Prefab Container Entity");
|
||||
@@ -291,7 +297,7 @@ namespace AzToolsFramework
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter), parentEntityId);
|
||||
|
||||
return AZStd::move(patch);
|
||||
}
|
||||
@@ -385,8 +391,8 @@ namespace AzToolsFramework
|
||||
|
||||
CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
@@ -589,54 +595,199 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Create Undo node on entities if they belong to an instance
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
if (owningInstance.has_value())
|
||||
if (!owningInstance.has_value())
|
||||
{
|
||||
PrefabDom afterState;
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (entity)
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (!entity)
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom beforeState;
|
||||
AZ::EntityId beforeParentId;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState, beforeParentId);
|
||||
|
||||
PrefabDom afterState;
|
||||
AZ::EntityId afterParentId;
|
||||
AZ::TransformBus::EventResult(afterParentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
{
|
||||
bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId);
|
||||
bool isNewParentOwnedByDifferentInstance = false;
|
||||
|
||||
if (beforeParentId != afterParentId)
|
||||
{
|
||||
PrefabDom beforeState;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState);
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
if (beforeOwningInstance.has_value() && afterOwningInstance.has_value() &&
|
||||
(&beforeOwningInstance->get() != &afterOwningInstance->get()))
|
||||
{
|
||||
if (IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate =
|
||||
aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(parentUndoBatch);
|
||||
linkUpdate->Capture(patch, owningInstance->get().GetLinkId());
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(parentUndoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
isNewParentOwnedByDifferentInstance = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the cache
|
||||
m_prefabUndoCache.Store(entityId, AZStd::move(afterState));
|
||||
if (isInstanceContainerEntity)
|
||||
{
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
|
||||
PrefabDom afterStateafterReparenting;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterStateafterReparenting, *entity);
|
||||
|
||||
PrefabDom newPatch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(newPatch, afterState, afterStateafterReparenting);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(newPatch, entityId);
|
||||
|
||||
InstanceOptionalReference owningInstanceAfterReparenting =
|
||||
m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, newPatch, owningInstanceAfterReparenting->get().GetLinkId());
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, patch, owningInstance->get().GetLinkId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
Internal_HandleEntityChange(parentUndoBatch, entityId, beforeState, afterState);
|
||||
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_prefabUndoCache.UpdateCache(entityId);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId)
|
||||
{
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(undoBatch);
|
||||
linkUpdate->Capture(patch, linkId);
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState)
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(undoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleInstanceChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId)
|
||||
{
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
EntityList entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Retrieve all descendant entities and instances of this entity that belonged to the same owning instance.
|
||||
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instances);
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instanceUniquePtrs;
|
||||
AZStd::vector<AZStd::pair<Instance*, PrefabDom>> instancePatches;
|
||||
|
||||
// Remove Entities and Instances from the prior instance
|
||||
{
|
||||
// Remove Instances
|
||||
for (Instance* nestedInstance : instances)
|
||||
{
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
|
||||
|
||||
PrefabDom oldLinkPatches;
|
||||
|
||||
if (linkRef.has_value())
|
||||
{
|
||||
auto patches = linkRef->get().GetLinkPatches();
|
||||
if (patches.has_value())
|
||||
{
|
||||
oldLinkPatches.CopyFrom(patches->get(), oldLinkPatches.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
auto nestedInstanceUniquePtr = beforeOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
|
||||
RemoveLink(nestedInstanceUniquePtr, beforeOwningInstance->get().GetTemplateId(), undoBatch);
|
||||
|
||||
instancePatches.emplace_back(AZStd::make_pair(nestedInstanceUniquePtr.get(), AZStd::move(oldLinkPatches)));
|
||||
instanceUniquePtrs.emplace_back(AZStd::move(nestedInstanceUniquePtr));
|
||||
}
|
||||
|
||||
// Get the previous state of the prior instance for undo/redo purposes
|
||||
PrefabDom beforeInstanceDomBeforeRemoval;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(beforeInstanceDomBeforeRemoval, beforeOwningInstance->get());
|
||||
|
||||
// Remove Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
beforeOwningInstance->get().DetachEntity(nestedEntity->GetId()).release();
|
||||
}
|
||||
|
||||
// Create the Update node for the prior owning instance
|
||||
// Instance removal will be taken care of from the RemoveLink function for undo/redo purposes
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
beforeOwningInstance->get(), "Update prior prefab instance", beforeInstanceDomBeforeRemoval, undoBatch);
|
||||
}
|
||||
|
||||
// Add Entities and Instances to new instance
|
||||
{
|
||||
// Add Instances
|
||||
for (auto& instanceUniquePtr : instanceUniquePtrs)
|
||||
{
|
||||
afterOwningInstance->get().AddInstance(AZStd::move(instanceUniquePtr));
|
||||
}
|
||||
|
||||
// Create Links
|
||||
for (auto& instanceInfo : instancePatches)
|
||||
{
|
||||
// Add a new link with the old dom
|
||||
CreateLink(
|
||||
*instanceInfo.first, afterOwningInstance->get().GetTemplateId(), undoBatch,
|
||||
AZStd::move(instanceInfo.second));
|
||||
}
|
||||
|
||||
// Get the previous state of the new instance for undo/redo purposes
|
||||
PrefabDom afterInstanceDomBeforeAdd;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(afterInstanceDomBeforeAdd, afterOwningInstance->get());
|
||||
|
||||
// Add Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
afterOwningInstance->get().AddEntity(*nestedEntity);
|
||||
}
|
||||
|
||||
// Create the Update node for the new owning instance
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
afterOwningInstance->get(), "Update new prefab instance", afterInstanceDomBeforeAdd, undoBatch);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
|
||||
@@ -989,6 +1140,123 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DetachPrefab(const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
if (!containerEntityId.IsValid())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
|
||||
}
|
||||
|
||||
if (IsLevelInstanceContainerEntity(containerEntityId))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
|
||||
if (owningInstance->get().GetContainerEntityId() != containerEntityId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Input entity should be its owning Instance's container entity."));
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:UndoCapture");
|
||||
|
||||
ScopedUndoBatch undoBatch("Detach Prefab");
|
||||
|
||||
InstanceOptionalReference getParentInstanceResult = owningInstance->get().GetParentInstance();
|
||||
AZ_Assert(getParentInstanceResult.has_value(), "Can't get parent Instance from Instance of given container entity.");
|
||||
|
||||
auto& parentInstance = getParentInstanceResult->get();
|
||||
const auto parentTemplateId = parentInstance.GetTemplateId();
|
||||
|
||||
{
|
||||
auto instancePtr = parentInstance.DetachNestedInstance(owningInstance->get().GetInstanceAlias());
|
||||
AZ_Assert(instancePtr, "Can't detach selected Instance from its parent Instance.");
|
||||
|
||||
RemoveLink(instancePtr, parentTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, parentInstance);
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
|
||||
oldEntityAliases.emplace(containerEntityId, instancePtr->GetEntityAlias(containerEntityId)->get());
|
||||
|
||||
auto containerEntityPtr = instancePtr->DetachContainerEntity();
|
||||
auto& containerEntity = *containerEntityPtr.release();
|
||||
auto editorPrefabComponent = containerEntity.FindComponent<EditorPrefabComponent>();
|
||||
containerEntity.Deactivate();
|
||||
const bool editorPrefabComponentRemoved = containerEntity.RemoveComponent(editorPrefabComponent);
|
||||
AZ_Assert(editorPrefabComponentRemoved, "Remove EditorPrefabComponent failed.");
|
||||
delete editorPrefabComponent;
|
||||
containerEntity.Activate();
|
||||
|
||||
const bool containerEntityAdded = parentInstance.AddEntity(containerEntity);
|
||||
AZ_Assert(containerEntityAdded, "Add target Instance's container entity to its parent Instance failed.");
|
||||
|
||||
EntityIdList entityIds;
|
||||
entityIds.emplace_back(containerEntity.GetId());
|
||||
|
||||
instancePtr->GetEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity>& entityPtr)
|
||||
{
|
||||
oldEntityAliases.emplace(entityPtr->GetId(), instancePtr->GetEntityAlias(entityPtr->GetId())->get());
|
||||
return true;
|
||||
});
|
||||
|
||||
instancePtr->DetachEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity> entityPtr)
|
||||
{
|
||||
auto& entity = *entityPtr.release();
|
||||
const bool entityAdded = parentInstance.AddEntity(entity);
|
||||
AZ_Assert(entityAdded, "Add target Instance's entity to its parent Instance failed.");
|
||||
|
||||
entityIds.emplace_back(entity.GetId());
|
||||
});
|
||||
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:RunRedo");
|
||||
command->RunRedo();
|
||||
}
|
||||
|
||||
const auto instanceTemplateId = instancePtr->GetTemplateId();
|
||||
auto parentContainerEntityId = parentInstance.GetContainerEntityId();
|
||||
instancePtr->GetNestedInstances(
|
||||
[&](AZStd::unique_ptr<Instance>& nestedInstancePtr)
|
||||
{
|
||||
//get previous link patch
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstancePtr->GetLinkId());
|
||||
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
|
||||
AZ_Assert(
|
||||
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
|
||||
nestedInstancePtr->GetLinkId());
|
||||
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
RemoveLink(nestedInstancePtr, instanceTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
UpdateLinkPatchesWithNewEntityAliases(linkPatchesCopy, oldEntityAliases, parentInstance);
|
||||
|
||||
CreateLink(*nestedInstancePtr, parentTemplateId, undoBatch.GetUndoBatch(),
|
||||
AZStd::move(linkPatchesCopy), true);
|
||||
});
|
||||
}
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::GenerateContainerEntityTransform(const EntityList& topLevelEntities,
|
||||
AZ::Vector3& translation, AZ::Quaternion& rotation)
|
||||
{
|
||||
@@ -1252,5 +1520,30 @@ namespace AzToolsFramework
|
||||
|
||||
stringToReplace.replace(oldAliasPathRef, newAliasPathRef);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent)
|
||||
{
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
linkPatch.Accept(writer);
|
||||
QString previousPatchString(buffer.GetString());
|
||||
|
||||
for (const auto& [entityId, oldEntityAlias] : oldEntityAliases)
|
||||
{
|
||||
EntityAliasOptionalReference newEntityAlias = newParent.GetEntityAlias(entityId);
|
||||
AZ_Assert(
|
||||
newEntityAlias.has_value(),
|
||||
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
linkPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace AzToolsFramework
|
||||
PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override;
|
||||
PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override;
|
||||
|
||||
PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override;
|
||||
|
||||
private:
|
||||
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
|
||||
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
|
||||
@@ -88,8 +90,8 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Creates a link between the templates of an instance and its parent.
|
||||
*
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link.
|
||||
* \param targetInstance The id of the target template.
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link (child).
|
||||
* \param targetInstance The id of the target template (parent).
|
||||
* \param undoBatch The undo batch to set as parent for this create link action.
|
||||
* \param patch The patch to store in the newly created link dom.
|
||||
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
|
||||
@@ -132,7 +134,18 @@ namespace AzToolsFramework
|
||||
bool IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
|
||||
static void Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId);
|
||||
static void Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState);
|
||||
void Internal_HandleInstanceChange(UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId);
|
||||
|
||||
void UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent);
|
||||
|
||||
static void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
|
||||
|
||||
static Instance* GetParentInstance(Instance* instance);
|
||||
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
|
||||
|
||||
@@ -150,6 +150,17 @@ namespace AzToolsFramework
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0;
|
||||
|
||||
/**
|
||||
* If the entity id is a container entity id, detaches the prefab instance corresponding to it. This includes converting
|
||||
* the container entity into a regular entity and putting it under the parent prefab, removing the link between this
|
||||
* instance and the parent, removing links between this instance and it's nested instances, adding entities directly
|
||||
* owned by this instance under the parent instance.
|
||||
* Bails if the entity is not a container entity or belongs to the level prefab instance.
|
||||
* @param containerEntityId The container entity id of the instance to detach.
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -652,7 +652,8 @@ namespace AzToolsFramework
|
||||
if (instancesValue->get().FindMember(rapidjson::StringRef(instanceAlias.c_str())) == instancesValue->get().MemberEnd())
|
||||
{
|
||||
instancesValue->get().AddMember(
|
||||
rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
|
||||
rapidjson::Value(instanceAlias.c_str(), targetTemplateDom.GetAllocator()), PrefabDomValue(),
|
||||
targetTemplateDom.GetAllocator());
|
||||
}
|
||||
|
||||
Template& sourceTemplate = sourceTemplateRef->get();
|
||||
@@ -705,14 +706,14 @@ namespace AzToolsFramework
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from TemplateToLinkIdsMap.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId());
|
||||
|
||||
result = RemoveLinkFromTargetTemplate(linkId, link);
|
||||
AZ_Assert(result,
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from target Template with Id '%llu'.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str(), link.GetTargetTemplateId());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId(), link.GetTargetTemplateId());
|
||||
|
||||
m_linkIdMap.erase(linkId);
|
||||
|
||||
|
||||
@@ -73,14 +73,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
PrefabDom oldData;
|
||||
Retrieve(entityId, oldData);
|
||||
AZ::EntityId oldParentId;
|
||||
Retrieve(entityId, oldData, oldParentId);
|
||||
|
||||
UpdateCache(entityId);
|
||||
|
||||
PrefabDom newData;
|
||||
Retrieve(entityId, newData);
|
||||
AZ::EntityId newParentId;
|
||||
Retrieve(entityId, newData, newParentId);
|
||||
|
||||
if (newData != oldData)
|
||||
if (newData != oldData || oldParentId != newParentId)
|
||||
{
|
||||
// display a useful message
|
||||
AZ::Entity* entity = nullptr;
|
||||
@@ -106,7 +108,7 @@ namespace AzToolsFramework
|
||||
// Clear out newly generated data and
|
||||
// replace with original data to ensure debug mode has the same data as profile/release
|
||||
// in the event of the consistency check failing.
|
||||
m_entitySavedStates[entityId] = AZStd::move(oldData);
|
||||
m_entitySavedStates[entityId] = {AZStd::move(oldData), oldParentId};
|
||||
|
||||
#endif // ENABLE_UNDOCACHE_CONSISTENCY_CHECKS
|
||||
}
|
||||
@@ -140,10 +142,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
// Capture it
|
||||
PrefabDom entityDom;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *entity);
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(entityDom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(entityDom), parentId};
|
||||
|
||||
AZLOG("Prefab Undo", "Correctly updated cache for entity of id %llu (%s)", static_cast<AZ::u64>(entityId), entity->GetName().c_str());
|
||||
|
||||
@@ -155,7 +160,7 @@ namespace AzToolsFramework
|
||||
m_entitySavedStates.erase(entityId);
|
||||
}
|
||||
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom)
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId)
|
||||
{
|
||||
auto it = m_entitySavedStates.find(entityId);
|
||||
|
||||
@@ -164,14 +169,15 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId]);
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId].dom);
|
||||
parentId = m_entitySavedStates[entityId].parentId;
|
||||
m_entitySavedStates.erase(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom)
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId)
|
||||
{
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(dom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(dom), parentId};
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Clear()
|
||||
|
||||
@@ -46,14 +46,19 @@ namespace AzToolsFramework
|
||||
void Validate(const AZ::EntityId& entityId) override;
|
||||
|
||||
// Retrieve the last known state for an entity
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom);
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId);
|
||||
|
||||
// Store dom as the cached state of entityId
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom);
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId);
|
||||
|
||||
private:
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabDom> EntityDomMap;
|
||||
EntityDomMap m_entitySavedStates;
|
||||
struct PrefabUndoCacheItem
|
||||
{
|
||||
PrefabDom dom;
|
||||
AZ::EntityId parentId;
|
||||
};
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabUndoCacheItem> EntityCache;
|
||||
EntityCache m_entitySavedStates;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
|
||||
+29
@@ -237,6 +237,24 @@ namespace AzToolsFramework
|
||||
{
|
||||
deleteAction->setDisabled(true);
|
||||
}
|
||||
|
||||
// Detach Prefab
|
||||
if (selectedEntities.size() == 1)
|
||||
{
|
||||
AZ::EntityId selectedEntity = selectedEntities[0];
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) &&
|
||||
!s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[this, selectedEntity]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntity);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
@@ -392,6 +410,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_DetachPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
PrefabOperationResult detachPrefabResult =
|
||||
s_prefabPublicInterface->DetachPrefab(containerEntity);
|
||||
|
||||
if (!detachPrefabResult.IsSuccess())
|
||||
{
|
||||
WarnUserOfError("Detach Prefab error", detachPrefabResult.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace AzToolsFramework
|
||||
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_DeleteSelected();
|
||||
static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity);
|
||||
|
||||
// Prompt and resolve dialogs
|
||||
static bool QueryUserForPrefabSaveLocation(
|
||||
|
||||
+2
-1
@@ -969,7 +969,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Build up components to display
|
||||
SharedComponentArray sharedComponentArray;
|
||||
BuildSharedComponentArray(sharedComponentArray, selectionEntityTypeInfo != SelectionEntityTypeInfo::OnlyStandardEntities);
|
||||
BuildSharedComponentArray(sharedComponentArray,
|
||||
!(selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyStandardEntities || selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities));
|
||||
|
||||
if (sharedComponentArray.size() == 0)
|
||||
{
|
||||
|
||||
+11
@@ -124,4 +124,15 @@ namespace AzToolsFramework
|
||||
|
||||
return cameraState;
|
||||
}
|
||||
|
||||
float GetScreenDisplayScaling(const int viewportId)
|
||||
{
|
||||
float scaling = 1.0f;
|
||||
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
|
||||
scaling, viewportId,
|
||||
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
|
||||
|
||||
return scaling;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+3
@@ -60,6 +60,9 @@ namespace AzToolsFramework
|
||||
/// Wrapper for EBus call to return the CameraState for a given viewport.
|
||||
AzFramework::CameraState GetCameraState(int viewportId);
|
||||
|
||||
/// Wrapper for EBus call to return the DPI scaling for a given viewport.
|
||||
float GetScreenDisplayScaling(const int viewportId);
|
||||
|
||||
/// A utility to return the center of several points.
|
||||
/// Take several positions and store the min and max of each in
|
||||
/// turn - when all points have been added return the center/midpoint.
|
||||
|
||||
+4
-3
@@ -3573,9 +3573,10 @@ namespace AzToolsFramework
|
||||
debugDisplay.SetLineWidth(1.0f);
|
||||
|
||||
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const float screenScale = GetScreenDisplayScaling(viewportId);
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
|
||||
// draw the label of of each axis for the gizmo
|
||||
const float labelSize = cl_viewportGizmoAxisLabelSize;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Launcher.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
@@ -22,6 +23,8 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/IO/RemoteStorageDrive.h>
|
||||
#include <AzFramework/Windowing/NativeWindow.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
#include <AzGameFramework/Application/GameApplication.h>
|
||||
|
||||
@@ -45,6 +48,19 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
|
||||
|
||||
namespace
|
||||
{
|
||||
void OnViewportResize(const AZ::Vector2& value);
|
||||
|
||||
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default size for the launcher viewport, 0 0 means full screen");
|
||||
|
||||
void OnViewportResize(const AZ::Vector2& value)
|
||||
{
|
||||
AzFramework::NativeWindowHandle windowHandle = nullptr;
|
||||
AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
|
||||
AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast<int32_t>(value.GetX()), aznumeric_cast<int32_t>(value.GetY()));
|
||||
AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize);
|
||||
}
|
||||
|
||||
void ExecuteConsoleCommandFile(AzFramework::Application& application)
|
||||
{
|
||||
const AZStd::string_view customConCmdKey = "console-command-file";
|
||||
|
||||
@@ -1233,7 +1233,7 @@ void EditorViewportWidget::SetViewportId(int id)
|
||||
|
||||
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
|
||||
controller->SetCameraListBuilderCallback(
|
||||
[](AzFramework::Cameras& cameras)
|
||||
[id](AzFramework::Cameras& cameras)
|
||||
{
|
||||
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
|
||||
auto firstPersonPanCamera =
|
||||
@@ -1243,17 +1243,17 @@ void EditorViewportWidget::SetViewportId(int id)
|
||||
|
||||
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
|
||||
orbitCamera->SetLookAtFn(
|
||||
[](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
|
||||
[id](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
|
||||
{
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
lookAtAfterInterpolation, id,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
|
||||
|
||||
// initially attempt to use manipulator transform if one exists (there is a selection)
|
||||
if (manipulatorTransform)
|
||||
// initially attempt to use the last set look at point after an interpolation has finished
|
||||
if (lookAtAfterInterpolation.has_value())
|
||||
{
|
||||
return manipulatorTransform->GetTranslation();
|
||||
return *lookAtAfterInterpolation;
|
||||
}
|
||||
|
||||
const float RayDistance = 1000.0f;
|
||||
|
||||
@@ -553,7 +553,7 @@ void CLogFile::OnWriteToConsole(const char* sText, bool bNewLine)
|
||||
|
||||
// remember selection and the top row
|
||||
int len = m_hWndEditBox->document()->toPlainText().length();
|
||||
int top;
|
||||
int top = 0;
|
||||
int from = m_hWndEditBox->textCursor().selectionStart();
|
||||
int to = from + m_hWndEditBox->textCursor().selectionEnd();
|
||||
bool keepPos = false;
|
||||
|
||||
@@ -121,7 +121,7 @@ protected:
|
||||
};
|
||||
#endif
|
||||
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_instance)
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance)
|
||||
|
||||
|
||||
QWidget* QtViewPane::CreateWidget()
|
||||
@@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name)
|
||||
|
||||
QtViewPaneManager* QtViewPaneManager::instance()
|
||||
{
|
||||
return s_instance();
|
||||
return s_viewPaneManagerInstance();
|
||||
}
|
||||
|
||||
bool QtViewPaneManager::exists()
|
||||
{
|
||||
return s_instance.exists();
|
||||
return s_viewPaneManagerInstance.exists();
|
||||
}
|
||||
|
||||
void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState)
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace
|
||||
{
|
||||
int fps;
|
||||
const char* fpsDesc;
|
||||
} fps[] = {
|
||||
} fpsOptions[] = {
|
||||
{24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"},
|
||||
{48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"}
|
||||
};
|
||||
@@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog()
|
||||
m_ui->m_resolutionCombo->setCurrentIndex(0);
|
||||
|
||||
// Fill the FPS combo box.
|
||||
for (int i = 0; i < AZStd::size(fps); ++i)
|
||||
for (int i = 0; i < AZStd::size(fpsOptions); ++i)
|
||||
{
|
||||
m_ui->m_fpsCombo->addItem(fps[i].fpsDesc);
|
||||
m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc);
|
||||
}
|
||||
m_ui->m_fpsCombo->setCurrentIndex(0);
|
||||
|
||||
@@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
|
||||
m_ui->m_destinationEdit->setText(item.folder);
|
||||
// fps
|
||||
bool bFound = false;
|
||||
for (int i = 0; i < arraysize(fps); ++i)
|
||||
for (int i = 0; i < arraysize(fpsOptions); ++i)
|
||||
{
|
||||
if (item.fps == fps[i].fps)
|
||||
if (item.fps == fpsOptions[i].fps)
|
||||
{
|
||||
m_ui->m_fpsCombo->setCurrentIndex(i);
|
||||
bFound = true;
|
||||
@@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange()
|
||||
|
||||
void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex)
|
||||
{
|
||||
m_customFPS = fps[itemIndex].fps;
|
||||
m_customFPS = fpsOptions[itemIndex].fps;
|
||||
CheckForEnableUpdateButton();
|
||||
}
|
||||
|
||||
@@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item)
|
||||
item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion,
|
||||
m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion);
|
||||
// fps
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
{
|
||||
item.fps = m_customFPS;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
}
|
||||
// prefix
|
||||
item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text();
|
||||
|
||||
@@ -157,7 +157,7 @@ static Quatern Qt_FromMatrix(HMatrix mat)
|
||||
* |w| is greater than 1/2, which is as small as a largest component can be.
|
||||
* Otherwise, the largest diagonal entry corresponds to the largest of |x|,
|
||||
* |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */
|
||||
Quatern qu;
|
||||
Quatern qu = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
double tr, s;
|
||||
|
||||
tr = mat[X][X] + mat[Y][Y] + mat[Z][Z];
|
||||
@@ -531,7 +531,7 @@ Quatern snuggle(Quatern q, HVect* k)
|
||||
#define swap(a, i, j) {a[3] = a[i]; a[i] = a[j]; a[j] = a[3]; }
|
||||
#define cycle(a, p) if (p) {a[3] = a[0]; a[0] = a[1]; a[1] = a[2]; a[2] = a[3]; } \
|
||||
else {a[3] = a[2]; a[2] = a[1]; a[1] = a[0]; a[0] = a[3]; }
|
||||
Quatern p;
|
||||
Quatern p = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
float ka[4];
|
||||
int i, turn = -1;
|
||||
ka[X] = k->x;
|
||||
|
||||
@@ -2239,7 +2239,8 @@ uint32 CFileUtil::GetAttributes(const char* filename, bool bUseSourceControl /*=
|
||||
bool CFileUtil::CompareFiles(const QString& strFilePath1, const QString& strFilePath2)
|
||||
{
|
||||
// Get the size of both files. If either fails we say they are different (most likely one doesn't exist)
|
||||
uint64 size1, size2;
|
||||
uint64 size1 = 0;
|
||||
uint64 size2 = 0;
|
||||
if (!GetDiskFileSize(strFilePath1.toUtf8().data(), size1) || !GetDiskFileSize(strFilePath2.toUtf8().data(), size2))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -116,6 +116,7 @@ bool CImageBT::Load(const QString& fileName, CFloatImage& image)
|
||||
|
||||
// Get the BT header data
|
||||
BtHeader header;
|
||||
memset(&header, 0, sizeof(BtHeader)); // C4701 potentially uninitialized local variable 'header' used
|
||||
bool validData = true;
|
||||
validData = validData && (fread(&header, sizeof(BtHeader), 1, file) != 0);
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wi
|
||||
const typename TS::value_type* savedStrBegin = 0;
|
||||
const typename TS::value_type* savedStrEnd = 0;
|
||||
const typename TS::value_type* savedWild = 0;
|
||||
size_t savedWildCount;
|
||||
size_t savedWildCount = 0;
|
||||
|
||||
const typename TS::value_type* pStr = str.c_str();
|
||||
const typename TS::value_type* pWild = wildcards.c_str();
|
||||
|
||||
@@ -179,11 +179,11 @@ ComponentEntityEditorPlugin::ComponentEntityEditorPlugin([[maybe_unused]] IEdito
|
||||
LyViewPane::EntityOutliner,
|
||||
LyViewPane::CategoryTools,
|
||||
outlinerOptions);
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewPaneOptions options;
|
||||
options.preferedDockingArea = Qt::NoDockWidgetArea;
|
||||
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
|
||||
AzToolsFramework::ViewPaneOptions options;
|
||||
options.preferedDockingArea = Qt::NoDockWidgetArea;
|
||||
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
|
||||
}
|
||||
|
||||
RegisterModuleResourceSelectors(GetIEditor()->GetResourceSelectorHost());
|
||||
|
||||
|
||||
@@ -1732,13 +1732,14 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
|
||||
// compute new camera transform
|
||||
const float fov = AzFramework::RetrieveFov(viewportContext->GetCameraProjectionMatrix());
|
||||
const float fovScale = (1.0f / AZStd::tan(fov * 0.5f));
|
||||
const float distanceToTarget = selectionSize * fovScale * centerScale;
|
||||
const float distanceToLookAt = selectionSize * fovScale * centerScale;
|
||||
const AZ::Transform nextCameraTransform =
|
||||
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToTarget), aabb.GetCenter());
|
||||
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToLookAt), aabb.GetCenter());
|
||||
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
viewportContext->GetId(),
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform,
|
||||
distanceToLookAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <NewProjectSettingsScreen.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
@@ -42,9 +41,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_stack = new QStackedWidget(this);
|
||||
m_stack->setObjectName("body");
|
||||
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding));
|
||||
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
|
||||
m_stack->addWidget(new NewProjectSettingsScreen());
|
||||
m_stack->addWidget(new GemCatalogScreen());
|
||||
m_gemCatalog = new GemCatalogScreen();
|
||||
m_stack->addWidget(m_gemCatalog);
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
|
||||
@@ -88,6 +88,7 @@ namespace O3DE::ProjectManager
|
||||
emit GotoPreviousScreenRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleNextButton()
|
||||
{
|
||||
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
|
||||
@@ -106,6 +107,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_projectInfo = newProjectScreen->GetProjectInfo();
|
||||
m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath();
|
||||
|
||||
// The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog.
|
||||
m_gemCatalog->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +133,9 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
|
||||
}
|
||||
|
||||
// Enable/disable gems for the newly created project.
|
||||
m_gemCatalog->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenWidget.h>
|
||||
#include <ProjectInfo.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
|
||||
@@ -48,6 +49,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QString m_projectTemplatePath;
|
||||
ProjectInfo m_projectInfo;
|
||||
|
||||
GemCatalogScreen* m_gemCatalog = nullptr;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
#include <GemCatalog/GemCatalogHeaderWidget.h>
|
||||
#include <GemCatalog/GemListHeaderWidget.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <GemCatalog/GemFilterWidget.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
//#define USE_TESTGEMDATA
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -29,47 +28,32 @@ namespace O3DE::ProjectManager
|
||||
: ScreenWidget(parent)
|
||||
{
|
||||
m_gemModel = new GemModel(this);
|
||||
GemSortFilterProxyModel* proxyModel = new GemSortFilterProxyModel(m_gemModel, this);
|
||||
m_proxModel = new GemSortFilterProxyModel(m_gemModel, this);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setMargin(0);
|
||||
vLayout->setSpacing(0);
|
||||
setLayout(vLayout);
|
||||
|
||||
GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(proxyModel);
|
||||
GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(m_proxModel);
|
||||
vLayout->addWidget(headerWidget);
|
||||
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setMargin(0);
|
||||
vLayout->addLayout(hLayout);
|
||||
|
||||
m_gemListView = new GemListView(proxyModel, proxyModel->GetSelectionModel(), this);
|
||||
m_gemListView = new GemListView(m_proxModel, m_proxModel->GetSelectionModel(), this);
|
||||
m_gemInspector = new GemInspector(m_gemModel, this);
|
||||
m_gemInspector->setFixedWidth(320);
|
||||
m_gemInspector->setFixedWidth(240);
|
||||
|
||||
// Start: Temporary gem test data
|
||||
#ifdef USE_TESTGEMDATA
|
||||
QVector<GemInfo> testGemData = GenerateTestData();
|
||||
for (const GemInfo& gemInfo : testGemData)
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
#else
|
||||
// End: Temporary gem test data
|
||||
auto result = PythonBindingsInterface::Get()->GetGems();
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
for (auto gemInfo : result.GetValue())
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
QWidget* filterWidget = new QWidget(this);
|
||||
filterWidget->setFixedWidth(240);
|
||||
m_filterWidgetLayout = new QVBoxLayout();
|
||||
m_filterWidgetLayout->setMargin(0);
|
||||
m_filterWidgetLayout->setSpacing(0);
|
||||
filterWidget->setLayout(m_filterWidgetLayout);
|
||||
|
||||
GemFilterWidget* filterWidget = new GemFilterWidget(proxyModel);
|
||||
filterWidget->setFixedWidth(250);
|
||||
|
||||
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(proxyModel);
|
||||
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxModel);
|
||||
|
||||
QVBoxLayout* middleVLayout = new QVBoxLayout();
|
||||
middleVLayout->setMargin(0);
|
||||
@@ -80,98 +64,111 @@ namespace O3DE::ProjectManager
|
||||
hLayout->addWidget(filterWidget);
|
||||
hLayout->addLayout(middleVLayout);
|
||||
hLayout->addWidget(m_gemInspector);
|
||||
|
||||
proxyModel->InvalidateFilter();
|
||||
}
|
||||
|
||||
QVector<GemInfo> GemCatalogScreen::GenerateTestData()
|
||||
void GemCatalogScreen::ReinitForProject(const QString& projectPath, bool isNewProject)
|
||||
{
|
||||
QVector<GemInfo> result;
|
||||
m_gemModel->clear();
|
||||
FillModel(projectPath, isNewProject);
|
||||
|
||||
GemInfo gem("EMotion FX",
|
||||
"O3DE Foundation",
|
||||
"EMFX is a real-time character animation system. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
(GemInfo::Android | GemInfo::iOS | GemInfo::macOS | GemInfo::Windows | GemInfo::Linux),
|
||||
true);
|
||||
gem.m_directoryLink = "C:/";
|
||||
gem.m_documentationLink = "http://www.amazon.com";
|
||||
gem.m_dependingGemUuids = QStringList({"EMotionFX", "Atom"});
|
||||
gem.m_conflictingGemUuids = QStringList({"Vegetation", "Camera", "ScriptCanvas", "CloudCanvas", "Networking"});
|
||||
gem.m_types = (GemInfo::Code | GemInfo::Asset);
|
||||
gem.m_version = "v1.01";
|
||||
gem.m_lastUpdatedDate = "24th April 2021";
|
||||
gem.m_binarySizeInKB = 40;
|
||||
gem.m_features = QStringList({"Animation", "Assets", "Physics"});
|
||||
gem.m_gemOrigin = GemInfo::O3DEFoundation;
|
||||
result.push_back(gem);
|
||||
if (m_filterWidget)
|
||||
{
|
||||
m_filterWidget->hide();
|
||||
m_filterWidget->deleteLater();
|
||||
}
|
||||
|
||||
gem.m_name = "Atom";
|
||||
gem.m_creator = "O3DE Seattle";
|
||||
gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||
gem.m_platforms = (GemInfo::Android | GemInfo::Windows | GemInfo::Linux | GemInfo::macOS);
|
||||
gem.m_isAdded = true;
|
||||
gem.m_directoryLink = "C:/";
|
||||
gem.m_documentationLink = "https://aws.amazon.com/gametech/";
|
||||
gem.m_dependingGemUuids = QStringList({"EMotionFX", "Core", "AudioSystem", "Camera", "Particles"});
|
||||
gem.m_conflictingGemUuids = QStringList({"CloudCanvas", "NovaNet"});
|
||||
gem.m_version = "v2.31";
|
||||
gem.m_lastUpdatedDate = "24th November 2020";
|
||||
gem.m_features = QStringList({"Assets", "Rendering", "UI", "VR", "Debug", "Environment"});
|
||||
gem.m_binarySizeInKB = 2087;
|
||||
result.push_back(gem);
|
||||
m_filterWidget = new GemFilterWidget(m_proxModel);
|
||||
m_filterWidgetLayout->addWidget(m_filterWidget);
|
||||
|
||||
gem.m_name = "Physics";
|
||||
gem.m_creator = "O3DE London";
|
||||
gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||
gem.m_platforms = (GemInfo::Android | GemInfo::Linux | GemInfo::macOS);
|
||||
gem.m_isAdded = true;
|
||||
gem.m_directoryLink = "C:/";
|
||||
gem.m_documentationLink = "https://aws.amazon.com/gametech/";
|
||||
gem.m_dependingGemUuids = QStringList({"GraphCanvas", "ExpressionEvaluation", "UI Lib", "Multiplayer", "GameStateSamples"});
|
||||
gem.m_conflictingGemUuids = QStringList({"Cloud Canvas", "EMotion FX", "Streaming", "MessagePopup", "Cloth", "Graph Canvas", "Twitch Integration"});
|
||||
gem.m_version = "v1.5.102145";
|
||||
gem.m_lastUpdatedDate = "1st January 2021";
|
||||
gem.m_binarySizeInKB = 2000000;
|
||||
gem.m_features = QStringList({"Physics", "Gameplay", "Debug", "Assets"});
|
||||
result.push_back(gem);
|
||||
m_proxModel->InvalidateFilter();
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Certificate Manager",
|
||||
"O3DE Irvine",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
GemInfo::Windows,
|
||||
false));
|
||||
// Select the first entry after everything got correctly sized
|
||||
QTimer::singleShot(200, [=]{
|
||||
QModelIndex firstModelIndex = m_gemListView->model()->index(0,0);
|
||||
m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
}
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Framework",
|
||||
"O3DE Seattle",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
GemInfo::iOS | GemInfo::Linux,
|
||||
false));
|
||||
void GemCatalogScreen::FillModel(const QString& projectPath, bool isNewProject)
|
||||
{
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult;
|
||||
if (isNewProject)
|
||||
{
|
||||
allGemInfosResult = PythonBindingsInterface::Get()->GetEngineGemInfos();
|
||||
}
|
||||
else
|
||||
{
|
||||
allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
|
||||
}
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Core",
|
||||
"O3DE Foundation",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
|
||||
true));
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
// Add all available gems to the model.
|
||||
const QVector<GemInfo> allGemInfos = allGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allGemInfos)
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Gestures",
|
||||
"O3DE Foundation",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
|
||||
false));
|
||||
// Gather enabled gems for the given project.
|
||||
auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
|
||||
if (enabledGemNamesResult.IsSuccess())
|
||||
{
|
||||
const QVector<AZStd::string> enabledGemNames = enabledGemNamesResult.GetValue();
|
||||
for (const AZStd::string& enabledGemName : enabledGemNames)
|
||||
{
|
||||
const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str());
|
||||
if (modelIndex.isValid())
|
||||
{
|
||||
GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true);
|
||||
GemModel::SetIsAdded(*m_gemModel, modelIndex, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("ProjectManager::GemCatalog", false,
|
||||
"Cannot find entry for gem with name '%s'. The CMake target name probably does not match the specified name in the gem.json.",
|
||||
enabledGemName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve enabled gems for project %1.\n\nError:\n%2").arg(projectPath, enabledGemNamesResult.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve gems for %1.\n\nError:\n%2").arg(projectPath, allGemInfosResult.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Effects System",
|
||||
"O3DE Foundation",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
|
||||
true));
|
||||
void GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
|
||||
{
|
||||
IPythonBindings* pythonBindings = PythonBindingsInterface::Get();
|
||||
QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
|
||||
result.push_back(O3DE::ProjectManager::GemInfo("Microphone",
|
||||
"O3DE Foundation",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus euismod ligula vitae dui dictum, a sodales dolor luctus. Sed id elit dapibus, finibus neque sed, efficitur mi. Nam facilisis ligula at eleifend pellentesque. Praesent non ex consectetur, blandit tellus in, venenatis lacus. Duis nec neque in urna ullamcorper euismod id eu leo. Nam efficitur dolor sed odio vehicula venenatis. Suspendisse nec est non velit commodo cursus in sit amet dui. Ut bibendum nisl et libero hendrerit dapibus. Vestibulum ultrices ullamcorper urna, placerat porttitor est lobortis in. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer a magna ac tellus sollicitudin porttitor. Phasellus lobortis viverra justo id bibendum. Etiam ac pharetra risus. Nulla vitae justo nibh. Nulla viverra leo et molestie interdum. Duis sit amet bibendum nulla, sit amet vehicula augue.",
|
||||
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
|
||||
false));
|
||||
for (const QModelIndex& modelIndex : toBeAdded)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
for (const QModelIndex& modelIndex : toBeRemoved)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenWidget.h>
|
||||
#include <GemCatalog/GemFilterWidget.h>
|
||||
#include <GemCatalog/GemListView.h>
|
||||
#include <GemCatalog/GemInspector.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
@@ -29,11 +31,17 @@ namespace O3DE::ProjectManager
|
||||
~GemCatalogScreen() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
void ReinitForProject(const QString& projectPath, bool isNewProject);
|
||||
void EnableDisableGemsForProject(const QString& projectPath);
|
||||
|
||||
private:
|
||||
QVector<GemInfo> GenerateTestData();
|
||||
void FillModel(const QString& projectPath, bool isNewProject);
|
||||
|
||||
GemListView* m_gemListView = nullptr;
|
||||
GemInspector* m_gemInspector = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
GemSortFilterProxyModel* m_proxModel = nullptr;
|
||||
QVBoxLayout* m_filterWidgetLayout = nullptr;
|
||||
GemFilterWidget* m_filterWidget = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -79,4 +79,9 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
return (m_platforms & platform);
|
||||
}
|
||||
|
||||
bool GemInfo::operator<(const GemInfo& gemInfo) const
|
||||
{
|
||||
return (m_displayName < gemInfo.m_displayName);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -61,6 +61,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
bool operator<(const GemInfo& gemInfo) const;
|
||||
|
||||
QString m_path;
|
||||
QString m_name = "Unknown Gem Name";
|
||||
QString m_displayName = "Unknown Gem Name";
|
||||
|
||||
@@ -283,12 +283,16 @@ namespace O3DE::ProjectManager
|
||||
AZ_Warning("ProjectManagerWindow", result != -1, "Append to sys path failed");
|
||||
|
||||
// import required modules
|
||||
m_cmake = pybind11::module::import("o3de.cmake");
|
||||
m_register = pybind11::module::import("o3de.register");
|
||||
m_manifest = pybind11::module::import("o3de.manifest");
|
||||
m_engineTemplate = pybind11::module::import("o3de.engine_template");
|
||||
m_enableGemProject = pybind11::module::import("o3de.enable_gem");
|
||||
m_disableGemProject = pybind11::module::import("o3de.disable_gem");
|
||||
|
||||
// make sure the engine is registered
|
||||
RegisterThisEngine();
|
||||
|
||||
return result == 0 && !PyErr_Occurred();
|
||||
} catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
@@ -311,7 +315,37 @@ namespace O3DE::ProjectManager
|
||||
return !PyErr_Occurred();
|
||||
}
|
||||
|
||||
bool PythonBindings::ExecuteWithLock(AZStd::function<void()> executionCallback)
|
||||
bool PythonBindings::RegisterThisEngine()
|
||||
{
|
||||
bool registrationResult = true; // already registered is considered successful
|
||||
bool pythonResult = ExecuteWithLock(
|
||||
[&]
|
||||
{
|
||||
// check current engine path against all other registered engines
|
||||
// to see if we are already registered
|
||||
auto allEngines = m_manifest.attr("get_engines")();
|
||||
if (pybind11::isinstance<pybind11::list>(allEngines))
|
||||
{
|
||||
for (auto engine : allEngines)
|
||||
{
|
||||
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
|
||||
if (enginePath.Compare(m_enginePath) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto result = m_register.attr("register")(m_enginePath.c_str());
|
||||
registrationResult = (result.cast<int>() == 0);
|
||||
});
|
||||
|
||||
bool finalResult = (registrationResult && pythonResult);
|
||||
AZ_Assert(finalResult, "Registration of this engine failed!");
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback)
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
|
||||
pybind11::gil_scoped_release release;
|
||||
@@ -320,13 +354,19 @@ namespace O3DE::ProjectManager
|
||||
try
|
||||
{
|
||||
executionCallback();
|
||||
return true;
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
AZ_Warning("PythonBindings", false, "Python exception %s", e.what());
|
||||
return false;
|
||||
return AZ::Failure<AZStd::string>(e.what());
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
bool PythonBindings::ExecuteWithLock(AZStd::function<void()> executionCallback)
|
||||
{
|
||||
return ExecuteWithLockErrorHandling(executionCallback).IsSuccess();
|
||||
}
|
||||
|
||||
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
|
||||
@@ -419,7 +459,7 @@ namespace O3DE::ProjectManager
|
||||
return result;
|
||||
}
|
||||
|
||||
AZ::Outcome<GemInfo> PythonBindings::GetGem(const QString& path)
|
||||
AZ::Outcome<GemInfo> PythonBindings::GetGemInfo(const QString& path)
|
||||
{
|
||||
GemInfo gemInfo = GemInfoFromPath(pybind11::str(path.toStdString()));
|
||||
if (gemInfo.IsValid())
|
||||
@@ -432,32 +472,79 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<GemInfo>> PythonBindings::GetGems()
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetEngineGemInfos()
|
||||
{
|
||||
QVector<GemInfo> gems;
|
||||
|
||||
bool result = ExecuteWithLock([&] {
|
||||
// external gems
|
||||
for (auto path : m_manifest.attr("get_gems")())
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
for (auto path : m_manifest.attr("get_engine_gems")())
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
}
|
||||
|
||||
// gems from the engine
|
||||
for (auto path : m_manifest.attr("get_engine_gems")())
|
||||
std::sort(gems.begin(), gems.end());
|
||||
return AZ::Success(AZStd::move(gems));
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetAllGemInfos(const QString& projectPath)
|
||||
{
|
||||
QVector<GemInfo> gems;
|
||||
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
}
|
||||
|
||||
if (!result)
|
||||
std::sort(gems.begin(), gems.end());
|
||||
return AZ::Success(AZStd::move(gems));
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<AZStd::string>, AZStd::string> PythonBindings::GetEnabledGemNames(const QString& projectPath)
|
||||
{
|
||||
// Retrieve the path to the cmake file that lists the enabled gems.
|
||||
pybind11::str enabledGemsFilename;
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
const pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
|
||||
pybind11::none(), // project_name
|
||||
pyProjectPath); // project_path
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure();
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
}
|
||||
else
|
||||
|
||||
// Retrieve the actual list of names from the cmake file.
|
||||
QVector<AZStd::string> gemNames;
|
||||
result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
|
||||
for (auto gemName : pyGemNames)
|
||||
{
|
||||
gemNames.push_back(Py_To_String(gemName));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Success(AZStd::move(gems));
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
}
|
||||
|
||||
return AZ::Success(AZStd::move(gemNames));
|
||||
}
|
||||
|
||||
bool PythonBindings::AddProject(const QString& path)
|
||||
@@ -637,38 +724,36 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
bool PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath)
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
bool result = ExecuteWithLock([&] {
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
return ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
|
||||
m_enableGemProject.attr("enable_gem_in_project")(
|
||||
pybind11::none(), // gem_name
|
||||
pyGemPath,
|
||||
pybind11::none(), // project_name
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
m_enableGemProject.attr("enable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
bool PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath)
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
bool result = ExecuteWithLock([&] {
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
return ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
|
||||
m_disableGemProject.attr("disable_gem_in_project")(
|
||||
pybind11::none(), // gem_name
|
||||
pyGemPath,
|
||||
pybind11::none(), // project_name
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
m_disableGemProject.attr("disable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
|
||||
|
||||
@@ -39,8 +39,10 @@ namespace O3DE::ProjectManager
|
||||
bool SetEngineInfo(const EngineInfo& engineInfo) override;
|
||||
|
||||
// Gem
|
||||
AZ::Outcome<GemInfo> GetGem(const QString& path) override;
|
||||
AZ::Outcome<QVector<GemInfo>> GetGems() override;
|
||||
AZ::Outcome<GemInfo> GetGemInfo(const QString& path) override;
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetEngineGemInfos() override;
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemInfos(const QString& projectPath) override;
|
||||
AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) override;
|
||||
|
||||
// Project
|
||||
AZ::Outcome<ProjectInfo> CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) override;
|
||||
@@ -49,8 +51,8 @@ namespace O3DE::ProjectManager
|
||||
bool AddProject(const QString& path) override;
|
||||
bool RemoveProject(const QString& path) override;
|
||||
bool UpdateProject(const ProjectInfo& projectInfo) override;
|
||||
bool AddGemToProject(const QString& gemPath, const QString& projectPath) override;
|
||||
bool RemoveGemFromProject(const QString& gemPath, const QString& projectPath) override;
|
||||
AZ::Outcome<void, AZStd::string> AddGemToProject(const QString& gemPath, const QString& projectPath) override;
|
||||
AZ::Outcome<void, AZStd::string> RemoveGemFromProject(const QString& gemPath, const QString& projectPath) override;
|
||||
|
||||
// ProjectTemplate
|
||||
AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates() override;
|
||||
@@ -58,16 +60,20 @@ namespace O3DE::ProjectManager
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PythonBindings);
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback);
|
||||
bool ExecuteWithLock(AZStd::function<void()> executionCallback);
|
||||
GemInfo GemInfoFromPath(pybind11::handle path);
|
||||
ProjectInfo ProjectInfoFromPath(pybind11::handle path);
|
||||
ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path);
|
||||
bool RegisterThisEngine();
|
||||
bool StartPython();
|
||||
bool StopPython();
|
||||
|
||||
|
||||
AZ::IO::FixedMaxPath m_enginePath;
|
||||
pybind11::handle m_engineTemplate;
|
||||
AZStd::recursive_mutex m_lock;
|
||||
pybind11::handle m_cmake;
|
||||
pybind11::handle m_register;
|
||||
pybind11::handle m_manifest;
|
||||
pybind11::handle m_enableGemProject;
|
||||
|
||||
@@ -57,13 +57,27 @@ namespace O3DE::ProjectManager
|
||||
* @param path the absolute path to the Gem
|
||||
* @return an outcome with GemInfo on success
|
||||
*/
|
||||
virtual AZ::Outcome<GemInfo> GetGem(const QString& path) = 0;
|
||||
virtual AZ::Outcome<GemInfo> GetGemInfo(const QString& path) = 0;
|
||||
|
||||
/**
|
||||
* Get info about all known Gems
|
||||
* @return an outcome with GemInfos on success
|
||||
* Get all available gem infos. This concatenates gems registered by the engine and the project.
|
||||
* @param path The absolute path to the project.
|
||||
* @return A list of gem infos.
|
||||
*/
|
||||
virtual AZ::Outcome<QVector<GemInfo>> GetGems() = 0;
|
||||
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemInfos(const QString& projectPath) = 0;
|
||||
|
||||
/**
|
||||
* Get engine gem infos.
|
||||
* @return A list of all registered gem infos.
|
||||
*/
|
||||
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetEngineGemInfos() = 0;
|
||||
|
||||
/**
|
||||
* Get a list of all enabled gem names for a given project.
|
||||
* @param[in] projectPath Absolute file path to the project.
|
||||
* @return A list of gem names of all the enabled gems for a given project or a error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) = 0;
|
||||
|
||||
|
||||
// Projects
|
||||
@@ -114,17 +128,17 @@ namespace O3DE::ProjectManager
|
||||
* Add a gem to a project
|
||||
* @param gemPath the absolute path to the gem
|
||||
* @param projectPath the absolute path to the project
|
||||
* @return true on success, false on failure
|
||||
* @return An outcome with the success flag as well as an error message in case of a failure.
|
||||
*/
|
||||
virtual bool AddGemToProject(const QString& gemPath, const QString& projectPath) = 0;
|
||||
virtual AZ::Outcome<void, AZStd::string> AddGemToProject(const QString& gemPath, const QString& projectPath) = 0;
|
||||
|
||||
/**
|
||||
* Remove gem to a project
|
||||
* @param gemPath the absolute path to the gem
|
||||
* @param projectPath the absolute path to the project
|
||||
* @return true on success, false on failure
|
||||
* @return An outcome with the success flag as well as an error message in case of a failure.
|
||||
*/
|
||||
virtual bool RemoveGemFromProject(const QString& gemPath, const QString& projectPath) = 0;
|
||||
virtual AZ::Outcome<void, AZStd::string> RemoveGemFromProject(const QString& gemPath, const QString& projectPath) = 0;
|
||||
|
||||
|
||||
// Project Templates
|
||||
|
||||
@@ -97,6 +97,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::HandleGemsButton()
|
||||
{
|
||||
// The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog.
|
||||
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/false);
|
||||
|
||||
m_stack->setCurrentWidget(m_gemCatalogScreen);
|
||||
Update();
|
||||
}
|
||||
@@ -113,6 +116,7 @@ namespace O3DE::ProjectManager
|
||||
emit GotoPreviousScreenRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::HandleNextButton()
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Settings)
|
||||
@@ -152,6 +156,12 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen)
|
||||
{
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
}
|
||||
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
|
||||
#include <SliceConverterEditorEntityContextComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -34,6 +35,9 @@ namespace AZ
|
||||
Application::Application(int argc, char** argv)
|
||||
: AzToolsFramework::ToolsApplication(&argc, &argv)
|
||||
{
|
||||
// We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here.
|
||||
RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor());
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
|
||||
if (projectPath.empty())
|
||||
{
|
||||
@@ -110,10 +114,21 @@ namespace AZ
|
||||
|
||||
AZ::ComponentTypeList Application::GetRequiredSystemComponents() const
|
||||
{
|
||||
// Use all of the default system components, but also add in the ThumbnailerNullComponent so that components requiring
|
||||
// a ThumbnailService can still be started up.
|
||||
// By default, we use all of the standard system components.
|
||||
AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents();
|
||||
|
||||
// Also add in the ThumbnailerNullComponent so that components requiring a ThumbnailService can still be started up.
|
||||
components.emplace_back(azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerNullComponent>());
|
||||
|
||||
// The Slice Converter requires a specialized variant of the EditorEntityContextComponent that exposes the ability
|
||||
// to disable the behavior of activating entities on creation. During conversion, the creation flow will be triggered,
|
||||
// but entity activation requires a significant amount of subsystem initialization that's unneeded for conversion.
|
||||
// So, to get around this, we swap out EditorEntityContextComponent with SliceConverterEditorEntityContextComponent.
|
||||
components.erase(
|
||||
AZStd::remove(
|
||||
components.begin(), components.end(), azrtti_typeid<AzToolsFramework::EditorEntityContextComponent>()),
|
||||
components.end());
|
||||
components.emplace_back(azrtti_typeid<AzToolsFramework::SliceConverterEditorEntityContextComponent>());
|
||||
return components;
|
||||
}
|
||||
} // namespace SerializeContextTools
|
||||
|
||||
@@ -30,13 +30,16 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <Application.h>
|
||||
#include <SliceConverter.h>
|
||||
#include <SliceConverterEditorEntityContextComponent.h>
|
||||
#include <Utilities.h>
|
||||
|
||||
|
||||
// SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data,
|
||||
// and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs.
|
||||
//
|
||||
@@ -99,12 +102,26 @@ namespace AZ
|
||||
bool result = true;
|
||||
rapidjson::StringBuffer scratchBuffer;
|
||||
|
||||
// For slice conversion, disable the EditorEntityContextComponent logic that activates entities on creation.
|
||||
// This prevents a lot of error messages and crashes during conversion due to lack of full environment and subsystem setup.
|
||||
AzToolsFramework::SliceConverterEditorEntityContextComponent::DisableOnContextEntityLogic();
|
||||
|
||||
// Loop through the list of requested files and convert them.
|
||||
AZStd::vector<AZStd::string> fileList = Utilities::ReadFileListFromCommandLine(application, "files");
|
||||
for (AZStd::string& filePath : fileList)
|
||||
{
|
||||
bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun);
|
||||
result = result && convertResult;
|
||||
|
||||
// Clear out all registered prefab templates between each top-level file that gets processed.
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
for (auto templateId : m_createdTemplateIds)
|
||||
{
|
||||
// We don't just want to call RemoveAllTemplates() because the root template should remain between file conversions.
|
||||
prefabSystemComponentInterface->RemoveTemplate(templateId);
|
||||
}
|
||||
m_aliasIdMapper.clear();
|
||||
m_createdTemplateIds.clear();
|
||||
}
|
||||
|
||||
DisconnectFromAssetProcessor();
|
||||
@@ -114,6 +131,13 @@ namespace AZ
|
||||
bool SliceConverter::ConvertSliceFile(
|
||||
AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
|
||||
{
|
||||
/* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert
|
||||
* the data in memory to a Prefab.
|
||||
* If the input file is a level file (.ly), we actually need to load the level slice file ("levelentities.editor_xml") from
|
||||
* within the level file, which effectively is a zip file of the level slice file and a bunch of legacy level files that won't
|
||||
* be converted, since the systems that would use them no longer exist.
|
||||
*/
|
||||
|
||||
bool result = true;
|
||||
bool packOpened = false;
|
||||
|
||||
@@ -144,7 +168,7 @@ namespace AZ
|
||||
AZ_STRING_ARG(fileExtension.Native()));
|
||||
}
|
||||
|
||||
auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
|
||||
auto callback = [this, &outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
|
||||
{
|
||||
if (classId != azrtti_typeid<AZ::Entity>())
|
||||
{
|
||||
@@ -178,6 +202,13 @@ namespace AZ
|
||||
bool SliceConverter::ConvertSliceToPrefab(
|
||||
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity)
|
||||
{
|
||||
/* Given a root slice entity, we convert it to a prefab by doing the following:
|
||||
* - Locate the SliceComponent
|
||||
* - Take all the entities directly located on the slice, and put them into a prefab
|
||||
* - Fix up any top-level entities to have the prefab container entity as their parent
|
||||
* - If there are any nested slice instances, convert the nested slices to prefabs, then convert the instances.
|
||||
*/
|
||||
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
// Find the slice from the root entity.
|
||||
@@ -192,9 +223,14 @@ namespace AZ
|
||||
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
|
||||
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
|
||||
|
||||
// Create the Prefab with the entities from the slice
|
||||
// Create the Prefab with the entities from the slice.
|
||||
// The entities are added in a separate step so that we can give them deterministic entity aliases that match their entity Ids
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
|
||||
prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath));
|
||||
prefabSystemComponentInterface->CreatePrefab({}, {}, outputPath));
|
||||
for (auto& entity : sliceEntities)
|
||||
{
|
||||
sourceInstance->AddEntity(*entity, AZStd::string::format("Entity_%s", entity->GetId().ToString().c_str()));
|
||||
}
|
||||
|
||||
// Dispatch events here, because prefab creation might trigger asset loads in rare circumstances.
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
@@ -204,12 +240,28 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
|
||||
FixPrefabEntities(container->get(), sliceEntities);
|
||||
|
||||
// Keep track of the template Id we created, we're going to remove it at the end of slice file conversion to make sure
|
||||
// the data doesn't stick around between file conversions.
|
||||
auto templateId = sourceInstance->GetTemplateId();
|
||||
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n");
|
||||
return false;
|
||||
}
|
||||
m_createdTemplateIds.emplace(templateId);
|
||||
|
||||
// Save off a mapping of the original slice entity IDs to the new prefab template entity aliases.
|
||||
// When converting nested slices, this mapping will be needed to fix up the parent entity hierarchy correctly.
|
||||
auto entityAliases = sourceInstance->GetEntityAliases();
|
||||
for (auto& alias : entityAliases)
|
||||
{
|
||||
auto id = sourceInstance->GetEntityId(alias);
|
||||
auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias);
|
||||
if (!result.second)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the prefab template with the fixed-up data in our prefab instance.
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
@@ -254,21 +306,26 @@ namespace AZ
|
||||
// via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.)
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity);
|
||||
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
|
||||
if (containerEntity.FindComponent<AzToolsFramework::Prefab::EditorPrefabComponent>() == nullptr)
|
||||
{
|
||||
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
|
||||
}
|
||||
|
||||
// Make all the components on the container entity have deterministic component IDs, so that multiple runs of the tool
|
||||
// on the same slice will produce the same prefab output. We're going to cheat a bit and just use the component type hash
|
||||
// as the component ID. This would break if we had multiple components of the same type, but that currently doesn't
|
||||
// happen for the container entity.
|
||||
auto containerComponents = containerEntity.GetComponents();
|
||||
for (auto& component : containerComponents)
|
||||
{
|
||||
component->SetId(component->GetUnderlyingComponentType().GetHash());
|
||||
}
|
||||
|
||||
// Reparent any root-level slice entities to the container entity.
|
||||
for (auto entity : sliceEntities)
|
||||
{
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
if (!transformComponent->GetParentId().IsValid())
|
||||
{
|
||||
transformComponent->SetParent(containerEntity.GetId());
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
}
|
||||
}
|
||||
constexpr bool onlySetIfInvalid = true;
|
||||
SetParentEntity(*entity, containerEntity.GetId(), onlySetIfInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,9 +333,13 @@ namespace AZ
|
||||
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
|
||||
AZ::SerializeContext* serializeContext, bool isDryRun)
|
||||
{
|
||||
/* Given a root slice, find all the nested slices and convert them. */
|
||||
|
||||
// Get the list of nested slices that this slice uses.
|
||||
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
// For each nested slice, convert it.
|
||||
for (auto& slice : sliceList)
|
||||
{
|
||||
// Get the nested slice asset
|
||||
@@ -312,7 +373,7 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the prefab template for the newly-created nested prefab.
|
||||
// Find the prefab template we created for the newly-created nested prefab.
|
||||
// To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path.
|
||||
AZ::IO::Path nestedPrefabPath = assetPath;
|
||||
nestedPrefabPath.ReplaceExtension("prefab");
|
||||
@@ -346,11 +407,25 @@ namespace AZ
|
||||
}
|
||||
|
||||
bool SliceConverter::ConvertSliceInstance(
|
||||
[[maybe_unused]] AZ::SliceComponent::SliceInstance& instance,
|
||||
[[maybe_unused]] AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AZ::SliceComponent::SliceInstance& instance,
|
||||
AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AzToolsFramework::Prefab::TemplateReference nestedTemplate,
|
||||
AzToolsFramework::Prefab::Instance* topLevelInstance)
|
||||
{
|
||||
/* To convert a slice instance, it's important to understand the similarities and differences between slices and prefabs.
|
||||
* Both slices and prefabs have the concept of instances of a nested slice/prefab, where each instance can have its own
|
||||
* set of changed data (transforms, component values, etc).
|
||||
* For slices, the changed data comes from applying a DataPatch to an instantiated set of entities from the nested slice.
|
||||
* From prefabs, the changed data comes from Json patches that are applied to the instantiated set of entities from the
|
||||
* nested prefab. The prefab instance entities also have different IDs than the slice instance entities, so we'll need
|
||||
* to remap some of them along the way.
|
||||
* To get from one to the other, we'll need to do the following:
|
||||
* - Instantiate the nested slice and nested prefab
|
||||
* - Patch the nested slice instance and fix up the entity ID references
|
||||
* - Replace the nested prefab instance entities with the fixed-up slice ones
|
||||
* - Add the nested instance (and the link patch) to the top-level prefab
|
||||
*/
|
||||
|
||||
auto instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
@@ -371,22 +446,83 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get()));
|
||||
|
||||
// Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will
|
||||
// be lost.
|
||||
AZ_Warning(
|
||||
"Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.",
|
||||
nestedTemplate->get().GetFilePath().c_str());
|
||||
// Instantiate a new instance of the nested slice
|
||||
SliceComponent* dependentSlice = sliceAsset.Get()->GetComponent();
|
||||
[[maybe_unused]] AZ::SliceComponent::InstantiateResult instantiationResult = dependentSlice->Instantiate();
|
||||
AZ_Assert(instantiationResult == AZ::SliceComponent::InstantiateResult::Success, "Failed to instantiate instance");
|
||||
|
||||
// Set the container entity of the nested prefab to have the top-level prefab as the parent.
|
||||
// Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity
|
||||
// within the level.
|
||||
// Apply the data patch for this instance of the nested slice. This will provide us with a version of the slice's entities
|
||||
// with all data overrides applied to them.
|
||||
DataPatch::FlagsMap sourceDataFlags = dependentSlice->GetDataFlagsForInstances().GetDataFlagsForPatching();
|
||||
DataPatch::FlagsMap targetDataFlags = instance.GetDataFlags().GetDataFlagsForPatching(&instance.GetEntityIdToBaseMap());
|
||||
AZ::ObjectStream::FilterDescriptor filterDesc(AZ::Data::AssetFilterNoAssetLoading);
|
||||
|
||||
AZ::SliceComponent::InstantiatedContainer sourceObjects(false);
|
||||
dependentSlice->GetEntities(sourceObjects.m_entities);
|
||||
dependentSlice->GetAllMetadataEntities(sourceObjects.m_metadataEntities);
|
||||
|
||||
const DataPatch& dataPatch = instance.GetDataPatch();
|
||||
auto instantiated =
|
||||
dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags);
|
||||
|
||||
// Run through all the instantiated entities and fix up their parent hierarchy:
|
||||
// - Invalid parents need to get set to the container.
|
||||
// - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity.
|
||||
// Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's
|
||||
// parent to that other instance.
|
||||
auto containerEntity = nestedInstance->GetContainerEntity();
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
containerEntity->get().FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
auto containerEntityId = containerEntity->get().GetId();
|
||||
for (auto entity : instantiated->m_entities)
|
||||
{
|
||||
transformComponent->SetParent(topLevelInstance->GetContainerEntityId());
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
bool onlySetIfInvalid = true;
|
||||
auto parentId = transformComponent->GetParentId();
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId));
|
||||
if (parentAlias != m_aliasIdMapper.end())
|
||||
{
|
||||
// Set the container's parent to this entity's parent, and set this entity's parent to the container
|
||||
// (i.e. go from A->B to A->container->B)
|
||||
auto newParentId = topLevelInstance->GetEntityId(parentAlias->second);
|
||||
SetParentEntity(containerEntity->get(), newParentId, false);
|
||||
onlySetIfInvalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace all the entities in the instance with the new patched ones.
|
||||
// (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us)
|
||||
nestedInstance->RemoveNestedEntities(
|
||||
[](const AZStd::unique_ptr<AZ::Entity>&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
for (auto& entity : instantiated->m_entities)
|
||||
{
|
||||
auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId()));
|
||||
if (entityAlias != m_aliasIdMapper.end())
|
||||
{
|
||||
nestedInstance->AddEntity(*entity, entityAlias->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to find entity alias.");
|
||||
nestedInstance->AddEntity(*entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten
|
||||
// another entity as its parent.
|
||||
{
|
||||
constexpr bool onlySetIfInvalid = true;
|
||||
SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid);
|
||||
}
|
||||
|
||||
// Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance,
|
||||
@@ -395,7 +531,22 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance);
|
||||
|
||||
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance));
|
||||
// When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one
|
||||
// will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce
|
||||
// the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains
|
||||
// a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the
|
||||
// unique instance ID.
|
||||
AZStd::string instanceAlias;
|
||||
auto entityIdMap = instance.GetEntityIdMap();
|
||||
if (!entityIdMap.empty())
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias);
|
||||
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance);
|
||||
@@ -418,9 +569,26 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::InvalidLinkId);
|
||||
prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId());
|
||||
|
||||
AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get()->UpdateTemplateInstancesInQueue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SliceConverter::SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid)
|
||||
{
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity.FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
// Only set the parent if we didn't set the onlySetIfInvalid flag, or if we did and the parent is currently invalid
|
||||
if (!onlySetIfInvalid || !transformComponent->GetParentId().IsValid())
|
||||
{
|
||||
transformComponent->SetParent(parentId);
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId)
|
||||
{
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
@@ -39,24 +39,35 @@ namespace AZ
|
||||
class SliceConverter : public Converter
|
||||
{
|
||||
public:
|
||||
static bool ConvertSliceFiles(Application& application);
|
||||
bool ConvertSliceFiles(Application& application);
|
||||
|
||||
private:
|
||||
static bool ConnectToAssetProcessor();
|
||||
static void DisconnectFromAssetProcessor();
|
||||
using TemplateEntityIdPair = AZStd::pair<AzToolsFramework::Prefab::TemplateId, AZ::EntityId>;
|
||||
|
||||
static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
|
||||
static bool ConvertSliceToPrefab(
|
||||
bool ConnectToAssetProcessor();
|
||||
void DisconnectFromAssetProcessor();
|
||||
|
||||
bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
|
||||
bool ConvertSliceToPrefab(
|
||||
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity);
|
||||
static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
|
||||
static bool ConvertNestedSlices(
|
||||
void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
|
||||
bool ConvertNestedSlices(
|
||||
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
|
||||
AZ::SerializeContext* serializeContext, bool isDryRun);
|
||||
static bool ConvertSliceInstance(
|
||||
bool ConvertSliceInstance(
|
||||
AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance);
|
||||
static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
|
||||
static bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
|
||||
void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid);
|
||||
void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
|
||||
bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
|
||||
|
||||
// Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used
|
||||
// with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs.
|
||||
AZStd::unordered_map<TemplateEntityIdPair, AzToolsFramework::Prefab::EntityAlias> m_aliasIdMapper;
|
||||
|
||||
// Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the
|
||||
// conversion for that file.
|
||||
AZStd::unordered_set<AzToolsFramework::Prefab::TemplateId> m_createdTemplateIds;
|
||||
};
|
||||
} // namespace SerializeContextTools
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// This class is an inelegant workaround for use by the Slice Converter to selectively disable entity add/remove logic
|
||||
// during slice conversion in the EditorEntityContextComponent. Specifically, the standard versions of these methods will
|
||||
// attempt to activate the entities as they're added. This is both unnecessary and undesirable during slice conversion, since
|
||||
// entity activation requires a lot of subsystems to be active and valid.
|
||||
// Instead, by selectively disabling this logic, the entities can remain in an initialized state, which is sufficient for conversion,
|
||||
// without requiring those extra subsystems.
|
||||
|
||||
// This problem also could have been solved by adding APIs to the EditorEntityContextComponent or the EntityContext, but there aren't
|
||||
// any other known valid use cases for disabling this logic, so the extra APIs would simply encourage "bad behavior" by using them
|
||||
// when they likely aren't necessary or desired.
|
||||
|
||||
class SliceConverterEditorEntityContextComponent
|
||||
: public EditorEntityContextComponent
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_COMPONENT(SliceConverterEditorEntityContextComponent, "{1CB0C38F-8E85-4422-91C6-E1F3B9B4B853}");
|
||||
|
||||
SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {}
|
||||
|
||||
// Simple API to selectively disable this logic *only* when performing slice to prefab conversion.
|
||||
static void DisableOnContextEntityLogic()
|
||||
{
|
||||
m_enableOnContextEntityLogic = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void OnContextEntitiesAdded([[maybe_unused]] const EntityList& entities) override
|
||||
{
|
||||
if (m_enableOnContextEntityLogic)
|
||||
{
|
||||
EditorEntityContextComponent::OnContextEntitiesAdded(entities);
|
||||
}
|
||||
}
|
||||
|
||||
void OnContextEntityRemoved([[maybe_unused]] const AZ::EntityId& id) override
|
||||
{
|
||||
if (m_enableOnContextEntityLogic)
|
||||
{
|
||||
EditorEntityContextComponent::OnContextEntityRemoved(id);
|
||||
}
|
||||
}
|
||||
|
||||
// By default, act just like the EditorEntityContextComponent
|
||||
static inline bool m_enableOnContextEntityLogic = true;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
@@ -125,7 +125,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (AZ::StringFunc::Equal("convert-slice", action.c_str()))
|
||||
{
|
||||
result = SliceConverter::ConvertSliceFiles(application);
|
||||
SliceConverter sliceConverter;
|
||||
result = sliceConverter.ConvertSliceFiles(application);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ set(FILES
|
||||
Dumper.h
|
||||
Dumper.cpp
|
||||
main.cpp
|
||||
SliceConverterEditorEntityContextComponent.h
|
||||
SliceConverter.h
|
||||
SliceConverter.cpp
|
||||
Utilities.h
|
||||
|
||||
@@ -56,9 +56,13 @@ ly_add_target(
|
||||
Gem::HttpRequestor
|
||||
)
|
||||
|
||||
# servers and clients use the above module.
|
||||
# Load the "Gem::AWSClientAuth" module in all types of applications.
|
||||
ly_create_alias(NAME AWSClientAuth.Servers NAMESPACE Gem TARGETS Gem::AWSClientAuth)
|
||||
ly_create_alias(NAME AWSClientAuth.Clients NAMESPACE Gem TARGETS Gem::AWSClientAuth)
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_create_alias(NAME AWSClientAuth.Tools NAMESPACE Gem TARGETS Gem::AWSClientAuth)
|
||||
ly_create_alias(NAME AWSClientAuth.Builders NAMESPACE Gem TARGETS Gem::AWSClientAuth)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
|
||||
@@ -79,14 +79,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Include/Private
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
AWSCORE_EDITOR
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
Gem::AWSCore.Static
|
||||
Gem::AWSCore.Editor.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::AWSCore
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AWSCoreModule.h>
|
||||
#include <AzCore/Module/Module.h>
|
||||
|
||||
namespace AWSCore
|
||||
{
|
||||
class AWSCoreEditorModule
|
||||
: public AWSCoreModule
|
||||
:public AZ::Module
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AWSCoreEditorModule, "{C1C9B898-848B-4C2F-A7AA-69642D12BCB5}", AWSCoreModule);
|
||||
AZ_RTTI(AWSCoreEditorModule, "{C1C9B898-848B-4C2F-A7AA-69642D12BCB5}", AZ::Module);
|
||||
AZ_CLASS_ALLOCATOR(AWSCoreEditorModule, AZ::SystemAllocator, 0);
|
||||
|
||||
AWSCoreEditorModule();
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
namespace AWSCore
|
||||
{
|
||||
AWSCoreEditorModule::AWSCoreEditorModule()
|
||||
: AWSCoreModule()
|
||||
{
|
||||
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
|
||||
m_descriptors.insert(m_descriptors.end(), {
|
||||
@@ -28,10 +27,9 @@ namespace AWSCore
|
||||
*/
|
||||
AZ::ComponentTypeList AWSCoreEditorModule::GetRequiredSystemComponents() const
|
||||
{
|
||||
AZ::ComponentTypeList requiredComponents = AWSCoreModule::GetRequiredSystemComponents();
|
||||
requiredComponents.push_back(azrtti_typeid<AWSCoreEditorSystemComponent>());
|
||||
|
||||
return requiredComponents;
|
||||
return AZ::ComponentTypeList{
|
||||
azrtti_typeid<AWSCoreEditorSystemComponent>()
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,9 +40,7 @@ namespace AWSCore
|
||||
|
||||
}
|
||||
|
||||
#if !defined(AWSCORE_EDITOR)
|
||||
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
|
||||
// The first parameter should be GemName_GemIdLower
|
||||
// The second should be the fully qualified name of the class above
|
||||
AZ_DECLARE_MODULE_CLASS(Gem_AWSCore, AWSCore::AWSCoreModule)
|
||||
#endif
|
||||
|
||||
@@ -48,7 +48,8 @@ class ConfigurationManager(object):
|
||||
def configuration(self, new_configuration: ConfigurationManager) -> None:
|
||||
self._configuration = new_configuration
|
||||
|
||||
def setup(self, config_path: str) -> None:
|
||||
def setup(self, config_path: str) -> bool:
|
||||
result: bool = True
|
||||
logger.info("Setting up default configuration ...")
|
||||
try:
|
||||
normalized_config_path: str = file_utils.normalize_file_path(config_path);
|
||||
@@ -63,5 +64,7 @@ class ConfigurationManager(object):
|
||||
self._configuration.account_id = aws_utils.get_default_account_id()
|
||||
self._configuration.region = aws_utils.get_default_region()
|
||||
except (RuntimeError, FileNotFoundError) as e:
|
||||
logger.exception(e)
|
||||
logger.error(e)
|
||||
result = False
|
||||
logger.debug(self._configuration)
|
||||
return result
|
||||
|
||||
@@ -74,11 +74,18 @@ if __name__ == "__main__":
|
||||
logger.warning("Failed to load style sheet for resource mapping tool")
|
||||
|
||||
logger.info("Initializing boto3 default session ...")
|
||||
aws_utils.setup_default_session(arguments.profile)
|
||||
try:
|
||||
aws_utils.setup_default_session(arguments.profile)
|
||||
except RuntimeError as error:
|
||||
logger.error(error)
|
||||
environment_utils.cleanup_qt_environment()
|
||||
exit(-1)
|
||||
|
||||
logger.info("Initializing configuration manager ...")
|
||||
configuration_manager: ConfigurationManager = ConfigurationManager()
|
||||
configuration_manager.setup(arguments.config_path)
|
||||
if not configuration_manager.setup(arguments.config_path):
|
||||
environment_utils.cleanup_qt_environment()
|
||||
exit(-1)
|
||||
|
||||
logger.info("Initializing thread manager ...")
|
||||
thread_manager: ThreadManager = ThreadManager()
|
||||
|
||||
+1238
-1124
File diff suppressed because it is too large
Load Diff
@@ -12,10 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
import boto3
|
||||
from botocore.paginate import (PageIterator, Paginator)
|
||||
from botocore.client import BaseClient
|
||||
from botocore.exceptions import ClientError
|
||||
from botocore.exceptions import (ClientError, ConfigNotFound, NoCredentialsError, ProfileNotFound)
|
||||
from typing import Dict, List
|
||||
|
||||
from model import (constants, error_messages)
|
||||
from model import error_messages
|
||||
from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder)
|
||||
|
||||
"""
|
||||
@@ -65,8 +65,11 @@ def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient:
|
||||
|
||||
|
||||
def setup_default_session(profile: str) -> None:
|
||||
global default_session
|
||||
default_session = boto3.session.Session(profile_name=profile)
|
||||
try:
|
||||
global default_session
|
||||
default_session = boto3.session.Session(profile_name=profile)
|
||||
except (ConfigNotFound, ProfileNotFound) as error:
|
||||
raise RuntimeError(error)
|
||||
|
||||
|
||||
def get_default_account_id() -> str:
|
||||
@@ -76,6 +79,8 @@ def get_default_account_id() -> str:
|
||||
except ClientError as error:
|
||||
raise RuntimeError(error_messages.AWS_SERVICE_REQUEST_CLIENT_ERROR_MESSAGE.format(
|
||||
"get_caller_identity", error.response['Error']['Code'], error.response['Error']['Message']))
|
||||
except NoCredentialsError as error:
|
||||
raise RuntimeError(error)
|
||||
|
||||
|
||||
def get_default_region() -> str:
|
||||
|
||||
@@ -11,7 +11,5 @@
|
||||
|
||||
set(FILES
|
||||
Include/Private/AWSCoreEditorModule.h
|
||||
Include/Private/AWSCoreModule.h
|
||||
Source/AWSCoreEditorModule.cpp
|
||||
Source/AWSCoreModule.cpp
|
||||
)
|
||||
|
||||
@@ -46,9 +46,13 @@ ly_add_target(
|
||||
Gem::AWSCore
|
||||
)
|
||||
|
||||
# Servers and Clients use the above metrics module
|
||||
# Load the "Gem::AWSMetrics" module in all types of applications.
|
||||
ly_create_alias(NAME AWSMetrics.Servers NAMESPACE Gem TARGETS Gem::AWSMetrics)
|
||||
ly_create_alias(NAME AWSMetrics.Clients NAMESPACE Gem TARGETS Gem::AWSMetrics)
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_create_alias(NAME AWSMetrics.Tools NAMESPACE Gem TARGETS Gem::AWSMetrics)
|
||||
ly_create_alias(NAME AWSMetrics.Builders NAMESPACE Gem TARGETS Gem::AWSMetrics)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ImageProcessingAtom
|
||||
int dstPosition;
|
||||
signed short int n;
|
||||
bool trimZeros = true, stillzero;
|
||||
int lastnonzero, hWeight, highest;
|
||||
int lastnonzero = 0, hWeight, highest = 0;
|
||||
signed int sumiWeights, iWeight;
|
||||
signed short int* weightsPtr;
|
||||
signed short int* weightsMem;
|
||||
|
||||
+1
-1
@@ -1106,7 +1106,7 @@ namespace ImageProcessingAtom
|
||||
//fractional amount to apply change in tap intensity along edge to taps
|
||||
// in a perpendicular direction to edge
|
||||
CP_ITYPE fixupFrac = (CP_ITYPE)(fixupDist - iFixup) / (CP_ITYPE)(fixupDist);
|
||||
CP_ITYPE fixupWeight;
|
||||
CP_ITYPE fixupWeight = 0.0f;
|
||||
|
||||
switch(a_FixupType )
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
}
|
||||
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/PBR/TransparentPassSrg.azsli>
|
||||
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
|
||||
#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"DrawListSortType": "KeyThenReverseDepth",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg"
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,5 @@ ShaderResourceGroup PassSrg : SRG_PerPass
|
||||
|
||||
Texture2D<uint4> m_tileLightData;
|
||||
StructuredBuffer<uint> m_lightListRemapped;
|
||||
Texture2D<float> m_linearDepthTexture;
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass
|
||||
{
|
||||
// [GFX TODO][ATOM-2012] adapt to multiple shadowmaps
|
||||
Texture2DArray<float> m_directionalLightShadowmap;
|
||||
Texture2DArray<float> m_directionalLightExponentialShadowmap;
|
||||
Texture2DArray<float> m_projectedShadowmaps;
|
||||
Texture2DArray<float> m_projectedExponentialShadowmap;
|
||||
Texture2D m_brdfMap;
|
||||
|
||||
Sampler LinearSampler
|
||||
{
|
||||
MinFilter = Linear;
|
||||
MagFilter = Linear;
|
||||
MipFilter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
AddressW = Clamp;
|
||||
};
|
||||
|
||||
Texture2D<uint4> m_tileLightData;
|
||||
StructuredBuffer<uint> m_lightListRemapped;
|
||||
Texture2D<float> m_linearDepthTexture;
|
||||
}
|
||||
@@ -246,7 +246,6 @@ set(FILES
|
||||
ShaderLib/Atom/Features/PBR/Hammersley.azsli
|
||||
ShaderLib/Atom/Features/PBR/LightingOptions.azsli
|
||||
ShaderLib/Atom/Features/PBR/LightingUtils.azsli
|
||||
ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli
|
||||
ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli
|
||||
ShaderLib/Atom/Features/PBR/Lighting/EnhancedLighting.azsli
|
||||
ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli
|
||||
|
||||
-2
@@ -46,8 +46,6 @@ namespace AZ
|
||||
uint16_t m_padding; // Explicit padding.
|
||||
};
|
||||
|
||||
static constexpr size_t size = sizeof(DiskLightData);
|
||||
|
||||
//! DiskLightFeatureProcessorInterface provides an interface to acquire, release, and update a disk light. This is necessary for code outside of
|
||||
//! the Atom features gem to communicate with the DiskLightFeatureProcessor.
|
||||
class DiskLightFeatureProcessorInterface
|
||||
|
||||
@@ -484,6 +484,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_TRANSITION_BARRIER transition;
|
||||
memset(&transition, 0, sizeof(D3D12_RESOURCE_TRANSITION_BARRIER)); // C4701 potentially unitialized local variable 'transition' used
|
||||
transition.pResource = image.GetMemoryView().GetMemory();
|
||||
|
||||
Scope& firstScope = static_cast<Scope&>(scopeAttachment->GetScope());
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace AZ
|
||||
Fence* fenceToSignal)
|
||||
{
|
||||
AZStd::vector<VkCommandBuffer> vkCommandBuffers;
|
||||
AZStd::vector<VkSemaphore> vkWaitSemaphores;
|
||||
AZStd::vector<VkSemaphore> vkWaitSemaphoreVector; // vulkan.h has a #define called vkWaitSemaphores, so we name this differently
|
||||
AZStd::vector<VkPipelineStageFlags> vkWaitPipelineStages;
|
||||
AZStd::vector<VkSemaphore> vkSignalSemaphores;
|
||||
VkSubmitInfo submitInfo;
|
||||
@@ -65,11 +65,11 @@ namespace AZ
|
||||
return item->GetNativeSemaphore();
|
||||
});
|
||||
vkWaitPipelineStages.reserve(waitSemaphoresInfo.size());
|
||||
vkWaitSemaphores.reserve(waitSemaphoresInfo.size());
|
||||
vkWaitSemaphoreVector.reserve(waitSemaphoresInfo.size());
|
||||
AZStd::for_each(waitSemaphoresInfo.begin(), waitSemaphoresInfo.end(), [&](auto& item)
|
||||
{
|
||||
vkWaitPipelineStages.push_back(item.first);
|
||||
vkWaitSemaphores.push_back(item.second->GetNativeSemaphore());
|
||||
vkWaitSemaphoreVector.push_back(item.second->GetNativeSemaphore());
|
||||
// Wait until the wait semaphores has been submitted for signaling.
|
||||
item.second->WaitEvent();
|
||||
});
|
||||
@@ -77,8 +77,8 @@ namespace AZ
|
||||
submitInfo = {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.pNext = nullptr;
|
||||
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphores.size());
|
||||
submitInfo.pWaitSemaphores = vkWaitSemaphores.empty() ? nullptr : vkWaitSemaphores.data();
|
||||
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphoreVector.size());
|
||||
submitInfo.pWaitSemaphores = vkWaitSemaphoreVector.empty() ? nullptr : vkWaitSemaphoreVector.data();
|
||||
submitInfo.pWaitDstStageMask = vkWaitPipelineStages.empty() ? nullptr : vkWaitPipelineStages.data();
|
||||
submitInfo.commandBufferCount = static_cast<uint32_t>(vkCommandBuffers.size());
|
||||
submitInfo.pCommandBuffers = vkCommandBuffers.empty() ? nullptr : vkCommandBuffers.data();
|
||||
|
||||
@@ -10,3 +10,5 @@
|
||||
#
|
||||
|
||||
add_subdirectory(Code)
|
||||
add_subdirectory(Tools)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace AZ
|
||||
{
|
||||
PrimitiveType = AZ_BIT(0),
|
||||
DepthState = AZ_BIT(1),
|
||||
EnableStencil = AZ_BIT(2),
|
||||
StencilState = AZ_BIT(2),
|
||||
FaceCullMode = AZ_BIT(3),
|
||||
BlendMode = AZ_BIT(4)
|
||||
};
|
||||
@@ -110,8 +110,8 @@ namespace AZ
|
||||
|
||||
//! Set DepthState if DrawStateOptions::DepthState option is enabled
|
||||
void SetDepthState(RHI::DepthState depthState);
|
||||
//! Enable/disable stencil if DrawStateOptions::EnableStencil option is enabled
|
||||
void SetEnableStencil(bool enable);
|
||||
//! Set StencilState if DrawStateOptions::StencilState option is enabled
|
||||
void SetStencilState(RHI::StencilState stencilState);
|
||||
//! Set CullMode if DrawStateOptions::FaceCullMode option is enabled
|
||||
void SetCullMode(RHI::CullMode cullMode);
|
||||
//! Set TargetBlendState for target 0 if DrawStateOptions::BlendMode option is enabled
|
||||
@@ -188,7 +188,7 @@ namespace AZ
|
||||
// states available for change
|
||||
RHI::CullMode m_cullMode;
|
||||
RHI::DepthState m_depthState;
|
||||
bool m_enableStencil;
|
||||
RHI::StencilState m_stencilState;
|
||||
RHI::PrimitiveTopology m_topology;
|
||||
RHI::TargetBlendState m_blendState0;
|
||||
|
||||
|
||||
@@ -239,16 +239,26 @@ namespace AZ
|
||||
|
||||
void CullingScene::RegisterOrUpdateCullable(Cullable& cullable)
|
||||
{
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
// Multiple threads can call RegisterOrUpdateCullable at the same time
|
||||
// since the underlying visScene is thread safe, but if you're inserting or
|
||||
// updating between BeginCulling and EndCulling, you'll get non-deterministic
|
||||
// results depending on a race condition if you happen to update before or after
|
||||
// the culling system starts Enumerating, so use soft_lock_shared here
|
||||
m_cullDataConcurrencyCheck.soft_lock_shared();
|
||||
m_visScene->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry);
|
||||
m_cullDataConcurrencyCheck.soft_unlock();
|
||||
m_cullDataConcurrencyCheck.soft_unlock_shared();
|
||||
}
|
||||
|
||||
void CullingScene::UnregisterCullable(Cullable& cullable)
|
||||
{
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
// Multiple threads can call RegisterOrUpdateCullable at the same time
|
||||
// since the underlying visScene is thread safe, but if you're inserting or
|
||||
// updating between BeginCulling and EndCulling, you'll get non-deterministic
|
||||
// results depending on a race condition if you happen to update before or after
|
||||
// the culling system starts Enumerating, so use soft_lock_shared here
|
||||
m_cullDataConcurrencyCheck.soft_lock_shared();
|
||||
m_visScene->RemoveEntry(cullable.m_cullData.m_visibilityEntry);
|
||||
m_cullDataConcurrencyCheck.soft_unlock();
|
||||
m_cullDataConcurrencyCheck.soft_unlock_shared();
|
||||
}
|
||||
|
||||
uint32_t CullingScene::GetNumCullables() const
|
||||
|
||||
@@ -30,25 +30,7 @@ namespace AZ
|
||||
constexpr const char* PerContextSrgName = "PerContextSrg";
|
||||
constexpr const char* PerDrawSrgName = "PerDrawSrg";
|
||||
};
|
||||
|
||||
bool CompareTargetBlendState(const RHI::TargetBlendState& firstState, const RHI::TargetBlendState& secondState)
|
||||
{
|
||||
return !(firstState.m_enable != secondState.m_enable
|
||||
|| firstState.m_blendOp != secondState.m_blendOp
|
||||
|| firstState.m_blendDest != secondState.m_blendDest
|
||||
|| firstState.m_blendSource != secondState.m_blendSource
|
||||
|| firstState.m_blendAlphaDest != secondState.m_blendAlphaDest
|
||||
|| firstState.m_blendAlphaOp != secondState.m_blendAlphaOp
|
||||
|| firstState.m_blendAlphaSource != secondState.m_blendAlphaSource);
|
||||
}
|
||||
|
||||
bool CompareDepthState(const RHI::DepthState& firstState, const RHI::DepthState& secondState)
|
||||
{
|
||||
return !(firstState.m_enable != secondState.m_enable
|
||||
|| firstState.m_func != secondState.m_func
|
||||
|| firstState.m_writeMask != secondState.m_writeMask);
|
||||
}
|
||||
|
||||
|
||||
void DynamicDrawContext::MultiStates::UpdateHash(const DrawStateOptions& drawStateOptions)
|
||||
{
|
||||
if (!m_isDirty)
|
||||
@@ -70,9 +52,19 @@ namespace AZ
|
||||
seed = TypeHash64(m_depthState.m_writeMask, seed);
|
||||
}
|
||||
|
||||
if (RHI::CheckBitsAny(drawStateOptions, DrawStateOptions::EnableStencil))
|
||||
if (RHI::CheckBitsAny(drawStateOptions, DrawStateOptions::StencilState))
|
||||
{
|
||||
seed = TypeHash64(m_enableStencil, seed);
|
||||
seed = TypeHash64(m_stencilState.m_enable, seed);
|
||||
seed = TypeHash64(m_stencilState.m_readMask, seed);
|
||||
seed = TypeHash64(m_stencilState.m_writeMask, seed);
|
||||
seed = TypeHash64(m_stencilState.m_frontFace.m_failOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_frontFace.m_depthFailOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_frontFace.m_passOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_frontFace.m_func, seed);
|
||||
seed = TypeHash64(m_stencilState.m_backFace.m_failOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_backFace.m_depthFailOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_backFace.m_passOp, seed);
|
||||
seed = TypeHash64(m_stencilState.m_backFace.m_func, seed);
|
||||
}
|
||||
|
||||
if (RHI::CheckBitsAny(drawStateOptions, DrawStateOptions::FaceCullMode))
|
||||
@@ -203,7 +195,7 @@ namespace AZ
|
||||
m_currentStates.m_cullMode = m_pipelineState->ConstDescriptor().m_renderStates.m_rasterState.m_cullMode;
|
||||
m_currentStates.m_topology = m_pipelineState->ConstDescriptor().m_inputStreamLayout.GetTopology();
|
||||
m_currentStates.m_depthState = m_pipelineState->ConstDescriptor().m_renderStates.m_depthStencilState.m_depth;
|
||||
m_currentStates.m_enableStencil = m_pipelineState->ConstDescriptor().m_renderStates.m_depthStencilState.m_stencil.m_enable;
|
||||
m_currentStates.m_stencilState = m_pipelineState->ConstDescriptor().m_renderStates.m_depthStencilState.m_stencil;
|
||||
m_currentStates.m_blendState0 = m_pipelineState->ConstDescriptor().m_renderStates.m_blendState.m_targets[0];
|
||||
m_currentStates.UpdateHash(m_drawStateOptions);
|
||||
|
||||
@@ -291,7 +283,7 @@ namespace AZ
|
||||
{
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::DepthState))
|
||||
{
|
||||
if (!CompareDepthState(m_currentStates.m_depthState, depthState))
|
||||
if (!(m_currentStates.m_depthState == depthState))
|
||||
{
|
||||
m_currentStates.m_depthState = depthState;
|
||||
m_currentStates.m_isDirty = true;
|
||||
@@ -303,19 +295,19 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicDrawContext::SetEnableStencil(bool enable)
|
||||
void DynamicDrawContext::SetStencilState(RHI::StencilState stencilState)
|
||||
{
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::EnableStencil))
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::StencilState))
|
||||
{
|
||||
if (m_currentStates.m_enableStencil != enable)
|
||||
if (!(m_currentStates.m_stencilState == stencilState))
|
||||
{
|
||||
m_currentStates.m_enableStencil = enable;
|
||||
m_currentStates.m_stencilState = stencilState;
|
||||
m_currentStates.m_isDirty = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("RHI", false, "Can't set SetEnableStencil if DrawVariation::EnableStencil wasn't enabled");
|
||||
AZ_Warning("RHI", false, "Can't set SetStencilState if DrawVariation::StencilState wasn't enabled");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -340,7 +332,7 @@ namespace AZ
|
||||
{
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::BlendMode))
|
||||
{
|
||||
if (!CompareTargetBlendState(m_currentStates.m_blendState0, blendState))
|
||||
if (!(m_currentStates.m_blendState0 == blendState))
|
||||
{
|
||||
m_currentStates.m_blendState0 = blendState;
|
||||
m_currentStates.m_isDirty = true;
|
||||
@@ -695,9 +687,9 @@ namespace AZ
|
||||
{
|
||||
m_pipelineState->RenderStatesOverlay().m_depthStencilState.m_depth = m_currentStates.m_depthState;
|
||||
}
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::EnableStencil))
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::StencilState))
|
||||
{
|
||||
m_pipelineState->RenderStatesOverlay().m_depthStencilState.m_stencil.m_enable = m_currentStates.m_enableStencil;
|
||||
m_pipelineState->RenderStatesOverlay().m_depthStencilState.m_stencil = m_currentStates.m_stencilState;
|
||||
}
|
||||
if (RHI::CheckBitsAny(m_drawStateOptions, DrawStateOptions::FaceCullMode))
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -34,10 +35,6 @@ namespace AZ
|
||||
|
||||
uint32_t ShaderAsset::MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType)
|
||||
{
|
||||
static constexpr uint32_t RhiIndexBitPosition = 30;
|
||||
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
|
||||
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
|
||||
|
||||
static constexpr uint32_t SubProductTypeBitPosition = 0;
|
||||
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
|
||||
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -24,10 +25,6 @@ namespace AZ
|
||||
uint32_t ShaderVariantAsset::MakeAssetProductSubId(
|
||||
uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId, uint32_t subProductType)
|
||||
{
|
||||
static constexpr uint32_t RhiIndexBitPosition = 30;
|
||||
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
|
||||
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
|
||||
|
||||
static constexpr uint32_t SubProductTypeBitPosition = 17;
|
||||
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
|
||||
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
ly_download_associated_package(pyside2)
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_pip_install_local_package_editable(${CMAKE_CURRENT_LIST_DIR} atom_rpi_tools)
|
||||
|
||||
ly_add_pytest(
|
||||
NAME test_pyside
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_pyside.py
|
||||
)
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_pytest(
|
||||
NAME RPI::atom_rpi_tools_tests
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/atom_rpi_tools/tests/
|
||||
TIMEOUT 30
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ly_add_pytest(
|
||||
NAME test_projects
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_projects.py
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
atom_rpi_tools is a Python project that contains a collection of tools
|
||||
developed by the Atom team. The project contains the following tools:
|
||||
|
||||
* Render pipeline merge tool:
|
||||
A library to manipulate .pass asset files and help gems create scripts to update render pipeline
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
* Python 3.7.5 (64-bit)
|
||||
|
||||
It is recommended that you completely remove any other versions of Python
|
||||
installed on your system.
|
||||
|
||||
|
||||
INSTALL
|
||||
-----------
|
||||
It is recommended to set up these these tools with Lumberyard's CMake build commands.
|
||||
|
||||
|
||||
UNINSTALLATION
|
||||
--------------
|
||||
|
||||
The preferred way to uninstall the project is:
|
||||
(engine install root)/python/python -m pip uninstall atom_rpi_tools
|
||||
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
@@ -0,0 +1,210 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
import json
|
||||
import shutil
|
||||
|
||||
class PassTemplate:
|
||||
# This class provide necessary functions for insert pass requests and update connections
|
||||
# which are common functions required for adding features.
|
||||
# It doesn't include the remove/delete furnctions since that's not common case for merging render pipeline
|
||||
def __init__(self, filePath: str):
|
||||
self.initialized = False
|
||||
self.file_path: str = filePath
|
||||
#load the json file
|
||||
json_data = open(filePath, "r")
|
||||
self.file_data = json.load(json_data)
|
||||
|
||||
if 'ClassName' not in self.file_data or 'ClassData' not in self.file_data or self.file_data['ClassName']!='PassAsset' or 'PassTemplate' not in self.file_data['ClassData']:
|
||||
raise KeyError('the json file is not a PassAsset file')
|
||||
return
|
||||
|
||||
if 'PassRequests' in self.file_data['ClassData']['PassTemplate']:
|
||||
self.passRequests = self.file_data['ClassData']['PassTemplate']['PassRequests']
|
||||
|
||||
if 'Slots' in self.file_data['ClassData']['PassTemplate']:
|
||||
self.slots = self.file_data['ClassData']['PassTemplate']['Slots']
|
||||
|
||||
self.initialized = True
|
||||
print('PassTemplate is loaded from ', filePath)
|
||||
|
||||
def find_pass(self, passName):
|
||||
# return pass's index in PassRequests if a PassRequest with input passName exists
|
||||
if not hasattr(self, 'passRequests'):
|
||||
return -1
|
||||
index = 0
|
||||
for passRequest in self.passRequests:
|
||||
if passRequest['Name'] == passName:
|
||||
return index
|
||||
index += 1
|
||||
return -1
|
||||
|
||||
def get_pass_count(self):
|
||||
if not hasattr(self, 'passRequests'):
|
||||
return 0
|
||||
return len(self.passRequests)
|
||||
|
||||
def __validate_pass_request_data(self, passRequest):
|
||||
if ('Name' not in passRequest or 'TemplateName' not in passRequest):
|
||||
raise KeyError('invalid pass request data')
|
||||
|
||||
def __ensure_pass_requests_key(self):
|
||||
if not hasattr(self, 'passRequests'):
|
||||
self.file_data['ClassData']['PassTemplate']['PassRequests'] = []
|
||||
self.passRequests = self.file_data['ClassData']['PassTemplate']['PassRequests']
|
||||
|
||||
def __ensure_pass_slots_key(self):
|
||||
if not hasattr(self, 'slots'):
|
||||
self.file_data['ClassData']['PassTemplate']['Slots'] = []
|
||||
self.slots = self.file_data['ClassData']['PassTemplate']['Slots']
|
||||
|
||||
def insert_pass_request(self, location, passRequest):
|
||||
self.__validate_pass_request_data(passRequest)
|
||||
|
||||
if (self.find_pass(passRequest['Name']) >= 0):
|
||||
raise ValueError('pass request ', passRequest['Name'], ' is already exist')
|
||||
# insert a passRequest before the specified location
|
||||
self.__ensure_pass_requests_key()
|
||||
self.passRequests.insert(location, passRequest)
|
||||
|
||||
def replace_references_after(self, startPassRequest, oldPass, oldSlot, newPass, newSlot):
|
||||
if not hasattr(self, 'passRequests'):
|
||||
return 0
|
||||
# from all pass requests after startPassRequest
|
||||
# replace all attachment references which uses oldPass and oldSlot
|
||||
# with newPass and newSlot
|
||||
started = False
|
||||
replaced_count = 0
|
||||
for request in self.passRequests:
|
||||
if started:
|
||||
if ('Connections' in request):
|
||||
for connection in request['Connections']:
|
||||
if connection['AttachmentRef']['Pass'] == oldPass and connection['AttachmentRef']['Attachment'] == oldSlot:
|
||||
connection['AttachmentRef']['Pass'] = newPass
|
||||
connection['AttachmentRef']['Attachment'] = newSlot
|
||||
replaced_count += 1
|
||||
if request['Name'] == startPassRequest and not started:
|
||||
started = True
|
||||
return replaced_count
|
||||
|
||||
def replace_references_for(self, passRequest, oldPass, oldSlot, newPass, newSlot):
|
||||
if not hasattr(self, 'passRequests'):
|
||||
return 0
|
||||
#replace pass reference for the specified passRequest
|
||||
replaced_count = 0
|
||||
for request in self.passRequests:
|
||||
if request['Name'] == passRequest:
|
||||
if ('Connections' in request):
|
||||
for connection in request['Connections']:
|
||||
if connection['AttachmentRef']['Pass'] == oldPass and connection['AttachmentRef']['Attachment'] == oldSlot:
|
||||
connection['AttachmentRef']['Pass'] = newPass
|
||||
connection['AttachmentRef']['Attachment'] = newSlot
|
||||
replaced_count += 1
|
||||
return replaced_count #return when the specified pass request is updated.
|
||||
return replaced_count
|
||||
|
||||
def __validate_slot_data(self, slotData):
|
||||
if ('Name' not in slotData or 'SlotType' not in slotData):
|
||||
raise KeyError('invalid slot data')
|
||||
|
||||
def get_slot_count(self):
|
||||
if not hasattr(self, 'slots'):
|
||||
return 0
|
||||
return len(self.slots)
|
||||
|
||||
def find_slot(self, slotName):
|
||||
# return slot's index in Slots if a PassRequest with input passName exists
|
||||
if not hasattr(self, 'slots'):
|
||||
return -1
|
||||
index = 0
|
||||
for slot in self.slots:
|
||||
if slot['Name'] == slotName:
|
||||
return index
|
||||
index += 1
|
||||
return -1
|
||||
|
||||
def insert_slot(self, location, newSlotData):
|
||||
# insert a new slot at specified location
|
||||
self.__validate_slot_data(newSlotData)
|
||||
# check if the slot already exist
|
||||
if (self.find_slot(newSlotData['Name']) >= 0):
|
||||
raise ValueError('Slot ', newSlotData['Name'], ' is already exist')
|
||||
|
||||
self.__ensure_pass_slots_key()
|
||||
self.slots.insert(location, newSlotData)
|
||||
|
||||
def add_slot(self, newSlotData):
|
||||
# append a new slot to slots
|
||||
self.__validate_slot_data(newSlotData)
|
||||
# check if the slot already exist
|
||||
if (self.find_slot(newSlotData['Name']) >= 0):
|
||||
raise ValueError('Slot ', newSlotData['Name'], ' is already exist')
|
||||
|
||||
self.__ensure_pass_slots_key()
|
||||
self.slots.append(newSlotData)
|
||||
|
||||
def get_pass_request(self, passName):
|
||||
if not hasattr(self, 'passRequests'):
|
||||
return
|
||||
# Get the pass request from PassRequests with matching pass name
|
||||
for passRequest in self.passRequests:
|
||||
if passRequest['Name'] == passName:
|
||||
return passRequest
|
||||
|
||||
def save(self):
|
||||
# backup the original file
|
||||
backupFilePath = self.file_path +'.backup'
|
||||
shutil.copyfile(self.file_path, backupFilePath)
|
||||
# save and overwrite file
|
||||
with open(self.file_path, 'w') as json_file:
|
||||
json.dump(self.file_data, json_file, indent = 4)
|
||||
print('File [', self.file_path, '] is updated. Old version is saved in [', backupFilePath, ']')
|
||||
|
||||
|
||||
class PassRequest:
|
||||
|
||||
def __init__(self, passRequest: object):
|
||||
self.pass_request = passRequest
|
||||
if 'Connections' in passRequest:
|
||||
self.connections = passRequest['Connections']
|
||||
|
||||
def __validate_connection(self, connection):
|
||||
if ('LocalSlot' not in connection or 'AttachmentRef' not in connection):
|
||||
raise KeyError('invalid connection data')
|
||||
|
||||
def __ensure_connections_key(self):
|
||||
if not hasattr(self, 'connections'):
|
||||
self.pass_request['Connections'] = []
|
||||
self.connections = self.pass_request['Connections']
|
||||
|
||||
def get_connection_count(self):
|
||||
if not hasattr(self, 'connections'):
|
||||
return 0
|
||||
return len(self.connections)
|
||||
|
||||
def find_connection(self, localSlotName):
|
||||
if not hasattr(self, 'connections'):
|
||||
return -1
|
||||
index = 0
|
||||
for connection in self.connections:
|
||||
if connection['LocalSlot'] == localSlotName:
|
||||
return index
|
||||
index += 1
|
||||
return -1
|
||||
|
||||
def add_connection(self, newConnection):
|
||||
self.__validate_connection(newConnection)
|
||||
if self.find_connection(newConnection['LocalSlot']) >= 0:
|
||||
raise ValueError('connection ', newConnection['LocalSlot'], ' already exists')
|
||||
self.__ensure_connections_key()
|
||||
self.connections.append(newConnection)
|
||||
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
@@ -0,0 +1,303 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Unit tests for pass_data.py
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import shutil
|
||||
import json
|
||||
from atom_rpi_tools.pass_data import PassTemplate
|
||||
from atom_rpi_tools.pass_data import PassRequest
|
||||
|
||||
good_pass_requests_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_requests.json')
|
||||
good_pass_slots_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_slots.json')
|
||||
bad_test_data_file = os.path.join(os.path.dirname(__file__), 'testdata/pass_test_bad.json')
|
||||
|
||||
@pytest.fixture
|
||||
def pass_requests_template(tmpdir):
|
||||
filename = 'pass_requests.json'
|
||||
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
|
||||
destFilePath = os.path.join(tmpdir, 'pass_requests.json')
|
||||
shutil.copyfile(source_path, destFilePath)
|
||||
return PassTemplate(destFilePath)
|
||||
|
||||
@pytest.fixture
|
||||
def pass_slots_template(tmpdir):
|
||||
filename = 'pass_slots.json'
|
||||
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
|
||||
destFilePath = os.path.join(tmpdir, 'pass_requests.json')
|
||||
shutil.copyfile(source_path, destFilePath)
|
||||
return PassTemplate(destFilePath)
|
||||
|
||||
@pytest.fixture
|
||||
def new_pass_request():
|
||||
pass_request = json.loads('{\"Name\": \"InsertPass\",\"TemplateName\": \"InsertPassTemplate\"}')
|
||||
return pass_request
|
||||
|
||||
@pytest.fixture
|
||||
def new_slot():
|
||||
slot = json.loads('{\"Name\": \"NewSlot\",\"SlotType\": \"Input\"}')
|
||||
return slot
|
||||
|
||||
@pytest.fixture
|
||||
def new_connection():
|
||||
connection = json.loads('{\"LocalSlot\": \"color\", \"AttachmentRef\": { \"Pass\": \"Parent\", \"Attachment\": \"DepthStencil\"}}')
|
||||
return connection
|
||||
|
||||
def test_PassTemplate_Initialize_BadPassTemplateData_ExceptionThrown():
|
||||
with pytest.raises(KeyError):
|
||||
PassTemplate(bad_test_data_file)
|
||||
|
||||
def test_PassTemplate_FindPass_Success(pass_requests_template):
|
||||
assert pass_requests_template.find_pass('OpaquePass') == 0
|
||||
assert pass_requests_template.find_pass('ImGuiPass') == 4
|
||||
assert pass_requests_template.find_pass('NotExistPass') == -1
|
||||
|
||||
def test_PassTemplate_InsertPassRequest_AtBegining_Success(pass_requests_template, new_pass_request):
|
||||
template = pass_requests_template
|
||||
pass_count = template.get_pass_count()
|
||||
template.insert_pass_request(0, new_pass_request)
|
||||
assert template.find_pass(new_pass_request['Name']) == 0
|
||||
assert template.get_pass_count() == pass_count+1
|
||||
|
||||
# verify the change is saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.find_pass(new_pass_request['Name'])== 0
|
||||
assert saved_tamplate.get_pass_count() == pass_count+1
|
||||
|
||||
def test_PassTemplate_InsertPassRequest_AtEnd_Success(pass_requests_template, new_pass_request):
|
||||
template = pass_requests_template
|
||||
pass_count = template.get_pass_count()
|
||||
template.insert_pass_request(pass_count, new_pass_request)
|
||||
assert template.find_pass(new_pass_request['Name']) == pass_count
|
||||
assert template.get_pass_count() == pass_count+1
|
||||
|
||||
# verify the change is saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.find_pass(new_pass_request['Name']) == pass_count
|
||||
assert saved_tamplate.get_pass_count() == pass_count+1
|
||||
|
||||
def test_PassTemplate_InsertPassRequest_WithDuplicatedName_ExceptionThrown(pass_requests_template, new_pass_request):
|
||||
template = pass_requests_template
|
||||
# insert new pass request
|
||||
template.insert_pass_request(0, new_pass_request)
|
||||
pass_count = template.get_pass_count()
|
||||
# exception when insert the same pass again
|
||||
with pytest.raises(ValueError):
|
||||
template.insert_pass_request(2, new_pass_request)
|
||||
# pass count doesn't change
|
||||
assert template.get_pass_count() == pass_count
|
||||
|
||||
def test_PassTemplate_InsertPassRequest_WithBadData_ExceptionThrown(pass_requests_template):
|
||||
template = pass_requests_template
|
||||
pass_count = template.get_pass_count()
|
||||
bad_pass_request = json.loads('{\"name\":\"value\"}')
|
||||
with pytest.raises(KeyError):
|
||||
template.insert_pass_request(2, bad_pass_request)
|
||||
assert template.get_pass_count() == pass_count
|
||||
|
||||
def test_PassTemplate_InsertPassRequest_AtOutOfRange_AppendSuccess(pass_requests_template, new_pass_request):
|
||||
template = pass_requests_template
|
||||
pass_count = template.get_pass_count()
|
||||
template.insert_pass_request(pass_count+2, new_pass_request)
|
||||
assert template.find_pass(new_pass_request['Name']) == pass_count
|
||||
assert template.get_pass_count() == pass_count+1
|
||||
|
||||
def test_PassTemplate_ReplaceReferencesAfter_Success(pass_requests_template):
|
||||
# replace OpaquePass.DepthStencil with Parent.DepthStencil'
|
||||
refPass = 'OpaquePass'
|
||||
# there are 2 passes after OpaquePass which use OpaquePass.DepthStencil as attachment reference
|
||||
assert pass_requests_template.replace_references_after(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 2
|
||||
# after the previous replacement, there it no OpaquePass.DepthStencil reference
|
||||
refPass = 'TransparentPass'
|
||||
assert pass_requests_template.replace_references_after(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
|
||||
|
||||
# verify changes are saved
|
||||
pass_requests_template.save()
|
||||
saved_tamplate = PassTemplate(pass_requests_template.file_path)
|
||||
assert saved_tamplate.replace_references_after('OpaquePass', 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
|
||||
|
||||
def test_PassTemplate_ReplaceReferencesFor_Success(pass_requests_template):
|
||||
refPass = 'TransparentPass'
|
||||
assert pass_requests_template.replace_references_for(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 1
|
||||
refPass = '2DPass'
|
||||
assert pass_requests_template.replace_references_for(refPass, 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
|
||||
|
||||
# verify changes are saved
|
||||
pass_requests_template.save()
|
||||
saved_tamplate = PassTemplate(pass_requests_template.file_path)
|
||||
# no reference of OpaquePass.DepthStencil in TransparentPass
|
||||
assert saved_tamplate.replace_references_for('TransparentPass', 'OpaquePass', 'DepthStencil', 'Parent', 'DepthStencil') == 0
|
||||
|
||||
def test_PassTemplate_FindSlot_Success(pass_slots_template):
|
||||
assert pass_slots_template.find_slot('Color') == -1
|
||||
assert pass_slots_template.find_slot('DepthStencil') == 0
|
||||
assert pass_slots_template.find_slot('ColorInputOutput') == 1
|
||||
|
||||
def test_PassTemplate_InsertSlot_AtBegining_Success(pass_slots_template, new_slot):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
depth_stencil_slot = template.find_slot('DepthStencil')
|
||||
template.insert_slot(0, new_slot)
|
||||
assert template.find_slot(new_slot['Name']) == 0
|
||||
assert template.find_slot('DepthStencil') == depth_stencil_slot+1 # DepthStencil moved back by 1
|
||||
assert template.get_slot_count() == slot_count+1
|
||||
|
||||
# verify the change is saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.find_slot(new_slot['Name']) == 0
|
||||
assert saved_tamplate.get_slot_count() == slot_count+1
|
||||
|
||||
def test_PassTemplate_InsertSlot_AtEnd_Success(pass_slots_template, new_slot):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
depth_stencil_slot = template.find_slot('DepthStencil')
|
||||
template.insert_slot(slot_count, new_slot)
|
||||
assert template.find_slot(new_slot['Name']) == slot_count
|
||||
assert template.find_slot('DepthStencil') == depth_stencil_slot
|
||||
assert template.get_slot_count() == slot_count+1
|
||||
|
||||
# verify the change is saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.find_slot(new_slot['Name']) == slot_count
|
||||
assert saved_tamplate.get_slot_count() == slot_count+1
|
||||
|
||||
def test_PassTemplate_AddSlot_GoodSlotData_Success(pass_slots_template, new_slot):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
depth_stencil_slot = template.find_slot('DepthStencil')
|
||||
template.add_slot(new_slot)
|
||||
assert template.find_slot(new_slot['Name']) == slot_count
|
||||
assert template.find_slot('DepthStencil') == depth_stencil_slot
|
||||
assert template.get_slot_count() == slot_count+1
|
||||
|
||||
# verify the change is saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.find_slot(new_slot['Name']) == slot_count
|
||||
assert saved_tamplate.get_slot_count() == slot_count+1
|
||||
|
||||
def test_PassTemplate_InsertSlot_OutOfRange_AppendSuccess(pass_slots_template, new_slot):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
template.insert_slot(slot_count+3, new_slot)
|
||||
assert template.find_slot(new_slot['Name']) == slot_count
|
||||
assert template.get_slot_count() == slot_count+1
|
||||
|
||||
def test_PassTemplate_AddDuplicateSlot_ExceptionThrown(pass_slots_template, new_slot):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
template.add_slot(new_slot)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
template.insert_slot(0, new_slot)
|
||||
with pytest.raises(ValueError):
|
||||
template.add_slot(new_slot)
|
||||
|
||||
def test_PassTemplate_InsertOrAddSlot_WithBadSlotData_ExceptionThrown(pass_slots_template):
|
||||
template = pass_slots_template
|
||||
slot_count = template.get_slot_count()
|
||||
bad_slot = json.loads('{\"slot\": \"xxx\"}')
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
template.insert_slot(0, bad_slot)
|
||||
with pytest.raises(KeyError):
|
||||
template.add_slot(bad_slot)
|
||||
|
||||
def test_PassReqeuest_Initialize_WithExistPassReqeuestFromPassTemplate_Success(pass_requests_template):
|
||||
template = pass_requests_template
|
||||
request = PassRequest(template.get_pass_request('OpaquePass'))
|
||||
connection_count = request.get_connection_count()
|
||||
assert connection_count == 2
|
||||
|
||||
def test_PassTemplate_GetPassRequest_NotExist_ReturnNull(pass_requests_template):
|
||||
assert not pass_requests_template.get_pass_request('NotExistPass')
|
||||
|
||||
def test_PassReqeuest_AddConnection_WithExistingConnections_Success(pass_requests_template, new_connection):
|
||||
template = pass_requests_template
|
||||
request = PassRequest(template.get_pass_request('OpaquePass'))
|
||||
connection_count = request.get_connection_count()
|
||||
request.add_connection(new_connection)
|
||||
connection_count += 1
|
||||
assert request.get_connection_count() == connection_count
|
||||
|
||||
# verify changes are saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
saved_request = PassRequest(saved_tamplate.get_pass_request('OpaquePass'))
|
||||
assert saved_request.get_connection_count() == connection_count
|
||||
|
||||
def test_PassReqeuest_AddConnection_WithNoExistingConnections_Success(pass_requests_template, new_connection):
|
||||
template = pass_requests_template
|
||||
request = PassRequest(template.get_pass_request('ImGuiPass'))
|
||||
assert request.get_connection_count() == 0
|
||||
request.add_connection(new_connection)
|
||||
assert request.get_connection_count() == 1
|
||||
|
||||
# verify changes are saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
saved_request = PassRequest(saved_tamplate.get_pass_request('ImGuiPass'))
|
||||
assert saved_request.get_connection_count() == 1
|
||||
|
||||
def test_PassReqeuest_AddConnection_WithDuplicatedName_ExceptionThrown(pass_requests_template, new_connection):
|
||||
template = pass_requests_template
|
||||
request = PassRequest(template.get_pass_request('OpaquePass'))
|
||||
request.add_connection(new_connection)
|
||||
with pytest.raises(ValueError):
|
||||
request.add_connection(new_connection)
|
||||
|
||||
def test_PassReqeuest_AddConnect_BadConnectionData_ExceptionThrown(pass_requests_template, new_connection):
|
||||
template = pass_requests_template
|
||||
request = PassRequest(template.get_pass_request('OpaquePass'))
|
||||
bad_connection = json.loads('{\"xxx\": \"xxx\"}')
|
||||
with pytest.raises(KeyError):
|
||||
request.add_connection(bad_connection)
|
||||
|
||||
def test_PassTemplate_InsertSlot_ToEmptyList_Success(pass_requests_template, new_slot):
|
||||
template = pass_requests_template
|
||||
# test insert slot function to pass template which doesn't have any slots
|
||||
slot_count = template.get_slot_count()
|
||||
assert slot_count == 0
|
||||
|
||||
assert template.find_slot(new_slot['Name'])==-1
|
||||
pass_requests_template.insert_slot(0, new_slot)
|
||||
assert template.find_slot(new_slot['Name']) == 0
|
||||
assert template.get_slot_count() == 1
|
||||
|
||||
# verify changes are saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.get_slot_count() == 1
|
||||
|
||||
def test_PassTempalte_InsertPassRequest_ToEmptyList_Success(pass_slots_template, new_pass_request):
|
||||
template = pass_slots_template
|
||||
# test insert pass function to pass template which doesn't have any pass requests
|
||||
pass_count = template.get_pass_count()
|
||||
assert pass_count == 0
|
||||
template.insert_pass_request(0, new_pass_request)
|
||||
assert template.find_pass(new_pass_request['Name']) == 0
|
||||
assert template.get_pass_count() == 1
|
||||
|
||||
# verify changes are saved
|
||||
template.save()
|
||||
saved_tamplate = PassTemplate(template.file_path)
|
||||
assert saved_tamplate.get_pass_count() == 1
|
||||
|
||||
def test_PassTemplate_Save_Success(pass_requests_template):
|
||||
pass_requests_template.save()
|
||||
saved_tamplate = PassTemplate(pass_requests_template.file_path)
|
||||
assert os.path.exists(pass_requests_template.file_path)
|
||||
assert os.path.exists(pass_requests_template.file_path +'.backup')
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Unit tests for utils.py
|
||||
"""
|
||||
import pytest
|
||||
import os
|
||||
import atom_rpi_tools.utils as utils
|
||||
|
||||
|
||||
def test_FindOrCopyFile_DestFileNotExist_CopySuccess(tmpdir):
|
||||
# created dir and copied
|
||||
filename = 'pass_requests.json'
|
||||
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
|
||||
dest_path = os.path.join(tmpdir, 'testdata/', 'pass_requests.json')
|
||||
assert not os.path.exists(dest_path)
|
||||
utils.find_or_copy_file(dest_path, source_path)
|
||||
assert os.path.exists(dest_path)
|
||||
source_size = os.path.getsize(source_path)
|
||||
dest_size = os.path.getsize(dest_path)
|
||||
assert source_size == dest_size
|
||||
|
||||
def test_FindOrCopyFile_DestFileAlreadyExists_Skip(tmpdir):
|
||||
# copy %cur_dir%/testdata/pass_requests.json to tempdir/testdata/pass_requests.json
|
||||
filename = 'pass_requests.json'
|
||||
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', filename)
|
||||
dest_path = os.path.join(tmpdir, 'testdata/', 'pass_requests.json')
|
||||
utils.find_or_copy_file(dest_path, source_path)
|
||||
|
||||
# skip if dest_path already exists
|
||||
assert os.path.exists(dest_path)
|
||||
before_size = os.path.getsize(dest_path)
|
||||
source_path = os.path.join(os.path.dirname(__file__), 'testdata/', 'pass_slots.json')
|
||||
before_source_size = os.path.getsize(source_path)
|
||||
assert before_size != source_path
|
||||
utils.find_or_copy_file(dest_path, source_path)
|
||||
after_size = os.path.getsize(dest_path)
|
||||
assert before_size == after_size
|
||||
|
||||
|
||||
def test_FindOrCopyFile_SourceFileNotExists_ExceptionThrown(tmpdir):
|
||||
# report error if source doesn't exist
|
||||
bad_source_path = 'notexist.dat'
|
||||
dest_path = os.path.join(tmpdir, 'notexist.dat')
|
||||
with pytest.raises(ValueError):
|
||||
utils.find_or_copy_file(dest_path, bad_source_path)
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "PipelineTemplate",
|
||||
"PassClass": "ParentPass",
|
||||
"PassRequests": [
|
||||
{
|
||||
"Name": "OpaquePass",
|
||||
"TemplateName": "OpaquePassTemplate",
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "DepthStencil",
|
||||
"AttachmentRef": {
|
||||
"Pass": "Parent",
|
||||
"Attachment": "DepthStencil"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "ColorInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "Parent",
|
||||
"Attachment": "ColorInputOutput"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "TransparentPass",
|
||||
"TemplateName": "TransparentPassTemplate",
|
||||
"Enabled": true,
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "DepthStencil",
|
||||
"AttachmentRef": {
|
||||
"Pass": "OpaquePass",
|
||||
"Attachment": "DepthStencil"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "ColorInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "OpaquePass",
|
||||
"Attachment": "Color"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "transparent",
|
||||
"DrawListSortType": "KeyThenReverseDepth",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "AuxGeomPass",
|
||||
"TemplateName": "AuxGeomPassTemplate",
|
||||
"Enabled": true,
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "DepthStencil",
|
||||
"AttachmentRef": {
|
||||
"Pass": "OpaquePass",
|
||||
"Attachment": "DepthStencil"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "ColorInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "TransparentPass",
|
||||
"Attachment": "ColorInputOutput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "auxgeom",
|
||||
"PipelineViewTag": "MainCamera"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "2DPass",
|
||||
"TemplateName": "UIPassTemplate",
|
||||
"Enabled": true,
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "ColorInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "TransparentPass",
|
||||
"Attachment": "ColorInputOutput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "2dpass",
|
||||
"PipelineViewTag": "MainCamera"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "ImGuiPass",
|
||||
"TemplateName": "ImGuiPassTemplate",
|
||||
"PassData": {
|
||||
"$type": "ImGuiPassData",
|
||||
"IsDefaultImGui": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "PipelineTemplate",
|
||||
"PassClass": "ParentPass",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "DepthStencil",
|
||||
"SlotType": "InputOutput"
|
||||
},
|
||||
{
|
||||
"Name": "ColorInputOutput",
|
||||
"SlotType": "InputOutput"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassData": {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
import os.path
|
||||
from os import path
|
||||
import shutil
|
||||
import json
|
||||
|
||||
|
||||
def find_or_copy_file(destFilePath, sourceFilePath):
|
||||
if path.exists(destFilePath):
|
||||
return
|
||||
if not path.exists(sourceFilePath):
|
||||
raise ValueError('find_or_copy_file: source file [', sourceFilePath, '] doesn\'t exist')
|
||||
return
|
||||
|
||||
dstDir = path.dirname(destFilePath)
|
||||
if not path.isdir(dstDir):
|
||||
os.makedirs(dstDir)
|
||||
shutil.copyfile(sourceFilePath, destFilePath)
|
||||
|
||||
def load_json_file(filePath):
|
||||
file_stream = open(filePath, "r")
|
||||
return json.load(file_stream)
|
||||
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
import os
|
||||
import platform
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
PYTHON_64 = platform.architecture()[0] == '64bit'
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not PYTHON_64:
|
||||
raise RuntimeError("32-bit Python is not a supported platform.")
|
||||
|
||||
with open(os.path.join(PROJECT_ROOT, 'README.txt')) as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name="atom_rpi_tools",
|
||||
version="1.0.0",
|
||||
description='Python interface to Atom RPI tools',
|
||||
long_description=long_description,
|
||||
packages=find_packages(exclude=['tests'])
|
||||
)
|
||||
+4
-1
@@ -51,7 +51,8 @@ namespace AtomToolsFramework
|
||||
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
|
||||
|
||||
// ModularViewportCameraControllerRequestBus overrides ...
|
||||
void InterpolateToTransform(const AZ::Transform& worldFromLocal) override;
|
||||
void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override;
|
||||
AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const override;
|
||||
|
||||
private:
|
||||
// AzFramework::ViewportDebugDisplayEventBus overrides ...
|
||||
@@ -71,6 +72,8 @@ namespace AtomToolsFramework
|
||||
AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity();
|
||||
float m_animationT = 0.0f;
|
||||
CameraMode m_cameraMode = CameraMode::Control;
|
||||
AZStd::optional<AZ::Vector3> m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished.
|
||||
//!< Will be cleared when the view changes (camera looks away).
|
||||
bool m_updatingTransform = false;
|
||||
|
||||
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;
|
||||
|
||||
+6
-1
@@ -32,7 +32,12 @@ namespace AtomToolsFramework
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Begin a smooth transition of the camera to the requested transform.
|
||||
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0;
|
||||
//! @param worldFromLocal The transform of where the camera should end up.
|
||||
//! @param lookAtDistance The distance between the camera transform and the imagined look at point.
|
||||
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) = 0;
|
||||
|
||||
//! Look at point after an interpolation has finished and no translation has occurred.
|
||||
virtual AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const = 0;
|
||||
|
||||
protected:
|
||||
~ModularViewportCameraControllerRequests() = default;
|
||||
|
||||
+20
-2
@@ -140,6 +140,18 @@ namespace AtomToolsFramework
|
||||
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
|
||||
m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, event.m_deltaTime.count());
|
||||
|
||||
// if there has been an interpolation, only clear the look at point if it is no longer
|
||||
// centered in the view (the camera has looked away from it)
|
||||
if (m_lookAtAfterInterpolation.has_value())
|
||||
{
|
||||
if (const float lookDirection =
|
||||
(*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY());
|
||||
!AZ::IsCloseMag(lookDirection, 1.0f, 0.001f))
|
||||
{
|
||||
m_lookAtAfterInterpolation = {};
|
||||
}
|
||||
}
|
||||
|
||||
viewportContext->SetCameraTransform(m_camera.Transform());
|
||||
}
|
||||
else if (m_cameraMode == CameraMode::Animation)
|
||||
@@ -148,8 +160,8 @@ namespace AtomToolsFramework
|
||||
{
|
||||
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
|
||||
};
|
||||
const float transitionT = smootherStepFn(m_animationT);
|
||||
|
||||
const float transitionT = smootherStepFn(m_animationT);
|
||||
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
m_transformStart.GetRotation().Slerp(m_transformEnd.GetRotation(), transitionT),
|
||||
m_transformStart.GetTranslation().Lerp(m_transformEnd.GetTranslation(), transitionT));
|
||||
@@ -185,11 +197,17 @@ namespace AtomToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal)
|
||||
void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance)
|
||||
{
|
||||
m_animationT = 0.0f;
|
||||
m_cameraMode = CameraMode::Animation;
|
||||
m_transformStart = m_camera.Transform();
|
||||
m_transformEnd = worldFromLocal;
|
||||
m_lookAtAfterInterpolation = m_transformEnd.GetTranslation() + m_transformEnd.GetBasisY() * lookAtDistance;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::Vector3> ModernViewportCameraControllerInstance::LookAtAfterInterpolation() const
|
||||
{
|
||||
return m_lookAtAfterInterpolation;
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -29,7 +29,8 @@ ly_add_target(
|
||||
Legacy::CryCommon
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_Bootstrap.Headers
|
||||
PUBLIC
|
||||
Gem::Atom_AtomBridge.Static
|
||||
)
|
||||
|
||||
################################################################################
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user