merge from main

This commit is contained in:
mnaumov
2021-06-03 16:01:27 -07:00
172 changed files with 8163 additions and 9121 deletions
@@ -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
+1 -1
View File
@@ -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)
{
+1 -1
View File
@@ -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)
{
+1 -1
View File
@@ -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();
/**
@@ -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
@@ -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
+8 -8
View File
@@ -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");
+2 -1
View File
@@ -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)
+12 -12
View File
@@ -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);
@@ -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;
@@ -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(
@@ -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)
{
@@ -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
@@ -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.
@@ -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;
+16
View File
@@ -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";
+9 -9
View 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;
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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();
+2 -2
View File
@@ -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;
+2 -1
View File
@@ -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;
+1
View File
@@ -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);
+1 -1
View File
@@ -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);
}
}
}
@@ -290,6 +290,9 @@ namespace O3DE::ProjectManager
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)
{
@@ -312,6 +315,36 @@ namespace O3DE::ProjectManager
return !PyErr_Occurred();
}
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);
@@ -426,7 +459,7 @@ namespace O3DE::ProjectManager
return result;
}
AZ::Outcome<GemInfo> PythonBindings::GetGemInfo(const QString& path)
AZ::Outcome<GemInfo> PythonBindings::GetGemInfo(const QString& path)
{
GemInfo gemInfo = GemInfoFromPath(pybind11::str(path.toStdString()));
if (gemInfo.IsValid())
@@ -65,9 +65,11 @@ namespace O3DE::ProjectManager
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;
@@ -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
+2 -1
View File
@@ -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
@@ -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()
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:
@@ -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;
@@ -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>
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a476e99b55cf2a76fef6775c5a57dad29f8ffcb942c625bab04c89051a72a560
size 62626
oid sha256:838830c99f344f5b68e5e85c9bc52751350caf48e662c9c2b767ab77039bbd8f
size 103472
@@ -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
@@ -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();
+2
View File
@@ -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
)
+39
View File
@@ -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
+10
View File
@@ -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)
+33
View File
@@ -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'])
)
@@ -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;
@@ -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;
@@ -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
)
################################################################################
@@ -27,11 +27,14 @@
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h>
#include <AtomBridge/PerViewportDynamicDrawInterface.h>
namespace AZ
{
class FFont;
static constexpr char AtomFontDynamicDrawContextName[] = "AtomFont";
//! AtomFont is the font system manager.
//! AtomFont manages the lifetime of FFont instances, each of which represents an individual font (e.g Courier New Italic)
@@ -90,13 +93,6 @@ namespace AZ
AzFramework::FontDrawInterface* GetFontDrawInterface(AzFramework::FontId fontId) const override;
AzFramework::FontDrawInterface* GetDefaultFontDrawInterface() const override;
void SceneAboutToBeRemoved(AzFramework::Scene& scene);
// Atom DynamicDraw interface management
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene);
public:
void UnregisterFont(const char* fontName);
@@ -108,8 +104,6 @@ namespace AZ
using FontFamilyMap = AZStd::unordered_map<AZStd::string, AZStd::weak_ptr<FontFamily>>;
using FontFamilyReverseLookupMap = AZStd::unordered_map<FontFamily*, FontFamilyMap::iterator>;
using SceneToDynamicDrawMap = AZStd::unordered_map<AZ::RPI::Scene*, AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext>>;
private:
//! Convenience method for loading fonts
IFFont* LoadFont(const char* fontName);
@@ -145,9 +139,6 @@ namespace AZ
int r_persistFontFamilies = 1; //!< Persist fonts for application lifetime to prevent unnecessary work; enabled by default.
AZStd::vector<FontFamilyPtr> m_persistedFontFamilies; //!< Stores persisted fonts (if "persist font families" is enabled)
SceneToDynamicDrawMap m_sceneToDynamicDrawMap;
AZStd::shared_mutex m_sceneToDynamicDrawMutex;
};
}
#endif
@@ -42,11 +42,9 @@
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/Bootstrap/DefaultWindowBus.h>
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
struct ISystem;
namespace AZ
@@ -68,7 +66,6 @@ namespace AZ
: public IFFont
, public AZStd::intrusive_refcount<AZStd::atomic_uint, FontDeleter>
, public AzFramework::FontDrawInterface
, private AZ::Render::Bootstrap::NotificationBus::Handler
{
using ref_count = AZStd::intrusive_refcount<AZStd::atomic_uint, FontDeleter>;
friend FontDeleter;
@@ -168,8 +165,8 @@ namespace AZ
struct FontShaderData
{
AZ::RHI::ShaderInputImageIndex m_imageInputIndex;
AZ::RHI::ShaderInputConstantIndex m_viewProjInputIndex;
AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture";
AZ::RHI::ShaderInputNameIndex m_viewProjInputIndex = "m_worldToProj";
};
public:
@@ -230,7 +227,6 @@ namespace AZ
private:
virtual ~FFont();
bool InitFont(AZ::RPI::Scene* renderScene);
bool InitTexture();
bool InitCache();
@@ -281,8 +277,6 @@ namespace AZ
void ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) const;
void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override;
RPI::WindowContextSharedPtr GetDefaultWindowContext() const;
RPI::ViewportContextPtr GetDefaultViewportContext() const;
@@ -303,6 +297,8 @@ namespace AZ
string m_name;
string m_curPath;
AZ::Name m_dynamicDrawContextName = AZ::Name(AZ::AtomFontDynamicDrawContextName);
FontTexture* m_fontTexture = nullptr;
size_t m_fontBufferSize = 0;
@@ -315,13 +311,6 @@ namespace AZ
AtomFont* m_atomFont = nullptr;
bool m_fontTexDirty = false;
enum class InitializationState : AZ::u8
{
Uninitialized,
Initializing,
Initialized
};
AZStd::atomic<InitializationState> m_fontInitializationState = InitializationState::Uninitialized;
FontEffects m_effects;
@@ -356,6 +345,7 @@ namespace AZ
if (font && font->m_atomFont)
{
font->m_atomFont->UnregisterFont(font->m_name);
font->m_atomFont = nullptr;
}
delete font;
@@ -354,17 +354,26 @@ AZ::AtomFont::AtomFont(ISystem* system)
#endif
AZ::Interface<AzFramework::FontQueryInterface>::Register(this);
m_sceneEventHandler = AzFramework::ISceneSystem::SceneEvent::Handler(
[this](AzFramework::ISceneSystem::EventType eventType, const AZStd::shared_ptr<AzFramework::Scene>& scene)
// register font per viewport dynamic draw context.
static const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
AZ::AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(
AZ::Name(AZ::AtomFontDynamicDrawContextName),
[](RPI::Ptr<RPI::DynamicDrawContext> drawContext)
{
if (eventType == AzFramework::ISceneSystem::EventType::ScenePendingRemoval)
{
SceneAboutToBeRemoved(*scene);
}
Data::Instance<RPI::Shader> shader = AZ::RPI::LoadShader(shaderFilepath);
AZ::RPI::ShaderOptionList shaderOptions;
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false")));
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
drawContext->InitShaderWithVariant(shader, &shaderOptions);
drawContext->InitVertexFormat(
{
{"POSITION", RHI::Format::R32G32B32_FLOAT},
{"COLOR", RHI::Format::B8G8R8A8_UNORM},
{"TEXCOORD0", RHI::Format::R32G32_FLOAT}
});
drawContext->EndInit();
});
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Font created before the scene system is available.");
sceneSystem->ConnectToEvents(m_sceneEventHandler);
}
AZ::AtomFont::~AtomFont()
@@ -860,52 +869,5 @@ XmlNodeRef AZ::AtomFont::LoadFontFamilyXml(const char* fontFamilyName, string& o
return root;
}
void AZ::AtomFont::SceneAboutToBeRemoved(AzFramework::Scene& scene)
{
AZ::RPI::ScenePtr* rpiScene = scene.FindSubsystem<AZ::RPI::ScenePtr>();
if (rpiScene)
{
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
if (auto it = m_sceneToDynamicDrawMap.find(rpiScene->get()); it != m_sceneToDynamicDrawMap.end())
{
m_sceneToDynamicDrawMap.erase(it);
}
}
}
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> AZ::AtomFont::GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene)
{
static const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
{
// shared lock while reading
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
if (auto it = m_sceneToDynamicDrawMap.find(scene); it != m_sceneToDynamicDrawMap.end())
{
return it->second;
}
}
// Create and initialize DynamicDrawContext for font draw
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(scene);
Data::Instance<RPI::Shader> shader = AZ::RPI::LoadShader(shaderFilepath);
AZ::RPI::ShaderOptionList shaderOptions;
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false")));
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
dynamicDraw->InitShaderWithVariant(shader, &shaderOptions);
dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::B8G8R8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}});
dynamicDraw->EndInit();
// exclusive lock while writing
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
m_sceneToDynamicDrawMap.insert(AZStd::make_pair(scene, dynamicDraw));
return dynamicDraw;
}
#endif
@@ -60,14 +60,7 @@ static const size_t MaxVerts = 8 * 1024; // 2048 quads
static const size_t MaxIndices = (MaxVerts * 6) / 4; // 6 indices per quad, 6/4 * MaxVerts
static const char DrawList2DPassName[] = "2dpass";
namespace ShaderInputs
{
static const char TextureIndexName[] = "m_texture";
static const char WorldToProjIndexName[] = "m_worldToProj";
static const char SamplerIndexName[] = "m_sampler";
}
AZ::FFont::FFont(AtomFont* atomFont, const char* fontName)
AZ::FFont::FFont(AZ::AtomFont* atomFont, const char* fontName)
: m_name(fontName)
, m_atomFont(atomFont)
{
@@ -78,9 +71,14 @@ AZ::FFont::FFont(AtomFont* atomFont, const char* fontName)
FontEffect* effect = AddEffect("default");
effect->AddPass();
AddRef();
// Create cpu memory to cache the font draw data before submit
m_vertexBuffer = new SVF_P3F_C4B_T2F[MaxVerts];
m_indexBuffer = new u16[MaxIndices];
AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
m_vertexCount = 0;
m_indexCount = 0;
AddRef();
}
AZ::RPI::ViewportContextPtr AZ::FFont::GetDefaultViewportContext() const
@@ -98,55 +96,10 @@ AZ::RPI::WindowContextSharedPtr AZ::FFont::GetDefaultWindowContext() const
return {};
}
bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
{
if (!renderScene)
{
return false;
}
auto initializationState = InitializationState::Uninitialized;
// Do an atomic transition to Initializing if we're in the Uninitialized state.
// Otherwise, check the current state.
// If we're Initialized, there's no more work to be done, return true to indicate we're good to go.
// If we're Initializing (on another thread), return false to let the consumer know it's not safe for us to be used yet.
if (!m_fontInitializationState.compare_exchange_strong(initializationState, InitializationState::Initializing))
{
return initializationState == InitializationState::Initialized;
}
// Create and initialize DynamicDrawContext for font draw
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(renderScene);
// Save draw srg input indices for later use
Data::Instance<RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
const RHI::ShaderResourceGroupLayout* layout = drawSrg->GetAsset()->GetLayout();
m_fontShaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::TextureIndexName));
AZ_Error("AtomFont::FFont", m_fontShaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.",
ShaderInputs::TextureIndexName);
m_fontShaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::WorldToProjIndexName));
AZ_Error("AtomFont::FFont", m_fontShaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.",
ShaderInputs::WorldToProjIndexName);
// Create cpu memory to cache the font draw data before submit
m_vertexBuffer = new SVF_P3F_C4B_T2F[MaxVerts];
m_indexBuffer = new u16[MaxIndices];
m_vertexCount = 0;
m_indexCount = 0;
m_fontInitializationState = InitializationState::Initialized;
return true;
}
AZ::FFont::~FFont()
{
AZ_Assert(m_atomFont == nullptr, "The font should already be unregistered through a call to AZ::FFont::Release()");
AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
delete[] m_vertexBuffer;
delete[] m_indexBuffer;
@@ -303,7 +256,8 @@ void AZ::FFont::DrawStringUInternal(
const TextDrawContext& ctx)
{
// Lazily ensure we're initialized before attempting to render.
if (!viewportContext || !InitFont(viewportContext->GetRenderScene().get()))
// Validate that there is a render scene before attempting to init.
if (!viewportContext || !viewportContext->GetRenderScene())
{
return;
}
@@ -323,12 +277,6 @@ void AZ::FFont::DrawStringUInternal(
return;
}
// if the font is about to be deleted then m_atomFont can be nullptr
if (!m_atomFont)
{
return;
}
const bool orthoMode = ctx.m_overrideViewProjMatrices;
const float viewX = viewport.m_minX;
@@ -406,14 +354,17 @@ void AZ::FFont::DrawStringUInternal(
if (numQuads)
{
auto dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(viewportContext->GetRenderScene().get());
//setup per draw srg
auto drawSrg = dynamicDraw->NewDrawSrg();
drawSrg->SetConstant(m_fontShaderData.m_viewProjInputIndex, modelViewProjMat);
drawSrg->SetImageView(m_fontShaderData.m_imageInputIndex, m_fontStreamingImage->GetImageView());
drawSrg->Compile();
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = AZ::AtomBridge::PerViewportDynamicDraw::Get()->GetDynamicDrawContextForViewport(m_dynamicDrawContextName, viewportContext->GetId());
if (dynamicDraw)
{
//setup per draw srg
auto drawSrg = dynamicDraw->NewDrawSrg();
drawSrg->SetConstant(m_fontShaderData.m_viewProjInputIndex, modelViewProjMat);
drawSrg->SetImageView(m_fontShaderData.m_imageInputIndex, m_fontStreamingImage->GetImageView());
drawSrg->Compile();
dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg);
dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg);
}
m_indexCount = 0;
m_vertexCount = 0;
}
@@ -694,12 +645,6 @@ uint32_t AZ::FFont::WriteTextQuadsToBuffers(SVF_P2F_C4B_T2F_F4B* verts, uint16_t
return numQuadsWritten;
}
// if the font is about to be deleted then m_atomFont can be nullptr
if (!m_atomFont)
{
return numQuadsWritten;
}
SVF_P2F_C4B_T2F_F4B* vertexData = verts;
uint16_t* indexData = indices;
size_t vertexOffset = 0;
@@ -1523,7 +1468,7 @@ bool AZ::FFont::UpdateTexture()
{
using namespace AZ;
if (m_fontInitializationState != InitializationState::Initialized || !m_fontImage)
if (!m_fontImage)
{
return false;
}
@@ -1591,7 +1536,7 @@ void AZ::FFont::Prepare(const char* str, bool updateTexture, const AtomFont::Gly
const bool rerenderGlyphs = m_sizeBehavior == SizeBehavior::Rerender;
const AtomFont::GlyphSize usedGlyphSize = rerenderGlyphs ? glyphSize : AtomFont::defaultGlyphSize;
bool texUpdateNeeded = m_fontTexture->PreCacheString(str, nullptr, m_sizeRatio, usedGlyphSize, m_fontHintParams) == 1 || m_fontTexDirty;
if (m_fontInitializationState == InitializationState::Initialized && updateTexture && texUpdateNeeded && m_fontImage)
if (updateTexture && texUpdateNeeded && m_fontImage)
{
UpdateTexture();
m_fontTexDirty = false;
@@ -1625,12 +1570,6 @@ void AZ::FFont::ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) co
y *= height / WindowScaleHeight;
}
void AZ::FFont::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
{
InitFont(bootstrapScene);
}
static void SetCommonContextFlags(AZ::TextDrawContext& ctx, const AzFramework::TextDrawParameters& params)
{
if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Center)
@@ -0,0 +1,37 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
//! Duplicates will be ignored
//! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp
class ThumbnailFeatureProcessorProviderRequests
: public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with thumbnail renderer
virtual const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const = 0;
};
using ThumbnailFeatureProcessorProviderBus = AZ::EBus<ThumbnailFeatureProcessorProviderRequests>;
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -34,12 +34,34 @@ namespace AZ
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type());
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type());
SystemTickBus::Handler::BusConnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusConnect();
m_steps[Step::Initialize] = AZStd::make_shared<InitializeStep>(this);
m_steps[Step::FindThumbnailToRender] = AZStd::make_shared<FindThumbnailToRenderStep>(this);
m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared<WaitForAssetsToLoadStep>(this);
m_steps[Step::Capture] = AZStd::make_shared<CaptureStep>(this);
m_steps[Step::ReleaseResources] = AZStd::make_shared<ReleaseResourcesStep>(this);
m_minimalFeatureProcessors =
{
"AZ::Render::TransformServiceFeatureProcessor",
"AZ::Render::MeshFeatureProcessor",
"AZ::Render::SimplePointLightFeatureProcessor",
"AZ::Render::SimpleSpotLightFeatureProcessor",
"AZ::Render::PointLightFeatureProcessor",
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
// flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// "AZ::Render::DirectionalLightFeatureProcessor",
"AZ::Render::DiskLightFeatureProcessor",
"AZ::Render::CapsuleLightFeatureProcessor",
"AZ::Render::QuadLightFeatureProcessor",
"AZ::Render::DecalTextureArrayFeatureProcessor",
"AZ::Render::ImageBasedLightFeatureProcessor",
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor"
};
}
CommonThumbnailRenderer::~CommonThumbnailRenderer()
@@ -50,6 +72,7 @@ namespace AZ
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect();
SystemTickBus::Handler::BusDisconnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect();
}
void CommonThumbnailRenderer::SetStep(Step step)
@@ -77,6 +100,11 @@ namespace AZ
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
}
const AZStd::vector<AZStd::string>& CommonThumbnailRenderer::GetCustomFeatureProcessors() const
{
return m_minimalFeatureProcessors;
}
AZStd::shared_ptr<ThumbnailRendererData> CommonThumbnailRenderer::GetData() const
{
return m_data;
@@ -17,6 +17,8 @@
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
@@ -34,9 +36,10 @@ namespace AZ
//! Provides custom rendering of material and model thumbnails
class CommonThumbnailRenderer
: private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
: public ThumbnailRendererContext
, private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
, private SystemTickBus::Handler
, public ThumbnailRendererContext
, private ThumbnailFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0)
@@ -57,9 +60,13 @@ namespace AZ
//! SystemTickBus::Handler interface overrides...
void OnSystemTick() override;
//! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides...
const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const override;
AZStd::unordered_map<Step, AZStd::shared_ptr<ThumbnailRendererStep>> m_steps;
Step m_currentStep = Step::None;
AZStd::shared_ptr<ThumbnailRendererData> m_data;
AZStd::vector<AZStd::string> m_minimalFeatureProcessors;
};
} // namespace Thumbnails
} // namespace LyIntegration
@@ -11,10 +11,16 @@
*/
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/EBus/Results.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
@@ -23,10 +29,11 @@
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h>
@@ -37,7 +44,6 @@ namespace AZ
{
namespace Thumbnails
{
InitializeStep::InitializeStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
@@ -50,24 +56,23 @@ namespace AZ
data->m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
data->m_entityContext->InitContext();
// Create and register a scene with minimum required feature processors
// Create and register a scene with all required feature processors
RPI::SceneDescriptor sceneDesc;
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor");
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DirectionalLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DiskLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::CapsuleLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::QuadLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DecalTextureArrayFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::ImageBasedLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PostProcessFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SkyBoxFeatureProcessor");
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> results;
ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors);
AZStd::set<AZStd::string> featureProcessorNames;
for (auto& resultCollection : results.values)
{
for (auto& featureProcessorName : resultCollection)
{
if (featureProcessorNames.emplace(featureProcessorName).second)
{
sceneDesc.m_featureProcessorNames.push_back(featureProcessorName);
}
}
}
data->m_scene = RPI::Scene::CreateScene(sceneDesc);
@@ -10,11 +10,12 @@
#
set(FILES
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h
Source/Module.cpp
Source/Animation/EditorAttachmentComponent.h
Source/Animation/EditorAttachmentComponent.cpp
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
Source/EditorCommonFeaturesSystemComponent.h
Source/EditorCommonFeaturesSystemComponent.cpp
Source/CoreLights/EditorAreaLightComponent.h
@@ -9,6 +9,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME DccScriptingInterface.Static STATIC
NAMESPACE Gem
@@ -38,3 +42,9 @@ ly_add_target(
PRIVATE
Gem::DccScriptingInterface.Static
)
# Any 'tool' type applications should use Gem::DccScriptingInterface.Editor:
ly_create_alias(NAME DccScriptingInterface.Tools NAMESPACE Gem TARGETS Gem::DccScriptingInterface.Editor)
# Add an empty 'builders' alias to allow the DccScriptInterface root gem path to be added to the generated
# cmake_dependencies.<project>.assetprocessor.setreg to allow the asset scan folder for it to be added
ly_create_alias(NAME DccScriptingInterface.Builders NAMESPACE Gem)
@@ -178,7 +178,10 @@ namespace Camera
if ((!m_viewSystem)||(!m_system))
{
// perform first-time init
m_system = gEnv->pSystem;
if (gEnv)
{
m_system = gEnv->pSystem;
}
if (m_system)
{
// Initialize local view.
@@ -165,8 +165,8 @@ namespace EMotionFX
const AZ::Outcome<size_t> boolYParamIndexOutcome = m_animGraphInstance->FindParameterIndex(nameBoolY);
success = boolXParamIndexOutcome.IsSuccess() && boolYParamIndexOutcome.IsSuccess();
uint32 boolXOutputPortIndex;
uint32 boolYOutputPortIndex;
uint32 boolXOutputPortIndex = InvalidIndex32;
uint32 boolYOutputPortIndex = InvalidIndex32;
const int portIndicesTosetCount = 2;
int portIndicesFound = 0;
const AZStd::vector<EMotionFX::AnimGraphNode::Port>& parameterNodeOutputPorts = parameterNode->GetOutputPorts();
+15 -6
View File
@@ -612,7 +612,9 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL
int y1 = y0 + 1;
int z1 = z0 + 1;
FN_DECIMAL xs, ys, zs;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
FN_DECIMAL zs = 0.0f;
switch (m_interp)
{
case Linear:
@@ -726,7 +728,8 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL
int x1 = x0 + 1;
int y1 = y0 + 1;
FN_DECIMAL xs, ys;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
switch (m_interp)
{
case Linear:
@@ -840,7 +843,9 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA
int y1 = y0 + 1;
int z1 = z0 + 1;
FN_DECIMAL xs, ys, zs;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
FN_DECIMAL zs = 0.0f;
switch (m_interp)
{
case Linear:
@@ -962,7 +967,8 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA
int x1 = x0 + 1;
int y1 = y0 + 1;
FN_DECIMAL xs, ys;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
switch (m_interp)
{
case Linear:
@@ -1699,7 +1705,9 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y, FN_DECIMAL z) c
int zr = FastRound(z);
FN_DECIMAL distance = 999999;
int xc, yc, zc;
int xc = 0;
int yc = 0;
int zc = 0;
switch (m_cellularDistanceFunction)
{
@@ -1923,7 +1931,8 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y) const
int yr = FastRound(y);
FN_DECIMAL distance = 999999;
int xc, yc;
int xc = 0;
int yc = 0;
switch (m_cellularDistanceFunction)
{
@@ -1072,8 +1072,7 @@ namespace GraphCanvas
if (!id.empty())
{
Selector selector = Selector::Get(id);
result.emplace_back(selector);
result.emplace_back(Selector::Get(id));
continue;
}
@@ -1111,8 +1110,7 @@ namespace GraphCanvas
{
bits.emplace_back(stateSelector);
}
Selector selector = aznew CompoundSelector(std::move(bits));
nestedSelectors.emplace_back(selector);
nestedSelectors.emplace_back(aznew CompoundSelector(std::move(bits)));
}
}

Some files were not shown because too many files have changed in this diff Show More