Merge branch 'main' into Atom/dmcdiar/ATOM-15718

This commit is contained in:
Doug McDiarmid
2021-06-03 18:45:53 -07:00
167 changed files with 8482 additions and 9386 deletions
@@ -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));
@@ -93,6 +93,7 @@ namespace AzToolsFramework
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);
/**
@@ -173,6 +174,8 @@ namespace AzToolsFramework
static EntityAlias GenerateEntityAlias();
AliasPath GetAbsoluteInstanceAliasPath() const;
static InstanceAlias GenerateInstanceAlias();
protected:
/**
* Gets the entities owned by this instance
@@ -189,8 +192,6 @@ namespace AzToolsFramework
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);
static InstanceAlias GenerateInstanceAlias();
// Provide access to private data members in the serializer
friend class JsonInstanceSerializer;
friend class InstanceEntityIdMapper;
@@ -152,6 +152,30 @@ namespace AzToolsFramework
Instance::EntityList newEntities;
if (PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
{
// If a link was created for a nested instance before the changes were propagated,
// then we associate it correctly here
instanceToUpdate->GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
if (nestedInstance->GetLinkId() != InvalidLinkId)
{
return;
}
for (auto linkId : currentTemplate.GetLinks())
{
LinkReference nestedLink = m_prefabSystemComponentInterface->FindLink(linkId);
if (!nestedLink.has_value())
{
continue;
}
if (nestedLink->get().GetInstanceName() == nestedInstance->GetInstanceAlias())
{
nestedInstance->SetLinkId(linkId);
break;
}
}
});
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
}
@@ -297,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);
}
@@ -595,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
@@ -754,16 +899,29 @@ namespace AzToolsFramework
if (!EntitiesBelongToSameInstance(entityIds))
{
return AZ::Failure(AZStd::string("Cannot duplicate multiple "
"entities belonging to different instances with one operation."));
return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
"Change your selection to contain entities in the same instance."));
}
// We've already verified the entities are all owned by the same instance,
// so we can just retrieve our instance from the first entity in the list.
InstanceOptionalReference commonEntityOwningInstance = GetOwnerInstanceByEntityId(entityIds[0]);
AZ_Assert(
commonEntityOwningInstance.has_value(),
"Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided");
AZ::EntityId firstEntityIdToDuplicate = entityIds[0];
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
if (!commonOwningInstance.has_value())
{
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
}
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
// cannot duplicate an instance from itself.
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
{
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
}
if (!commonOwningInstance.has_value())
{
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
}
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
@@ -776,105 +934,63 @@ namespace AzToolsFramework
{
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities");
// Take a snapshot of the instance DOM before we manipulate it
Prefab::PrefabDom instanceDomBefore;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonEntityOwningInstance->get());
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<Instance*> instances;
// Gather all entities/instances in the hierarchy, but don't detach them because we are duplicating not deleting.
EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonEntityOwningInstance->get(), entities, instances);
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
if (!success)
{
return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication"));
}
// Make a copy of our before instance DOM where we will add our duplicated entities
Prefab::PrefabDom instanceDomAfter;
// Take a snapshot of the instance DOM before we manipulate it
PrefabDom instanceDomBefore;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get());
// Make a copy of our before instance DOM where we will add our duplicated entities and/or instances
PrefabDom instanceDomAfter;
instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator());
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
EntityIdList duplicatedEntityAndInstanceIds;
for (AZ::Entity* entity : entities)
{
EntityAliasOptionalReference oldAliasRef = commonEntityOwningInstance->get().GetEntityAlias(entity->GetId());
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
EntityAlias oldAlias = oldAliasRef.value();
// Duplicate any nested entities and instances as requested
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
newInstanceAliasToOldInstanceMap);
// Give this the outer allocator so that the memory reference will be valid when
// it gets used for AddMember
Prefab::PrefabDom entityDomBefore(&instanceDomAfter.GetAllocator());
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
// Keep track of the old alias <-> new alias mapping for this duplicated entity
// so we can fixup references later
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
oldAliasToNewAliasMap.insert(AZStd::make_pair(oldAlias, newEntityAlias));
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
entityDomBefore.Accept(writer);
// Store our duplicated Entity DOM with its new alias as a string
// so that we can fixup entity alias references before adding it
// to the Entities member of our instance DOM
QString entityDomString(buffer.GetString());
aliasToEntityDomMap.insert(AZStd::make_pair(newEntityAlias, entityDomString));
}
auto entitiesIter = instanceDomAfter.FindMember(PrefabDomUtils::EntitiesName);
AZ_Assert(entitiesIter != instanceDomAfter.MemberEnd(), "Instance DOM missing the Entities member.");
// Now that all the duplicated Entity DOMs have been created, we need to iterate
// through them and replace any previous EntityAlias references with the new ones.
// These are more than just parent entity references for nested entities, this will
// also cover any EntityId references that were made in the components between them.
for (auto aliasEntityPair : aliasToEntityDomMap)
{
EntityAlias newEntityAlias = aliasEntityPair.first;
QString newEntityDomString = aliasEntityPair.second;
// Replace all of the old alias references with the new ones
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
// inadvertently replacing a matching string vs. where an actual EntityId is expected
// This will cover both cases where an alias could be used in a normal entity vs. an instance
for (auto aliasMapIter : oldAliasToNewAliasMap)
{
ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second);
}
// Create the new Entity DOM from parsing the JSON string
Prefab::PrefabDom entityDomAfter(&instanceDomAfter.GetAllocator());
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
// Add the new Entity DOM to the Entities member of the instance
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), instanceDomAfter.GetAllocator());
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, instanceDomAfter.GetAllocator());
}
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity duplication");
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
command->SetParent(undoBatch.GetUndoBatch());
command->Capture(instanceDomBefore, instanceDomAfter, commonEntityOwningInstance->get().GetTemplateId());
command->RunRedo();
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
command->Redo();
EntityIdList duplicatedEntityIds;
for (auto aliasMapIter : oldAliasToNewAliasMap)
// Create links for our duplicated instances (if any were duplicated)
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
{
EntityAlias newEntityAlias = aliasMapIter.second;
LinkId oldLinkId = oldInstance->GetLinkId();
auto linkRef = m_prefabSystemComponentInterface->FindLink(oldLinkId);
AZ_Assert(
linkRef.has_value(), "Unable to find link with id '%llu' during instance duplication.",
oldLinkId);
AliasPath absoluteEntityPath = commonEntityOwningInstance->get().GetAbsoluteInstanceAliasPath();
absoluteEntityPath.Append(newEntityAlias);
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
AZ_Assert(
linkPatches.has_value(), "Link with id '%llu' is missing patches.",
oldLinkId);
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
duplicatedEntityIds.push_back(newEntityId);
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
m_prefabSystemComponentInterface->CreateLink(
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
}
// Select the duplicated entities
auto selectionUndo = aznew SelectionCommand(duplicatedEntityIds, "Select Duplicated Entities");
// Select the duplicated entities/instances
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
}
@@ -1363,8 +1479,159 @@ namespace AzToolsFramework
return true;
}
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds)
{
if (entities.empty())
{
return;
}
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
for (AZ::Entity* entity : entities)
{
EntityAliasOptionalReference oldAliasRef = commonOwningInstance.GetEntityAlias(entity->GetId());
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
EntityAlias oldAlias = oldAliasRef.value();
// Give this the outer allocator so that the memory reference will be valid when
// it gets used for AddMember
PrefabDom entityDomBefore(&domToAddDuplicatedEntitiesUnder.GetAllocator());
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
// Keep track of the old alias <-> new alias mapping for this duplicated entity
// so we can fixup references later
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
oldAliasToNewAliasMap.emplace(oldAlias, newEntityAlias);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
entityDomBefore.Accept(writer);
// Store our duplicated Entity DOM with its new alias as a string
// so that we can fixup entity alias references before adding it
// to the Entities member of our instance DOM
QString entityDomString(buffer.GetString());
aliasToEntityDomMap.emplace(newEntityAlias, entityDomString);
}
auto entitiesIter = domToAddDuplicatedEntitiesUnder.FindMember(PrefabDomUtils::EntitiesName);
AZ_Assert(entitiesIter != domToAddDuplicatedEntitiesUnder.MemberEnd(), "Instance DOM missing the Entities member.");
// Now that all the duplicated Entity DOMs have been created, we need to iterate
// through them and replace any previous EntityAlias references with the new ones.
// These are more than just parent entity references for nested entities, this will
// also cover any EntityId references that were made in the components between them.
for (auto [newEntityAlias, newEntityDomString] : aliasToEntityDomMap)
{
// Replace all of the old alias references with the new ones
for (auto [oldAlias, newAlias] : oldAliasToNewAliasMap)
{
ReplaceOldAliases(newEntityDomString, oldAlias, newAlias);
}
// Create the new Entity DOM from parsing the JSON string
PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
// Add the new Entity DOM to the Entities member of the instance
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), domToAddDuplicatedEntitiesUnder.GetAllocator());
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
}
for (auto aliasMapIter : oldAliasToNewAliasMap)
{
EntityAlias newEntityAlias = aliasMapIter.second;
AliasPath absoluteEntityPath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
absoluteEntityPath.Append(newEntityAlias);
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
duplicatedEntityIds.push_back(newEntityId);
}
}
void PrefabPublicHandler::DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap)
{
if (instances.empty())
{
return;
}
AZStd::unordered_map<InstanceAlias, InstanceAlias> oldInstanceAliasToNewInstanceAliasMap;
AZStd::unordered_map<InstanceAlias, QString> aliasToInstanceDomMap;
for (auto instance : instances)
{
PrefabDom nestedInstanceDomBefore;
m_instanceToTemplateInterface->GenerateDomForInstance(nestedInstanceDomBefore, *instance);
// Keep track of the old alias <-> new alias mapping for this duplicated instance
// so we can fixup references later
InstanceAlias oldAlias = instance->GetInstanceAlias();
InstanceAlias newInstanceAlias = Instance::GenerateInstanceAlias();
oldInstanceAliasToNewInstanceAliasMap.emplace(oldAlias, newInstanceAlias);
// Keep track of our new instance alias with the Instance it was duplicated from,
// so that after all instances are duplicated, we can go back and create links for them
newInstanceAliasToOldInstanceMap.emplace(newInstanceAlias, instance);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
nestedInstanceDomBefore.Accept(writer);
// Store our duplicated Instance DOM with its new alias as a string
// so that we can fixup instance alias references before adding it
// to the Instances member of our instance DOM
QString instanceDomString(buffer.GetString());
aliasToInstanceDomMap.emplace(newInstanceAlias, instanceDomString);
}
auto instancesIter = domToAddDuplicatedInstancesUnder.FindMember(PrefabDomUtils::InstancesName);
AZ_Assert(instancesIter != domToAddDuplicatedInstancesUnder.MemberEnd(), "Instance DOM missing the Instances member.");
// Now that all the duplicated Instance DOMs have been created, we need to iterate
// through them and replace any previous InstanceAlias references with the new ones.
for (auto [newInstanceAlias, newInstanceDomString]: aliasToInstanceDomMap)
{
// Replace all of the old alias references with the new ones
for (auto [oldAlias, newAlias] : oldInstanceAliasToNewInstanceAliasMap)
{
ReplaceOldAliases(newInstanceDomString, oldAlias, newAlias);
}
// Create the new Instance DOM from parsing the JSON string
PrefabDom nestedInstanceDomAfter(&domToAddDuplicatedInstancesUnder.GetAllocator());
nestedInstanceDomAfter.Parse(newInstanceDomString.toUtf8().constData());
// Add the new Instance DOM to the Instances member of the instance
rapidjson::Value aliasName(newInstanceAlias.c_str(), newInstanceAlias.length(), domToAddDuplicatedInstancesUnder.GetAllocator());
instancesIter->value.AddMember(AZStd::move(aliasName), nestedInstanceDomAfter, domToAddDuplicatedInstancesUnder.GetAllocator());
}
for (auto aliasMapIter : oldInstanceAliasToNewInstanceAliasMap)
{
InstanceAlias newInstanceAlias = aliasMapIter.second;
AliasPath absoluteInstancePath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
absoluteInstancePath.Append(newInstanceAlias);
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath);
duplicatedEntityIds.push_back(newEntityId);
}
}
void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
// Replace all of the old alias references with the new ones
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
// inadvertently replacing a matching string vs. where an actual EntityId is expected
// This will cover both cases where an alias could be used in a normal entity vs. an instance
QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data());
QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data());
@@ -73,6 +73,33 @@ namespace AzToolsFramework
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
/**
* Duplicate a list of entities owned by a common owning instance by directly
* copying/modifying their entries in the instance DOM
*
* \param commonOwningInstance The common owning instance of all the entities being duplicated.
* \param entities The list of Entities that will be duplicated.
* \param domToAddDuplicatedEntitiesUnder The DOM of the common owning instance where the duplicated
* entity DOM values will be added to.
* \param duplicatedEntityIds A list of EntityIds corresponding to the entities that were duplicated.
*/
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds);
/**
* Duplicate a list of instances owned by a common owning instance by directly
* copying/modifying their entries in the instance DOM
*
* \param commonOwningInstance The common owning instance of all the instances being duplicated.
* \param entities The list of Instances that will be duplicated.
* \param domToAddDuplicatedInstancesUnder The DOM of the common owning instance where the duplicated
* instance DOM values will be added to.
* \param duplicatedEntityIds A list of EntityIds corresponding to the instances that were duplicated.
*/
void DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap);
/**
* Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch.
@@ -90,8 +117,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.
@@ -134,6 +161,12 @@ namespace AzToolsFramework
bool IsCyclicalDependencyFound(
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
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,
@@ -706,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;
@@ -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);
}
}
}
@@ -294,7 +294,8 @@ namespace O3DE::ProjectManager
RegisterThisEngine();
return result == 0 && !PyErr_Occurred();
} catch ([[maybe_unused]] const std::exception& e)
}
catch ([[maybe_unused]] const std::exception& e)
{
AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what());
return false;
@@ -320,25 +321,25 @@ namespace O3DE::ProjectManager
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))
{
// 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)
{
for (auto engine : allEngines)
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
if (enginePath.Compare(m_enginePath) == 0)
{
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
if (enginePath.Compare(m_enginePath) == 0)
{
return;
}
return;
}
}
}
auto result = m_register.attr("register")(m_enginePath.c_str());
registrationResult = (result.cast<int>() == 0);
});
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!");
@@ -378,12 +379,12 @@ namespace O3DE::ProjectManager
auto o3deData = m_manifest.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(o3deData))
{
engineInfo.m_path = Py_To_String(enginePath);
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
engineInfo.m_path = Py_To_String(enginePath);
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]);
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path","");
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData, "third_party_path", "");
}
auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath);
@@ -391,8 +392,8 @@ namespace O3DE::ProjectManager
{
try
{
engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE");
engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE");
}
catch ([[maybe_unused]] const std::exception& e)
{
@@ -416,19 +417,19 @@ namespace O3DE::ProjectManager
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
{
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString();
auto registrationResult = m_register.attr("register")(
enginePath, // engine_path
pybind11::none(), // project_path
enginePath, // engine_path
pybind11::none(), // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
defaultProjectsFolder,
defaultGemsFolder,
@@ -477,12 +478,12 @@ namespace O3DE::ProjectManager
QVector<GemInfo> gems;
auto result = ExecuteWithLockErrorHandling([&]
{
for (auto path : m_manifest.attr("get_engine_gems")())
{
for (auto path : m_manifest.attr("get_engine_gems")())
{
gems.push_back(GemInfoFromPath(path));
}
});
gems.push_back(GemInfoFromPath(path));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -497,13 +498,13 @@ namespace O3DE::ProjectManager
QVector<GemInfo> gems;
auto result = ExecuteWithLockErrorHandling([&]
{
pybind11::str pyProjectPath = projectPath.toStdString();
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
{
pybind11::str pyProjectPath = projectPath.toStdString();
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
{
gems.push_back(GemInfoFromPath(path));
}
});
gems.push_back(GemInfoFromPath(path));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -518,12 +519,12 @@ namespace O3DE::ProjectManager
// Retrieve the path to the cmake file that lists the enabled gems.
pybind11::str enabledGemsFilename;
auto result = ExecuteWithLockErrorHandling([&]
{
const pybind11::str pyProjectPath = projectPath.toStdString();
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
pybind11::none(), // project_name
pyProjectPath); // project_path
});
{
const pybind11::str pyProjectPath = projectPath.toStdString();
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
pybind11::none(), // project_name
pyProjectPath); // project_path
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -532,13 +533,13 @@ namespace O3DE::ProjectManager
// Retrieve the actual list of names from the cmake file.
QVector<AZStd::string> gemNames;
result = ExecuteWithLockErrorHandling([&]
{
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
for (auto gemName : pyGemNames)
{
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
for (auto gemName : pyGemNames)
{
gemNames.push_back(Py_To_String(gemName));
}
});
gemNames.push_back(Py_To_String(gemName));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -552,13 +553,13 @@ namespace O3DE::ProjectManager
bool registrationResult = false;
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
return result && registrationResult;
}
@@ -568,30 +569,30 @@ namespace O3DE::ProjectManager
bool registrationResult = false;
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(
pybind11::none(), // engine_path
projectPath, // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
pybind11::none(), // default_projects_folder
pybind11::none(), // default_gems_folder
pybind11::none(), // default_templates_folder
pybind11::none(), // default_restricted_folder
pybind11::none(), // external_subdir_engine_path
pybind11::none(), // external_subdir_project_path
true, // remove
false // force
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(
pybind11::none(), // engine_path
projectPath, // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
pybind11::none(), // default_projects_folder
pybind11::none(), // default_gems_folder
pybind11::none(), // default_templates_folder
pybind11::none(), // default_restricted_folder
pybind11::none(), // external_subdir_engine_path
pybind11::none(), // external_subdir_project_path
true, // remove
false // force
);
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
return result && registrationResult;
}
@@ -649,12 +650,12 @@ namespace O3DE::ProjectManager
try
{
// required
gemInfo.m_name = Py_To_String(data["gem_name"]);
gemInfo.m_name = Py_To_String(data["gem_name"]);
// optional
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
if (data.contains("Tags"))
{
@@ -685,7 +686,7 @@ namespace O3DE::ProjectManager
try
{
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName);
projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName);
}
catch ([[maybe_unused]] const std::exception& e)
{
@@ -727,33 +728,33 @@ namespace O3DE::ProjectManager
AZ::Outcome<void, AZStd::string> PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath)
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_enableGemProject.attr("enable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
m_enableGemProject.attr("enable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
);
});
});
}
AZ::Outcome<void, AZStd::string> PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath)
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_disableGemProject.attr("disable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
m_disableGemProject.attr("disable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
);
});
});
}
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
@@ -773,8 +774,8 @@ namespace O3DE::ProjectManager
{
// required
templateInfo.m_displayName = Py_To_String(data["display_name"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
// optional
if (data.contains("canonical_tags"))
@@ -806,7 +807,7 @@ namespace O3DE::ProjectManager
QVector<ProjectTemplateInfo> templates;
bool result = ExecuteWithLock([&] {
for (auto path : m_manifest.attr("get_project_templates")())
for (auto path : m_manifest.attr("get_templates_for_project_creation")())
{
templates.push_back(ProjectTemplateInfoFromPath(path));
}
@@ -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;
@@ -8,7 +8,8 @@
"Name": "IBLSpecular",
"Description": "The input cubemap generates an IBL specular output cubemap.",
"FileMasks": [
"_iblspecularcm"
"_iblspecularcm",
"_iblspecularcm256"
],
"SourceColor": "Linear",
"DestColor": "Linear",
@@ -34,7 +35,8 @@
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
"Name": "IBLSpecular",
"FileMasks": [
"_iblspecularcm"
"_iblspecularcm",
"_iblspecularcm256"
],
"SourceColor": "Linear",
"DestColor": "Linear",
@@ -59,7 +61,8 @@
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
"Name": "IBLSpecular",
"FileMasks": [
"_iblspecularcm"
"_iblspecularcm",
"_iblspecularcm256"
],
"SourceColor": "Linear",
"DestColor": "Linear",
@@ -84,7 +87,8 @@
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
"Name": "IBLSpecular",
"FileMasks": [
"_iblspecularcm"
"_iblspecularcm",
"_iblspecularcm256"
],
"SourceColor": "Linear",
"DestColor": "Linear",
@@ -109,7 +113,8 @@
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
"Name": "IBLSpecular",
"FileMasks": [
"_iblspecularcm"
"_iblspecularcm",
"_iblspecularcm256"
],
"SourceColor": "Linear",
"DestColor": "Linear",
@@ -0,0 +1,135 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
"Name": "IBLSpecularHigh",
"Description": "The input cubemap generates an IBL specular output cubemap.",
"FileMasks": [
"_iblspecularcm512"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 512,
"MaxTextureSize": 512,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"PlatformsPresets": {
"android": {
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
"Name": "IBLSpecularHigh",
"FileMasks": [
"_iblspecularcm512"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 512,
"MaxTextureSize": 512,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"ios": {
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
"Name": "IBLSpecularHigh",
"FileMasks": [
"_iblspecularcm512"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 512,
"MaxTextureSize": 512,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"mac": {
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
"Name": "IBLSpecularHigh",
"FileMasks": [
"_iblspecularcm512"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 512,
"MaxTextureSize": 512,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"provo": {
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
"Name": "IBLSpecularHigh",
"FileMasks": [
"_iblspecularcm512"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 512,
"MaxTextureSize": 512,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
}
}
}
}
@@ -0,0 +1,135 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
"Name": "IBLSpecularLow",
"Description": "The input cubemap generates an IBL specular output cubemap.",
"FileMasks": [
"_iblspecularcm128"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 128,
"MaxTextureSize": 128,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"PlatformsPresets": {
"android": {
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
"Name": "IBLSpecularLow",
"FileMasks": [
"_iblspecularcm128"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 128,
"MaxTextureSize": 128,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"ios": {
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
"Name": "IBLSpecularLow",
"FileMasks": [
"_iblspecularcm128"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 128,
"MaxTextureSize": 128,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"mac": {
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
"Name": "IBLSpecularLow",
"FileMasks": [
"_iblspecularcm128"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 128,
"MaxTextureSize": 128,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"provo": {
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
"Name": "IBLSpecularLow",
"FileMasks": [
"_iblspecularcm128"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 128,
"MaxTextureSize": 128,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
}
}
}
}
@@ -0,0 +1,135 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
"Name": "IBLSpecularVeryHigh",
"Description": "The input cubemap generates an IBL specular output cubemap.",
"FileMasks": [
"_iblspecularcm1024"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 1024,
"MaxTextureSize": 1024,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"PlatformsPresets": {
"android": {
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
"Name": "IBLSpecularVeryHigh",
"FileMasks": [
"_iblspecularcm1024"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 1024,
"MaxTextureSize": 1024,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"ios": {
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
"Name": "IBLSpecularVeryHigh",
"FileMasks": [
"_iblspecularcm1024"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 1024,
"MaxTextureSize": 1024,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"mac": {
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
"Name": "IBLSpecularVeryHigh",
"FileMasks": [
"_iblspecularcm1024"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 1024,
"MaxTextureSize": 1024,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"provo": {
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
"Name": "IBLSpecularVeryHigh",
"FileMasks": [
"_iblspecularcm1024"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 1024,
"MaxTextureSize": 1024,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
}
}
}
}
@@ -0,0 +1,135 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
"Name": "IBLSpecularVeryLow",
"Description": "The input cubemap generates an IBL specular output cubemap.",
"FileMasks": [
"_iblspecularcm64"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 64,
"MaxTextureSize": 64,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"PlatformsPresets": {
"android": {
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
"Name": "IBLSpecularVeryLow",
"FileMasks": [
"_iblspecularcm64"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 64,
"MaxTextureSize": 64,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"ios": {
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
"Name": "IBLSpecularVeryLow",
"FileMasks": [
"_iblspecularcm64"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 64,
"MaxTextureSize": 64,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"mac": {
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
"Name": "IBLSpecularVeryLow",
"FileMasks": [
"_iblspecularcm64"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 64,
"MaxTextureSize": 64,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
},
"provo": {
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
"Name": "IBLSpecularVeryLow",
"FileMasks": [
"_iblspecularcm64"
],
"SourceColor": "Linear",
"DestColor": "Linear",
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
"MinTextureSize": 64,
"MaxTextureSize": 64,
"IsPowerOf2": true,
"CubemapSettings": {
"Filter": "GGX",
"MipAngle": 7.0,
"MipSlope": 2.299999952316284,
"EdgeFixup": -431602080.0,
"SubId": 2000
},
"MipMapSetting": {
"MipGenType": "Box"
}
}
}
}
}
@@ -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();
@@ -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
@@ -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;
@@ -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
@@ -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
@@ -39,6 +39,7 @@ namespace AZ
serializeContext->Class<EditorReflectionProbeComponent, BaseClass>()
->Version(2, ConvertToEditorRenderComponentAdapter<1>)
->Field("useBakedCubemap", &EditorReflectionProbeComponent::m_useBakedCubemap)
->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel)
->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath)
->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset)
;
@@ -67,6 +68,13 @@ namespace AZ
->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_useBakedCubemap, "Use Baked Cubemap", "Selects between a cubemap that captures the environment at location in the scene or a preauthored cubemap")
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorReflectionProbeComponent::OnUseBakedCubemapValidate)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnUseBakedCubemapChanged)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel, "Baked Cubemap Quality", "Resolution of the baked cubemap")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
->EnumAttribute(BakedCubeMapQualityLevel::VeryLow, "Very Low")
->EnumAttribute(BakedCubeMapQualityLevel::Low, "Low")
->EnumAttribute(BakedCubeMapQualityLevel::Medium, "Medium")
->EnumAttribute(BakedCubeMapQualityLevel::High, "High")
->EnumAttribute(BakedCubeMapQualityLevel::VeryHigh, "Very High")
->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath, "Baked Cubemap Path", "Baked Cubemap Path")
->Attribute(AZ::Edit::Attributes::ReadOnly, true)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
@@ -332,6 +340,12 @@ namespace AZ
// clear it to force the generation of a new filename
cubeMapRelativePath.clear();
}
// if the quality level changed we need to generate a new filename
if (m_controller.m_configuration.m_bakedCubeMapQualityLevel != m_bakedCubeMapQualityLevel)
{
cubeMapRelativePath.clear();
}
}
// build a new cubemap path if necessary
@@ -345,7 +359,10 @@ namespace AZ
AZStd::string uuidString;
uuid.ToString(uuidString);
cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + "_iblspecularcm.dds";
// determine the filemask suffix from the cubemap quality level setting
AZStd::string fileSuffix = BakedCubeMapFileSuffixes[aznumeric_cast<uint32_t>(m_bakedCubeMapQualityLevel)];
cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + fileSuffix;
// replace any invalid filename characters
auto invalidCharacters = [](char letter)
@@ -384,6 +401,7 @@ namespace AZ
// save the relative source path in the configuration
AzToolsFramework::ScopedUndoBatch undoBatch("Cubemap path changed.");
m_controller.m_configuration.m_bakedCubeMapRelativePath = cubeMapRelativePath;
m_controller.m_configuration.m_bakedCubeMapQualityLevel = m_bakedCubeMapQualityLevel;
SetDirty();
// update UI cubemap path display
@@ -77,6 +77,7 @@ namespace AZ
// UI settings
// the user can select between a baked cubemap or an authored cubemap asset
bool m_useBakedCubemap = true;
BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium;
AZStd::string m_bakedCubeMapRelativePath;
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
@@ -46,6 +46,7 @@ namespace AZ
->Field("InnerLength", &ReflectionProbeComponentConfig::m_innerLength)
->Field("InnerWidth", &ReflectionProbeComponentConfig::m_innerWidth)
->Field("UseBakedCubemap", &ReflectionProbeComponentConfig::m_useBakedCubemap)
->Field("BakedCubemapQualityLevel", &ReflectionProbeComponentConfig::m_bakedCubeMapQualityLevel)
->Field("BakedCubeMapRelativePath", &ReflectionProbeComponentConfig::m_bakedCubeMapRelativePath)
->Field("BakedCubeMapAsset", &ReflectionProbeComponentConfig::m_bakedCubeMapAsset)
->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset)
@@ -24,6 +24,29 @@ namespace AZ
{
namespace Render
{
enum class BakedCubeMapQualityLevel : uint32_t
{
VeryLow, // 64
Low, // 128
Medium, // 256
High, // 512
VeryHigh, // 1024
Count
};
static const char* BakedCubeMapFileSuffixes[] =
{
"_iblspecularcm64.dds",
"_iblspecularcm128.dds",
"_iblspecularcm256.dds",
"_iblspecularcm512.dds",
"_iblspecularcm1024.dds"
};
static_assert(AZ_ARRAY_SIZE(BakedCubeMapFileSuffixes) == aznumeric_cast<uint32_t>(BakedCubeMapQualityLevel::Count),
"BakedCubeMapFileSuffixes must have the same number of entries as BakedCubeMapQualityLevel");
class ReflectionProbeComponentConfig final
: public AZ::ComponentConfig
{
@@ -43,6 +66,7 @@ namespace AZ
bool m_showVisualization = true;
bool m_useBakedCubemap = true;
BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium;
AZStd::string m_bakedCubeMapRelativePath;
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
@@ -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)));
}
}
@@ -1239,7 +1239,7 @@ namespace GraphCanvas
bool GraphUtils::IsValidModelConnection(const GraphId& graphId, const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint)
{
bool validConnection;
bool validConnection = false;
AZStd::unordered_set< Endpoint > finalSourceEndpoints = RemapEndpointForModel(sourceEndpoint);
AZStd::unordered_set< Endpoint > finalTargetEndpoints = RemapEndpointForModel(targetEndpoint);
@@ -23,10 +23,6 @@
#include <QPainter>
#include <QStaticText>
static const QColor timeMarkerCol = QColor(255, 0, 255);
static const QColor textCol = QColor(0, 0, 0);
static const QColor ltgrayCol = QColor(110, 110, 110);
QColor InterpolateColor(const QColor& c1, const QColor& c2, float fraction)
{
const int r = (c2.red() - c1.red()) * fraction + c1.red();
@@ -48,7 +48,6 @@ static const EUiAnimCurveType DEFAULT_TRACK_TYPE = eUiAnimCurveType_BezierFloat;
// Old serialization values that are no longer
// defined in IUiAnimationSystem.h, but needed for conversion:
static const int OLD_APARAM_USER = 100;
static const int OLD_ACURVE_GOTO = 21;
static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95;
static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96;
@@ -691,7 +691,7 @@ IUiAnimTrack* CUiAnimAzEntityNode::CreateTrackForAzField(const UiAnimParamData&
return nullptr;
}
EUiAnimValue valueType;
EUiAnimValue valueType = eUiAnimValue_Unknown;
switch (numElements)
{
case 2:
@@ -375,7 +375,7 @@ namespace LyShine
}
///////////////////////////////////////////////////////////////////////////////////////////////
void LyShineSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams)
void LyShineSystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams)
{
#if !defined(AZ_MONOLITHIC_BUILD)
// When module is linked dynamically, we must set our gEnv pointer.
@@ -107,7 +107,8 @@ void UiLayoutGridComponent::ApplyLayoutHeight()
AZStd::vector<AZ::EntityId> childEntityIds;
EBUS_EVENT_ID_RESULT(childEntityIds, GetEntityId(), UiElementBus, GetChildEntityIds);
int childIndex = 0;
int columnIndex, rowIndex;
int columnIndex = 0;
int rowIndex = 0;
for (auto child : childEntityIds)
{
// Set the anchors
@@ -627,7 +628,8 @@ AZ::Vector2 UiLayoutGridComponent::GetChildrenBoundingRectSize(const AZ::Vector2
UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
// Calculate number of rows and columns
int numColumns, numRows;
int numColumns = 0;
int numRows = 0;
switch (m_startingDirection)
{
case StartingDirection::HorizontalOrder:
@@ -151,6 +151,8 @@ namespace UiNavigationHelpers
}
UiTransformInterface::Rect parentRect;
parentRect.Set(0.0f, 0.0f, 0.0f, 0.0f);
AZ::Matrix4x4 parentTransformFromViewport;
if (parentElement.IsValid() && !isCurElementDescendantOfParentElement)
{
@@ -60,7 +60,6 @@ static const EAnimCurveType DEFAULT_TRACK_TYPE = eAnimCurveType_BezierFloat;
// Old serialization values that are no longer
// defined in IMovieSystem.h, but needed for conversion:
static const int OLD_APARAM_USER = 100;
static const int OLD_ACURVE_GOTO = 21;
static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95;
static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96;
@@ -281,7 +281,6 @@ void CAnimPostFXNode::SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bL
paramType.Serialize(trackNode, true);
// Don't use APARAM_USER because it could change in newer versions
// CAnimNode::SerializeAnims will then take care of that
static const unsigned int OLD_APARAM_USER = 100;
paramType = static_cast<AnimParamType>(static_cast<int>(paramType.GetType()) + OLD_APARAM_USER);
paramType.Serialize(trackNode, false);
}
@@ -51,11 +51,11 @@ namespace Audio
// To avoid errors, we initialize COM here with the same model.
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const CLSID CLSID_MMDeviceEnumerator_UUID = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator_UUID = __uuidof(IMMDeviceEnumerator);
HRESULT hresult = CoCreateInstance(
CLSID_MMDeviceEnumerator, nullptr,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
CLSID_MMDeviceEnumerator_UUID, nullptr,
CLSCTX_ALL, IID_IMMDeviceEnumerator_UUID,
reinterpret_cast<void**>(&m_enumerator)
);
@@ -133,8 +133,8 @@ namespace Audio
AZ_Assert(m_device != nullptr, "Attempting to start a Microphone session while the device is uninitialized - Windows!\n");
// Get the IAudioClient from the device
const IID IID_IAudioClient = __uuidof(IAudioClient);
HRESULT hresult = m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audioClient));
const IID IID_IAudioClient_UUID = __uuidof(IAudioClient);
HRESULT hresult = m_device->Activate(IID_IAudioClient_UUID, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audioClient));
if (FAILED(hresult))
{
@@ -182,8 +182,8 @@ namespace Audio
}
// Get the IAudioCaptureClient
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
hresult = m_audioClient->GetService(IID_IAudioCaptureClient, reinterpret_cast<void**>(&m_audioCaptureClient));
const IID IID_IAudioCaptureClient_UUID = __uuidof(IAudioCaptureClient);
hresult = m_audioClient->GetService(IID_IAudioCaptureClient_UUID, reinterpret_cast<void**>(&m_audioCaptureClient));
if (FAILED(hresult))
{
@@ -39,6 +39,7 @@ namespace Multiplayer
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
using EntityPreRenderEvent = AZ::Event<float, float>;
//! @class NetBindComponent
//! @brief Component that provides net-binding to a networked entity.
@@ -97,6 +98,7 @@ namespace Multiplayer
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void NotifyPreRender(float deltaTime, float blendFactor);
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
@@ -104,6 +106,7 @@ namespace Multiplayer
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
void AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler);
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
@@ -152,6 +155,7 @@ namespace Multiplayer
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
EntityPreRenderEvent m_entityPreRenderEvent;
AZ::Event<> m_onRemove;
RpcSendEvent::Handler m_handleLocalServerRpcMessageEventHandle;
AZ::Event<>::Handler m_handleMarkedDirty;
@@ -13,6 +13,7 @@
#pragma once
#include <Source/AutoGen/NetworkTransformComponent.AutoComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/Component/TransformBus.h>
namespace Multiplayer
@@ -32,13 +33,22 @@ namespace Multiplayer
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
private:
void OnPreRender(float deltaTime, float blendFactor);
void OnRotationChangedEvent(const AZ::Quaternion& rotation);
void OnTranslationChangedEvent(const AZ::Vector3& translation);
void OnScaleChangedEvent(float scale);
void OnResetCountChangedEvent();
AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity();
AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity();
AZ::Event<AZ::Quaternion>::Handler m_rotationEventHandler;
AZ::Event<AZ::Vector3>::Handler m_translationEventHandler;
AZ::Event<float>::Handler m_scaleEventHandler;
AZ::Event<uint8_t>::Handler m_resetCountEventHandler;
EntityPreRenderEvent::Handler m_entityPreRenderEventHandler;
};
class NetworkTransformComponentController
@@ -45,10 +45,10 @@ namespace Multiplayer
static constexpr uint32_t MaxRecordBits = 2048;
ReplicationRecord() = default;
ReplicationRecord(NetEntityRole netEntityRole);
ReplicationRecord(NetEntityRole remoteNetEntityRole);
void SetNetworkRole(NetEntityRole netEntityRole);
NetEntityRole GetNetworkRole() const;
void SetRemoteNetworkRole(NetEntityRole remoteNetEntityRole);
NetEntityRole GetRemoteNetworkRole() const;
bool AreAllBitsConsumed() const;
void ResetConsumedBits();
@@ -92,6 +92,6 @@ namespace Multiplayer
// Sequence number this ReplicationRecord was sent on
AzNetworking::PacketId m_sentPacketId = AzNetworking::InvalidPacketId;
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;;
NetEntityRole m_remoteNetEntityRole = NetEntityRole::InvalidRole;;
};
}
@@ -175,7 +175,7 @@ void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{
//! {{ PropertyName }} Handler
//! {{ Property.attrib['Description'] }}
//! HandleOn {{ HandleOn }}
virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) = 0;
virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection, [[maybe_unused]] {{ ', [[maybe_unused]] '.join(paramDefines) }}) {}
{% endif %}
{% endmacro %}
{#
@@ -311,7 +311,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop
{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }})
{
constexpr RpcIndex rpcId = static_cast<RpcIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
constexpr Multiplayer::RpcIndex rpcId = static_cast<Multiplayer::RpcIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
{% if Property.attrib['IsReliable']|booleanTrue %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable;
{% else %}
@@ -368,6 +368,31 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo
->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) {
self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
})
->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntity", [](AZ::EntityId id, {{ ', '.join(paramDefines) }}) {
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(id);
if (!entity)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str())
return;
}
{{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>();
if (!networkComponent)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str())
return;
}
{{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController());
if (!controller)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str())
return;
}
controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
})
{% endif %}
{% endcall %}
{% endmacro %}
@@ -25,6 +25,7 @@ namespace Multiplayer
AZ_CVAR(AZ::TimeMs, cl_MaxRewindHistoryMs, AZ::TimeMs{ 2000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of milliseconds to keep for server correction rewind and replay");
#ifndef AZ_RELEASE_BUILD
AZ_CVAR(float, cl_DebugHackTimeMultiplier, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Scalar value used to simulate clock hacking cheats for validating bank time system and anticheat");
AZ_CVAR(bool, cl_EnableDesyncDebugging, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If enabled, debug logs will contain verbose information on detected state desyncs");
#endif
AZ_CVAR(bool, sv_EnableCorrections, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables server corrections on autonomous proxy desyncs");
@@ -214,11 +215,12 @@ namespace Multiplayer
// Send correction
SendClientInputCorrection(GetLastInputId(), correction);
#ifdef _DEBUG
// In debug, show which states caused the correction
#ifndef AZ_RELEASE_BUILD
AZStd::string clientStateString;
AZStd::string serverStateString;
if (cl_EnableDesyncDebugging)
{
// In debug, show which states caused the correction
// Write in client state
AzNetworking::NetworkOutputSerializer clientStateSerializer(clientState.GetBuffer(), clientState.GetSize());
GetNetBindComponent()->SerializeEntityCorrection(clientStateSerializer);
@@ -236,11 +238,13 @@ namespace Multiplayer
GetNetBindComponent()->SerializeEntityCorrection(serverValues);
AZStd::map<AZStd::string, AZStd::pair<AZStd::string, AZStd::string>> mapComparison;
// put the server value in the first part of the pair
for (const auto& pair : serverValues.GetValueMap())
{
mapComparison[pair.first].first = pair.second;
}
// put the client value in the second part of the pair
for (const auto& pair : clientValues.GetValueMap())
{
@@ -266,12 +270,13 @@ namespace Multiplayer
}
}
}
#else
const AZStd::string clientStateString = "available in debug only";
const AZStd::string serverStateString = "available in debug only";
#endif
else
{
clientStateString = "available in debug only";
serverStateString = "available in debug only";
}
AZLOG_ERROR("** Autonomous proxy desync detected! ** clientState=[%s], serverState=[%s]", clientStateString.c_str(), serverStateString.c_str());
#endif
}
}
}
@@ -416,7 +421,7 @@ namespace Multiplayer
ClientInputId LocalPredictionPlayerInputComponentController::GetLastInputId() const
{
return m_clientInputId;
return m_lastClientInputId;
}
HostFrameId LocalPredictionPlayerInputComponentController::GetInputFrameId(const NetworkInput& input) const
@@ -520,10 +525,13 @@ namespace Multiplayer
// In debug, send the entire client output state to the server to make it easier to debug desync issues
AzNetworking::PacketEncodingBuffer processInputResult;
#ifdef _DEBUG
AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity());
GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer);
processInputResult.Resize(processInputResultSerializer.GetSize());
#ifndef AZ_RELEASE_BUILD
if (cl_EnableDesyncDebugging)
{
AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity());
GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer);
processInputResult.Resize(processInputResultSerializer.GetSize());
}
#endif
// Save this input and discard move history outside our client rewind window

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