Merge pull request #6468 from aws-lumberyard-dev/Prefabs/ProcessStackAddPrefabBug
Spawnable Entity Alias improvements
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/RTTI/TypeSafeIntegral.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
@@ -164,6 +165,8 @@ namespace AzFramework
|
||||
public:
|
||||
friend class SpawnableEntitiesDefinition;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(AzFramework::EntitySpawnTicket, AZ::SystemAllocator, 0);
|
||||
|
||||
using Id = uint32_t;
|
||||
|
||||
EntitySpawnTicket() = default;
|
||||
|
||||
@@ -499,12 +499,8 @@ namespace AzFramework
|
||||
for (auto it = newEntitiesBegin; it != newEntitiesEnd; ++it)
|
||||
{
|
||||
AZ::Entity* clone = (*it);
|
||||
// The entity component framework doesn't handle entities without TransformComponent safely.
|
||||
if (!clone->GetComponents().empty())
|
||||
{
|
||||
clone->SetSpawnTicketId(request.m_ticketId);
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
|
||||
}
|
||||
clone->SetSpawnTicketId(request.m_ticketId);
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone);
|
||||
}
|
||||
|
||||
// Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context.
|
||||
@@ -636,12 +632,8 @@ namespace AzFramework
|
||||
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
|
||||
{
|
||||
AZ::Entity* clone = (*it);
|
||||
// The entity component framework doesn't handle entities without TransformComponent safely.
|
||||
if (!clone->GetComponents().empty())
|
||||
{
|
||||
clone->SetSpawnTicketId(request.m_ticketId);
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
|
||||
}
|
||||
clone->SetSpawnTicketId(request.m_ticketId);
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
|
||||
}
|
||||
|
||||
if (request.m_completionCallback)
|
||||
@@ -668,7 +660,7 @@ namespace AzFramework
|
||||
{
|
||||
if (entity != nullptr)
|
||||
{
|
||||
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
// Setting it to 0 is needed to avoid the infinite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
entity->SetSpawnTicketId(0);
|
||||
GameEntityContextRequestBus::Broadcast(
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
|
||||
@@ -702,7 +694,7 @@ namespace AzFramework
|
||||
{
|
||||
if (*entityIterator != nullptr && (*entityIterator)->GetId() == request.m_entityId)
|
||||
{
|
||||
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
// Setting it to 0 is needed to avoid the infinite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
(*entityIterator)->SetSpawnTicketId(0);
|
||||
GameEntityContextRequestBus::Broadcast(
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntity, (*entityIterator)->GetId());
|
||||
@@ -949,11 +941,6 @@ namespace AzFramework
|
||||
GameEntityContextRequestBus::Broadcast(
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Entities without components wouldn't have been send to the GameEntityContext.
|
||||
delete entity;
|
||||
}
|
||||
}
|
||||
delete request.m_ticket;
|
||||
|
||||
|
||||
@@ -77,6 +77,12 @@ namespace UnitTest
|
||||
public:
|
||||
AZ_COMPONENT(TargetSpawnableComponent, "{B4041561-63A7-4E1E-80F1-78C08D497960}");
|
||||
|
||||
TargetSpawnableComponent() = default;
|
||||
explicit TargetSpawnableComponent(AZ::EntityId parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Activate() override {}
|
||||
void Deactivate() override {}
|
||||
|
||||
@@ -84,14 +90,19 @@ namespace UnitTest
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
||||
{
|
||||
serializeContext->Class<TargetSpawnableComponent, AZ::Component>();
|
||||
serializeContext->Class<TargetSpawnableComponent, AZ::Component>()
|
||||
->Field("Parent", &TargetSpawnableComponent::m_parent);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::EntityId m_parent;
|
||||
};
|
||||
|
||||
class SpawnableEntitiesManagerTest : public AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
constexpr static AZ::u64 EntityIdStartId = 40;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsFixture::SetUp();
|
||||
@@ -111,7 +122,7 @@ namespace UnitTest
|
||||
m_spawnable = aznew AzFramework::Spawnable(
|
||||
AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready);
|
||||
m_spawnableAsset = new AZ::Data::Asset<AzFramework::Spawnable>(m_spawnable, AZ::Data::AssetLoadBehavior::Default);
|
||||
m_ticket = new AzFramework::EntitySpawnTicket(*m_spawnableAsset);
|
||||
m_ticket = aznew AzFramework::EntitySpawnTicket(*m_spawnableAsset);
|
||||
|
||||
auto managerInterface = AzFramework::SpawnableEntitiesInterface::Get();
|
||||
m_manager = azrtti_cast<AzFramework::SpawnableEntitiesManager*>(managerInterface);
|
||||
@@ -147,22 +158,43 @@ namespace UnitTest
|
||||
{
|
||||
auto entry = AZStd::make_unique<AZ::Entity>();
|
||||
entry->AddComponent(aznew SourceSpawnableComponent());
|
||||
entry->SetId(AZ::EntityId(EntityIdStartId + i));
|
||||
entities.push_back(AZStd::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AzFramework::Spawnable> CreateTargetSpawnable(size_t numElements)
|
||||
AZ::Data::Asset<AzFramework::Spawnable> CreateTargetSpawnable(size_t numElements, bool requiresMatchingEntityIds)
|
||||
{
|
||||
auto target = aznew AzFramework::Spawnable(
|
||||
AZ::Data::AssetId(AZ::Uuid("{716CD8C3-0BA8-4F32-B579-0EC7C967796F}")), AZ::Data::AssetData::AssetStatus::Ready);
|
||||
|
||||
AzFramework::Spawnable::EntityList& entities = target->GetEntities();
|
||||
entities.reserve(numElements);
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
if (requiresMatchingEntityIds)
|
||||
{
|
||||
auto entry = AZStd::make_unique<AZ::Entity>();
|
||||
entry->AddComponent(aznew TargetSpawnableComponent());
|
||||
entities.push_back(AZStd::move(entry));
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
auto entry = AZStd::make_unique<AZ::Entity>();
|
||||
if (i != 0)
|
||||
{
|
||||
entry->AddComponent(aznew TargetSpawnableComponent(AZ::EntityId(EntityIdStartId + i - 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->AddComponent(aznew TargetSpawnableComponent());
|
||||
}
|
||||
entry->SetId(AZ::EntityId(EntityIdStartId + i));
|
||||
entities.push_back(AZStd::move(entry));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
auto entry = AZStd::make_unique<AZ::Entity>();
|
||||
entry->AddComponent(aznew TargetSpawnableComponent());
|
||||
entities.push_back(AZStd::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Data::Asset<AzFramework::Spawnable>(target, AZ::Data::AssetLoadBehavior::NoLoad);
|
||||
@@ -212,6 +244,38 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DoParentEntityIdsMatch(AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
if (entities.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::Entity* previous = nullptr;
|
||||
for (const AZ::Entity* entity : entities)
|
||||
{
|
||||
if (entity)
|
||||
{
|
||||
if (previous)
|
||||
{
|
||||
if (TargetSpawnableComponent* link = entity->FindComponent<TargetSpawnableComponent>(); link != nullptr)
|
||||
{
|
||||
if (link->m_parent != previous->GetId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
previous = entity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsEveryOtherEntityAReplacement(AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
bool onAlternative = true;
|
||||
@@ -516,7 +580,7 @@ namespace UnitTest
|
||||
// Make sure we start with a fresh ticket each time, or else each iteration through this loop would continue to build up
|
||||
// more and more entities.
|
||||
delete m_ticket;
|
||||
m_ticket = new AzFramework::EntitySpawnTicket(*m_spawnableAsset);
|
||||
m_ticket = aznew AzFramework::EntitySpawnTicket(*m_spawnableAsset);
|
||||
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
@@ -599,7 +663,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = true;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace,
|
||||
@@ -608,11 +673,13 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allReplaced = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allReplaced](
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allReplaced, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allReplaced = AreAllEntitiesReplaced(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -621,6 +688,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(4, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allReplaced);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithAdditional_SourceAndTargetComponentsMerged)
|
||||
@@ -628,7 +696,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = false;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional,
|
||||
@@ -637,11 +706,13 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allAdded = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allAdded](
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allAdded, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allAdded = IsEveryOtherEntityAReplacement(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -650,6 +721,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(8, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allAdded);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged)
|
||||
@@ -657,7 +729,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = true;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge,
|
||||
@@ -666,11 +739,13 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allMerged = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allMerged](
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allMerged, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allMerged = AreAllMerged(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -679,6 +754,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(4, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allMerged);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1095,7 +1171,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = true;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace,
|
||||
@@ -1106,11 +1183,13 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allReplaced = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allReplaced](
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allReplaced, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allReplaced = AreAllEntitiesReplaced(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -1119,6 +1198,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(4, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allReplaced);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithAdditional_SourceAndTargetComponentsMerged)
|
||||
@@ -1126,7 +1206,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = false;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional,
|
||||
@@ -1137,12 +1218,14 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allAdded = false;
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback =
|
||||
[&spawnedEntitiesCount, &allAdded](
|
||||
[&spawnedEntitiesCount, &allAdded, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allAdded = IsEveryOtherEntityAReplacement(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -1151,6 +1234,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(8, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allAdded);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged)
|
||||
@@ -1158,7 +1242,8 @@ namespace UnitTest
|
||||
using namespace AzFramework;
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4);
|
||||
constexpr bool requiresMatchingEntityIds = true;
|
||||
AZ::Data::Asset<Spawnable> target = CreateTargetSpawnable(4, requiresMatchingEntityIds);
|
||||
InsertEntityAliases<4>(
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||||
{ Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge,
|
||||
@@ -1169,11 +1254,13 @@ namespace UnitTest
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
bool allMerged = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allMerged](
|
||||
bool allEntityIdsPatched = false;
|
||||
auto callback = [&spawnedEntitiesCount, &allMerged, &allEntityIdsPatched](
|
||||
AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
allMerged = AreAllMerged(entities);
|
||||
allEntityIdsPatched = DoParentEntityIdsMatch(entities);
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
@@ -1182,6 +1269,7 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(4, spawnedEntitiesCount);
|
||||
EXPECT_TRUE(allMerged);
|
||||
EXPECT_TRUE(allEntityIdsPatched);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1302,6 +1390,36 @@ namespace UnitTest
|
||||
// ClaimEntities
|
||||
//
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, ClaimEntities_Call_AllEntitiesWereClaimedAndNotDeleted)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
|
||||
AZStd::vector<AZ::Entity*> claimedEntities;
|
||||
auto callback = [&claimedEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView container)
|
||||
{
|
||||
for (AZ::Entity* entity : container)
|
||||
{
|
||||
claimedEntities.push_back(entity);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->SpawnAllEntities(ticket);
|
||||
m_manager->ClaimEntities(ticket, AZStd::move(callback));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
EXPECT_EQ(NumEntities, claimedEntities.size());
|
||||
|
||||
// If these calls fail it means that the ticket has still deleted the entities, so they weren't properly claimed.
|
||||
for (AZ::Entity* entity : claimedEntities)
|
||||
{
|
||||
delete entity;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, ClaimEntities_DeleteTicketBeforeCall_NoCrash)
|
||||
{
|
||||
auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView) {};
|
||||
|
||||
+2
-2
@@ -415,8 +415,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Construct the runtime entities and products
|
||||
bool readyToCreateRootSpawnable = m_playInEditorData.m_assetsCache.IsActivated();
|
||||
if (!readyToCreateRootSpawnable &&
|
||||
!m_playInEditorData.m_assetsCache.Activate(Prefab::PrefabConversionUtils::PlayInEditor))
|
||||
if (!readyToCreateRootSpawnable && !m_playInEditorData.m_assetsCache.Activate(Prefab::PrefabConversionUtils::PlayInEditor))
|
||||
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key '%.*s'.", AZ_STRING_ARG(Prefab::PrefabConversionUtils::PlayInEditor));
|
||||
return;
|
||||
|
||||
@@ -142,14 +142,14 @@ namespace AzToolsFramework
|
||||
return m_templateSourcePath;
|
||||
}
|
||||
|
||||
void Instance::SetTemplateSourcePath(AZ::IO::PathView sourcePath)
|
||||
void Instance::SetTemplateSourcePath(AZ::IO::Path sourcePath)
|
||||
{
|
||||
m_templateSourcePath = sourcePath;
|
||||
m_templateSourcePath = AZStd::move(sourcePath);
|
||||
}
|
||||
|
||||
void Instance::SetContainerEntityName(AZStd::string_view containerName)
|
||||
void Instance::SetContainerEntityName(AZStd::string containerName)
|
||||
{
|
||||
m_containerEntity->SetName(containerName);
|
||||
m_containerEntity->SetName(AZStd::move(containerName));
|
||||
}
|
||||
|
||||
bool Instance::AddEntity(AZ::Entity& entity)
|
||||
@@ -608,6 +608,31 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
AZ::EntityId Instance::GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const
|
||||
{
|
||||
return GetInstanceAndEntityIdFromAliasPath(relativeAliasPath).second;
|
||||
}
|
||||
|
||||
AZStd::pair<Instance*, AZ::EntityId> Instance::GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath)
|
||||
{
|
||||
Instance* instance = this;
|
||||
AliasPathView path = relativeAliasPath.ParentPath();
|
||||
for (auto it : path)
|
||||
{
|
||||
InstanceOptionalReference child = instance->FindNestedInstance(it.Native());
|
||||
if (child.has_value())
|
||||
{
|
||||
instance = &(child->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZStd::pair<Instance*, AZ::EntityId>(nullptr, AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::pair<Instance*, AZ::EntityId>(instance, instance->GetEntityId(relativeAliasPath.Filename().Native()));
|
||||
}
|
||||
|
||||
AZStd::pair<const Instance*, AZ::EntityId> Instance::GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath) const
|
||||
{
|
||||
const Instance* instance = this;
|
||||
AliasPathView path = relativeAliasPath.ParentPath();
|
||||
@@ -620,11 +645,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::EntityId();
|
||||
return AZStd::pair<const Instance*, AZ::EntityId>(nullptr, AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
return instance->GetEntityId(relativeAliasPath.Filename().Native());
|
||||
return AZStd::pair<const Instance*, AZ::EntityId>(instance, instance->GetEntityId(relativeAliasPath.Filename().Native()));
|
||||
}
|
||||
|
||||
AZStd::vector<InstanceAlias> Instance::GetNestedInstanceAliases(TemplateId templateId) const
|
||||
|
||||
@@ -80,8 +80,8 @@ namespace AzToolsFramework
|
||||
void SetTemplateId(TemplateId templateId);
|
||||
|
||||
const AZ::IO::Path& GetTemplateSourcePath() const;
|
||||
void SetTemplateSourcePath(AZ::IO::PathView sourcePath);
|
||||
void SetContainerEntityName(AZStd::string_view containerName);
|
||||
void SetTemplateSourcePath(AZ::IO::Path sourcePath);
|
||||
void SetContainerEntityName(AZStd::string containerName);
|
||||
|
||||
bool AddEntity(AZ::Entity& entity);
|
||||
bool AddEntity(AZStd::unique_ptr<AZ::Entity>&& entity);
|
||||
@@ -169,6 +169,13 @@ namespace AzToolsFramework
|
||||
* @return entityId, invalid ID if not found
|
||||
*/
|
||||
AZ::EntityId GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const;
|
||||
/**
|
||||
* Retrieves the instance pointer and entity id from an alias path that's relative to this instance.
|
||||
*
|
||||
* @return A pair with the Instance and entity id. The Instance is set to null and entityId is set to invalid if not found.
|
||||
*/
|
||||
AZStd::pair<Instance*, AZ::EntityId> GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath);
|
||||
AZStd::pair<const Instance*, AZ::EntityId> GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath) const;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+9
-38
@@ -37,13 +37,13 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
|
||||
prefabProcessorContext.ListPrefabs(
|
||||
[this, &serializeContext, &prefabProcessorContext]([[maybe_unused]] AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
[this, &serializeContext, &prefabProcessorContext](PrefabDocument& prefab)
|
||||
{
|
||||
auto result = RemoveEditorInfo(prefab, serializeContext, prefabProcessorContext);
|
||||
if (!result)
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false, "Converting to runtime Prefab '%.*s' failed, Error: %s .", AZ_STRING_ARG(prefabName),
|
||||
"Prefab", false, "Converting to runtime Prefab '%s' failed, Error: %s .", prefab.GetName().c_str(),
|
||||
result.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
@@ -58,10 +58,9 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInfoRemover::GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities)
|
||||
void EditorInfoRemover::GetEntitiesFromInstance(AzToolsFramework::Prefab::Instance& instance, EntityList& hierarchyEntities)
|
||||
{
|
||||
instance->GetAllEntitiesInHierarchy(
|
||||
instance.GetAllEntitiesInHierarchy(
|
||||
[&hierarchyEntities](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
hierarchyEntities.emplace_back(entity.get());
|
||||
@@ -498,7 +497,7 @@ exportComponent, prefabProcessorContext);
|
||||
}
|
||||
|
||||
EditorInfoRemover::RemoveEditorInfoResult EditorInfoRemover::RemoveEditorInfo(
|
||||
PrefabDom& prefab,
|
||||
PrefabDocument& prefab,
|
||||
AZ::SerializeContext* serializeContext,
|
||||
PrefabProcessorContext& prefabProcessorContext)
|
||||
{
|
||||
@@ -510,28 +509,10 @@ exportComponent, prefabProcessorContext);
|
||||
|
||||
m_componentRequirementsValidator.SetPlatformTags(prefabProcessorContext.GetPlatformTags());
|
||||
|
||||
// convert Prefab DOM into Prefab Instance.
|
||||
AZStd::unique_ptr<Instance> instance(aznew Instance());
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefab,
|
||||
Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId))
|
||||
{
|
||||
PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
||||
|
||||
AZStd::string errorMessage("Failed to Load Prefab Instance from given Prefab Dom during Removal of Editor Info.");
|
||||
if (sourceReference.has_value() &&
|
||||
sourceReference->get().IsString() &&
|
||||
sourceReference->get().GetStringLength() != 0)
|
||||
{
|
||||
AZStd::string_view source(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
|
||||
errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source));
|
||||
}
|
||||
|
||||
return AZ::Failure(errorMessage);
|
||||
}
|
||||
|
||||
// grab all nested entities from the Instance as source entities.
|
||||
Instance& sourceInstance = prefab.GetInstance();
|
||||
EntityList sourceEntities;
|
||||
GetEntitiesFromInstance(instance, sourceEntities);
|
||||
GetEntitiesFromInstance(sourceInstance, sourceEntities);
|
||||
|
||||
EntityList exportEntities;
|
||||
|
||||
@@ -597,7 +578,7 @@ exportComponent, prefabProcessorContext);
|
||||
exportEntitiesMap.emplace(entity->GetId(), entity);
|
||||
}
|
||||
);
|
||||
instance->RemoveNestedEntities(
|
||||
sourceInstance.RemoveNestedEntities(
|
||||
[&exportEntitiesMap](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
return exportEntitiesMap.find(entity->GetId()) == exportEntitiesMap.end();
|
||||
@@ -605,7 +586,7 @@ exportComponent, prefabProcessorContext);
|
||||
);
|
||||
|
||||
// replace entities of instance with exported ones.
|
||||
instance->GetAllEntitiesInHierarchy(
|
||||
sourceInstance.GetAllEntitiesInHierarchy(
|
||||
[&exportEntitiesMap](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
auto entityId = entity->GetId();
|
||||
@@ -614,16 +595,6 @@ exportComponent, prefabProcessorContext);
|
||||
}
|
||||
);
|
||||
|
||||
// save the final result in the target Prefab DOM.
|
||||
PrefabDom filteredPrefab;
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Saving exported Prefab Instance within a Prefab Dom failed.")
|
||||
);
|
||||
}
|
||||
prefab.Swap(filteredPrefab);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+2
-3
@@ -43,7 +43,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
using RemoveEditorInfoResult = AZ::Outcome<void, AZStd::string>;
|
||||
RemoveEditorInfoResult RemoveEditorInfo(
|
||||
PrefabDom& prefab,
|
||||
PrefabDocument& prefab,
|
||||
AZ::SerializeContext* serializeContext,
|
||||
PrefabProcessorContext& prefabProcessorContext);
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
protected:
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
static void GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities);
|
||||
static void GetEntitiesFromInstance(AzToolsFramework::Prefab::Instance& instance, EntityList& hierarchyEntities);
|
||||
|
||||
static bool ReadComponentAttribute(
|
||||
AZ::Component* component,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
enum class EntityAliasType : uint8_t
|
||||
{
|
||||
Disable, //!< No alias is added.
|
||||
OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned.
|
||||
//!< The original entity will be left in the spawnable and a copy is returned.
|
||||
Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned. The original
|
||||
//!< entity is returned and a blank entity is left.
|
||||
Additional, //!< At runtime the alias entity will be added as an additional but unrelated entity with a new entity id.
|
||||
//!< An empty entity will be returned.
|
||||
Merge //!< At runtime the components in both entities will be merged. An empty entity will be returned. The added
|
||||
//!< components may no conflict with the entities already in the root entity.
|
||||
};
|
||||
|
||||
enum class EntityAliasSpawnableLoadBehavior : uint8_t
|
||||
{
|
||||
NoLoad, //!< Don't load the spawnable referenced in the entity alias. Loading will be up to the caller.
|
||||
QueueLoad, //!< Queue the spawnable referenced in the entity alias for loading. This will be an async load because asset
|
||||
//!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. This option will allow
|
||||
//!< to disable loading the referenced spawnable through the event fired from the spawnables asset handler.
|
||||
DependentLoad //!< The spawnable referenced in the entity alias is made a dependency of the spawnable that holds the entity
|
||||
//!< alias. This will cause the spawnable to be automatically loaded along with the owning spawnable.
|
||||
};
|
||||
|
||||
struct EntityAliasSpawnableLink
|
||||
{
|
||||
EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index);
|
||||
|
||||
AzFramework::Spawnable& m_spawnable;
|
||||
AZ::EntityId m_index;
|
||||
};
|
||||
|
||||
struct EntityAliasPrefabLink
|
||||
{
|
||||
EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias);
|
||||
|
||||
AZStd::string m_prefabName;
|
||||
AzToolsFramework::Prefab::AliasPath m_alias;
|
||||
};
|
||||
|
||||
struct EntityAliasStore
|
||||
{
|
||||
using LinkStore = AZStd::variant<AZStd::monostate, EntityAliasSpawnableLink, EntityAliasPrefabLink>;
|
||||
|
||||
LinkStore m_source;
|
||||
LinkStore m_target;
|
||||
uint32_t m_tag;
|
||||
AzFramework::Spawnable::EntityAliasType m_aliasType;
|
||||
EntityAliasSpawnableLoadBehavior m_loadBehavior;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+3
-3
@@ -112,9 +112,9 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
// Use a random uuid as this is only a temporary source.
|
||||
PrefabConversionUtils::PrefabProcessorContext context(AZ::Uuid::CreateRandom());
|
||||
PrefabDom copy;
|
||||
copy.CopyFrom(templateReference->get().GetPrefabDom(), copy.GetAllocator(), false);
|
||||
context.AddPrefab(spawnableName, AZStd::move(copy));
|
||||
PrefabDocument document(spawnableName);
|
||||
document.SetPrefabDom(templateReference->get().GetPrefabDom());
|
||||
context.AddPrefab(AZStd::move(document));
|
||||
m_converter.ProcessPrefab(context);
|
||||
|
||||
if (!context.HasCompletedSuccessfully() || context.GetProcessedObjects().empty())
|
||||
|
||||
+28
-39
@@ -25,9 +25,9 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
AZ::DataStream::StreamType serializationFormat = m_serializationFormat == SerializationFormats::Binary ?
|
||||
AZ::DataStream::StreamType::ST_BINARY : AZ::DataStream::StreamType::ST_XML;
|
||||
context.ListPrefabs([&context, serializationFormat](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
context.ListPrefabs([&context, serializationFormat](PrefabDocument& prefab)
|
||||
{
|
||||
ProcessPrefab(context, prefabName, prefab, serializationFormat);
|
||||
ProcessPrefab(context, prefab, serializationFormat);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab,
|
||||
void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, PrefabDocument& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab::SpawnableUtils;
|
||||
|
||||
AZStd::string uniqueName = prefabName;
|
||||
AZStd::string uniqueName = prefab.GetName();
|
||||
uniqueName += AzFramework::Spawnable::DotFileExtension;
|
||||
|
||||
auto serializer = [serializationFormat](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool
|
||||
@@ -64,45 +64,34 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AZStd::move(uniqueName), context.GetSourceUuid(), AZStd::move(serializer));
|
||||
AZ_Assert(spawnable, "Failed to create a new spawnable.");
|
||||
|
||||
Instance instance;
|
||||
if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(
|
||||
instance, prefab, object.GetReferencedAssets(),
|
||||
Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is
|
||||
// going to be used to create clones of the entities.
|
||||
{
|
||||
// Resolve entity aliases that store PrefabDOM information to use the spawnable instead. This is done before the entities are
|
||||
// moved from the instance as they'd otherwise can't be found.
|
||||
context.ResolveSpawnableEntityAliases(prefabName, *spawnable, instance);
|
||||
Instance& instance = prefab.GetInstance();
|
||||
// Resolve entity aliases that store PrefabDOM information to use the spawnable instead. This is done before the entities are
|
||||
// moved from the instance as they'd otherwise can't be found.
|
||||
context.ResolveSpawnableEntityAliases(prefab.GetName(), *spawnable, instance);
|
||||
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities();
|
||||
instance.DetachAllEntitiesInHierarchy(
|
||||
[&entities, &context](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities();
|
||||
instance.DetachAllEntitiesInHierarchy(
|
||||
[&entities, &context](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
if (entity)
|
||||
{
|
||||
if (entity)
|
||||
entity->InvalidateDependencies();
|
||||
AZ::Entity::DependencySortOutcome evaluation = entity->EvaluateDependenciesGetDetails();
|
||||
if (evaluation.IsSuccess())
|
||||
{
|
||||
entity->InvalidateDependencies();
|
||||
AZ::Entity::DependencySortOutcome evaluation = entity->EvaluateDependenciesGetDetails();
|
||||
if (evaluation.IsSuccess())
|
||||
{
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s",
|
||||
entity->GetName().c_str(), entity->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str());
|
||||
context.ErrorEncountered();
|
||||
}
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
}
|
||||
});
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s",
|
||||
entity->GetName().c_str(), entity->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str());
|
||||
context.ErrorEncountered();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable);
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefabs", false, "Failed to convert prefab '%.*s' to a spawnable.", AZ_STRING_ARG(prefabName));
|
||||
context.ErrorEncountered();
|
||||
}
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable);
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+1
-2
@@ -40,8 +40,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat);
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, PrefabDocument& prefab, AZ::DataStream::StreamType serializationFormat);
|
||||
|
||||
SerializationFormats m_serializationFormat{ SerializationFormats::Binary };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
PrefabDocument::PrefabDocument(AZStd::string name)
|
||||
: m_name(AZStd::move(name))
|
||||
, m_instance(AZStd::make_unique<AzToolsFramework::Prefab::Instance>())
|
||||
{
|
||||
m_instance->SetTemplateSourcePath(AZ::IO::Path("InMemory") / m_name);
|
||||
}
|
||||
|
||||
bool PrefabDocument::SetPrefabDom(const PrefabDom& prefab)
|
||||
{
|
||||
if (ConstructInstanceFromPrefabDom(prefab))
|
||||
{
|
||||
constexpr bool copyConstStrings = true;
|
||||
m_dom.CopyFrom(prefab, m_dom.GetAllocator(), copyConstStrings);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabDocument::SetPrefabDom(PrefabDom&& prefab)
|
||||
{
|
||||
if (ConstructInstanceFromPrefabDom(prefab))
|
||||
{
|
||||
m_dom = AZStd::move(prefab);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::string& PrefabDocument::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const PrefabDom& PrefabDocument::GetDom() const
|
||||
{
|
||||
if (m_isDirty)
|
||||
{
|
||||
m_isDirty = !PrefabDomUtils::StoreInstanceInPrefabDom(*m_instance, m_dom);
|
||||
}
|
||||
return m_dom;
|
||||
}
|
||||
|
||||
PrefabDom&& PrefabDocument::TakeDom()
|
||||
{
|
||||
if (m_isDirty)
|
||||
{
|
||||
[[maybe_unused]] bool storedSuccessfully = PrefabDomUtils::StoreInstanceInPrefabDom(*m_instance, m_dom);
|
||||
AZ_Assert(storedSuccessfully, "Failed to store Instance '%s' to PrefabDom.", m_name.c_str());
|
||||
m_isDirty = false;
|
||||
}
|
||||
// After the PrefabDom is moved an empty PrefabDom is left behind. This should be reflected in the Instance,
|
||||
// so reset it so it's empty as well.
|
||||
m_instance->Reset();
|
||||
m_instance->SetTemplateSourcePath(AZ::IO::Path("InMemory") / m_name);
|
||||
return AZStd::move(m_dom);
|
||||
}
|
||||
|
||||
void PrefabDocument::ListEntitiesWithComponentType(
|
||||
AZ::TypeId componentType, const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const
|
||||
{
|
||||
m_instance->GetAllEntitiesInHierarchyConst(
|
||||
[this, &componentType, &callback](const AZ::Entity& entity) -> bool
|
||||
{
|
||||
if (entity.FindComponent(componentType))
|
||||
{
|
||||
return callback(m_instance->GetAliasPathRelativeToInstance(entity.GetId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AZ::Entity* PrefabDocument::CreateEntityAlias(
|
||||
PrefabDocument& source,
|
||||
AzToolsFramework::Prefab::AliasPathView entity,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context)
|
||||
{
|
||||
auto&& [sourceInstance, entityId] = source.m_instance->GetInstanceAndEntityIdFromAliasPath(entity);
|
||||
if (sourceInstance != nullptr && entityId.IsValid())
|
||||
{
|
||||
return SpawnableUtils::CreateEntityAlias(
|
||||
source.m_name, *sourceInstance, m_name, *m_instance, entityId, aliasType, loadBehavior, tag, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AzToolsFramework::Prefab::Instance& PrefabDocument::GetInstance()
|
||||
{
|
||||
// Assume that changes will be made to the instance.
|
||||
m_isDirty = true;
|
||||
return *m_instance;
|
||||
}
|
||||
|
||||
const AzToolsFramework::Prefab::Instance& PrefabDocument::GetInstance() const
|
||||
{
|
||||
return *m_instance;
|
||||
}
|
||||
|
||||
bool PrefabDocument::ConstructInstanceFromPrefabDom(const PrefabDom& prefab)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab;
|
||||
|
||||
m_instance->Reset();
|
||||
if (PrefabDomUtils::LoadInstanceFromPrefabDom(*m_instance, prefab, PrefabDomUtils::LoadFlags::AssignRandomEntityId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef AZ_ENABLE_TRACING
|
||||
AZStd::string_view sourceName = m_name;
|
||||
PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
||||
if (sourceReference.has_value() && sourceReference->get().IsString() && sourceReference->get().GetStringLength() != 0)
|
||||
{
|
||||
sourceName = AZStd::string_view(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
|
||||
}
|
||||
AZ_Error(
|
||||
"PrefabDocument", false, "Failed to construct Prefab instance from given PrefabDOM '%.*s'.", AZ_STRING_ARG(sourceName));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabProcessorContext;
|
||||
|
||||
class PrefabDocument final
|
||||
{
|
||||
public:
|
||||
explicit PrefabDocument(AZStd::string name);
|
||||
PrefabDocument(const PrefabDocument&) = delete;
|
||||
PrefabDocument(PrefabDocument&&) = default;
|
||||
|
||||
PrefabDocument& operator=(const PrefabDocument&) = delete;
|
||||
PrefabDocument& operator=(PrefabDocument&&) = default;
|
||||
|
||||
bool SetPrefabDom(const PrefabDom& prefab);
|
||||
bool SetPrefabDom(PrefabDom&& prefab);
|
||||
|
||||
const AZStd::string& GetName() const;
|
||||
const PrefabDom& GetDom() const;
|
||||
PrefabDom&& TakeDom();
|
||||
|
||||
template<typename Component>
|
||||
void ListEntitiesWithComponentType(const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const;
|
||||
void ListEntitiesWithComponentType(
|
||||
AZ::TypeId componentType, const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const;
|
||||
AZ::Entity* CreateEntityAlias(
|
||||
PrefabDocument& source,
|
||||
AzToolsFramework::Prefab::AliasPathView entity,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
|
||||
|
||||
// Where possible, prefer functions directly on the PrefabDocument Instead of using the Instance.
|
||||
AzToolsFramework::Prefab::Instance& GetInstance();
|
||||
const AzToolsFramework::Prefab::Instance& GetInstance() const;
|
||||
|
||||
private:
|
||||
bool ConstructInstanceFromPrefabDom(const PrefabDom& prefab);
|
||||
|
||||
mutable PrefabDom m_dom;
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> m_instance;
|
||||
AZStd::string m_name;
|
||||
mutable bool m_isDirty{ false };
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.inl>
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
template<typename Component>
|
||||
void PrefabDocument::ListEntitiesWithComponentType(const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const
|
||||
{
|
||||
ListEntitiesWithComponentType(azrtti_typeid<Component>(), callback);
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+57
-15
@@ -7,8 +7,10 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Component/EntityUtils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
@@ -30,27 +32,42 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
: m_sourceUuid(sourceUuid)
|
||||
{}
|
||||
|
||||
bool PrefabProcessorContext::AddPrefab(AZStd::string prefabName, PrefabDom prefab)
|
||||
bool PrefabProcessorContext::AddPrefab(PrefabDocument&& document)
|
||||
{
|
||||
auto result = m_prefabs.emplace(AZStd::move(prefabName), AZStd::move(prefab));
|
||||
return result.second;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback)
|
||||
{
|
||||
m_isIterating = true;
|
||||
for (auto& it : m_prefabs)
|
||||
AZStd::string name = document.GetName();
|
||||
if (!m_prefabNames.contains(name))
|
||||
{
|
||||
callback(it.first, it.second);
|
||||
m_prefabNames.emplace(AZStd::move(name));
|
||||
// If currently iterating add to pending queue to avoid invalidating the container that's being iterated over.
|
||||
PrefabContainer& container = m_isIterating ? m_pendingPrefabAdditions : m_prefabs;
|
||||
container.push_back(AZStd::move(document));
|
||||
return true;
|
||||
}
|
||||
m_isIterating = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(PrefabDocument&)>& callback)
|
||||
{
|
||||
for (const auto& it : m_prefabs)
|
||||
// Enable iterating state so the prefab container doesn't get invalided. Enabling this flag will cause new prefabs
|
||||
// to be stored in a temporary buffer that can be moved into the regular prefab container after iterating.
|
||||
m_isIterating = true;
|
||||
for (PrefabDocument& document : m_prefabs)
|
||||
{
|
||||
callback(it.first, it.second);
|
||||
callback(document);
|
||||
}
|
||||
|
||||
m_isIterating = false;
|
||||
m_prefabs.insert(
|
||||
m_prefabs.end(), AZStd::make_move_iterator(m_pendingPrefabAdditions.begin()),
|
||||
AZStd::make_move_iterator(m_pendingPrefabAdditions.end()));
|
||||
m_pendingPrefabAdditions.clear();
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(const PrefabDocument&)>& callback) const
|
||||
{
|
||||
for (const PrefabDocument& document : m_prefabs)
|
||||
{
|
||||
callback(document);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +149,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab;
|
||||
|
||||
// Resolve prefab links into spawnable links for the provided spawnable.
|
||||
for (EntityAliasStore& entityAlias : m_entityAliases)
|
||||
{
|
||||
auto sourcePrefab = AZStd::get_if<EntityAliasPrefabLink>(&entityAlias.m_source);
|
||||
@@ -224,11 +242,35 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
it = aliasVisitors.emplace(source->m_spawnable.GetId(), AZStd::move(visitor)).first;
|
||||
}
|
||||
it->second.AddAlias(
|
||||
AZ::Data::Asset<AzFramework::Spawnable>(&target->m_spawnable, loadBehavior), alias.m_tag, sourceIndex, targetIndex,
|
||||
AZ::Data::Asset<AzFramework::Spawnable>(target->m_spawnable.GetId(), azrtti_typeid<AzFramework::Spawnable>()), alias.m_tag,
|
||||
sourceIndex, targetIndex,
|
||||
alias.m_aliasType, alias.m_loadBehavior == EntityAliasSpawnableLoadBehavior::QueueLoad);
|
||||
|
||||
// Register the dependency between the two spawnables.
|
||||
RegisterProductAssetDependency(source->m_spawnable.GetId(), target->m_spawnable.GetId(), loadBehavior);
|
||||
|
||||
// Patch up all entity ids so the alias points to the same entity id if needed.
|
||||
switch (alias.m_aliasType)
|
||||
{
|
||||
case AzFramework::Spawnable::EntityAliasType::Original:
|
||||
continue;
|
||||
case AzFramework::Spawnable::EntityAliasType::Disable:
|
||||
continue;
|
||||
case AzFramework::Spawnable::EntityAliasType::Replace:
|
||||
break; // Requires entity id for alias in source and target spawnable matches.
|
||||
case AzFramework::Spawnable::EntityAliasType::Additional:
|
||||
continue;
|
||||
case AzFramework::Spawnable::EntityAliasType::Merge:
|
||||
break; // Requires entity id for alias in source and target spawnable matches.
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entityIdMapper = [source, target](const AZ::EntityId& originalId, bool /*isEntityId*/) -> AZ::EntityId
|
||||
{
|
||||
return originalId == target->m_index ? source->m_index : originalId;
|
||||
};
|
||||
AZ::EntityUtils::ReplaceEntityIdsAndEntityRefs(&target->m_spawnable, entityIdMapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-55
@@ -21,60 +21,12 @@
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
enum class EntityAliasType : uint8_t
|
||||
{
|
||||
Disable, //!< No alias is added.
|
||||
OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned.
|
||||
//!< The original entity will be left in the spawnable and a copy is returned.
|
||||
Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned. The original
|
||||
//!< entity is returned and a blank entity is left.
|
||||
Additional, //!< At runtime the alias entity will be added as an additional but unrelated entity with a new entity id.
|
||||
//!< An empty entity will be returned.
|
||||
Merge //!< At runtime the components in both entities will be merged. An empty entity will be returned. The added
|
||||
//!< components may no conflict with the entities already in the root entity.
|
||||
};
|
||||
|
||||
enum class EntityAliasSpawnableLoadBehavior : uint8_t
|
||||
{
|
||||
NoLoad, //!< Don't load the spawnable referenced in the entity alias. Loading will be up to the caller.
|
||||
QueueLoad, //!< Queue the spawnable referenced in the entity alias for loading. This will be an async load because asset
|
||||
//!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. This option will allow
|
||||
//!< to disable loading the referenced spawnable through the event fired from the spawnables asset handler.
|
||||
DependentLoad //!< The spawnable referenced in the entity alias is made a dependency of the spawnable that holds the entity
|
||||
//!< alias. This will cause the spawnable to be automatically loaded along with the owning spawnable.
|
||||
};
|
||||
|
||||
struct EntityAliasSpawnableLink
|
||||
{
|
||||
EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index);
|
||||
|
||||
AzFramework::Spawnable& m_spawnable;
|
||||
AZ::EntityId m_index;
|
||||
};
|
||||
|
||||
struct EntityAliasPrefabLink
|
||||
{
|
||||
EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias);
|
||||
|
||||
AZStd::string m_prefabName;
|
||||
AzToolsFramework::Prefab::AliasPath m_alias;
|
||||
};
|
||||
|
||||
struct EntityAliasStore
|
||||
{
|
||||
using LinkStore = AZStd::variant<AZStd::monostate, EntityAliasSpawnableLink, EntityAliasPrefabLink>;
|
||||
|
||||
LinkStore m_source;
|
||||
LinkStore m_target;
|
||||
uint32_t m_tag;
|
||||
AzFramework::Spawnable::EntityAliasType m_aliasType;
|
||||
EntityAliasSpawnableLoadBehavior m_loadBehavior;
|
||||
};
|
||||
|
||||
struct AssetDependencyInfo
|
||||
{
|
||||
AZ::Data::AssetId m_assetId;
|
||||
@@ -93,9 +45,9 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
explicit PrefabProcessorContext(const AZ::Uuid& sourceUuid);
|
||||
virtual ~PrefabProcessorContext() = default;
|
||||
|
||||
virtual bool AddPrefab(AZStd::string prefabName, PrefabDom prefab);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const;
|
||||
virtual bool AddPrefab(PrefabDocument&& document);
|
||||
virtual void ListPrefabs(const AZStd::function<void(PrefabDocument&)>& callback);
|
||||
virtual void ListPrefabs(const AZStd::function<void(const PrefabDocument&)>& callback) const;
|
||||
virtual bool HasPrefabs() const;
|
||||
|
||||
virtual bool RegisterSpawnableProductAssetDependency(
|
||||
@@ -128,12 +80,15 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
virtual void ErrorEncountered();
|
||||
|
||||
protected:
|
||||
using NamedPrefabContainer = AZStd::unordered_map<AZStd::string, PrefabDom>;
|
||||
using PrefabNames = AZStd::unordered_set<AZStd::string>;
|
||||
using PrefabContainer = AZStd::vector<PrefabDocument>;
|
||||
using SpawnableEntityAliasStore = AZStd::vector<EntityAliasStore>;
|
||||
|
||||
AZ::Data::AssetLoadBehavior ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const;
|
||||
|
||||
NamedPrefabContainer m_prefabs;
|
||||
PrefabContainer m_prefabs;
|
||||
PrefabContainer m_pendingPrefabAdditions;
|
||||
PrefabNames m_prefabNames;
|
||||
SpawnableEntityAliasStore m_entityAliases;
|
||||
ProcessedObjectStoreContainer m_products;
|
||||
ProductAssetDependencyContainer m_registeredProductAssetDependencies;
|
||||
|
||||
+43
-110
@@ -16,10 +16,12 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
@@ -52,14 +54,32 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
return result;
|
||||
}
|
||||
|
||||
const AZ::Entity* FindEntity(AZ::EntityId entityId, const AzToolsFramework::Prefab::Instance& source)
|
||||
{
|
||||
const AZ::Entity* result = nullptr;
|
||||
source.GetConstEntities(
|
||||
[&result, entityId](const AZ::Entity& entity)
|
||||
{
|
||||
if (entity.GetId() != entityId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = &entity;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
AZ::Entity* FindEntity(AZ::EntityId entityId, AzFramework::Spawnable& source)
|
||||
{
|
||||
uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source);
|
||||
return index != InvalidEntityIndex ? source.GetEntities()[index].get() : nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
AZStd::unique_ptr<AZ::Entity> CloneEntity(AZ::EntityId entityId, T& source)
|
||||
AZStd::unique_ptr<AZ::Entity> CloneEntity(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source)
|
||||
{
|
||||
AZ::Entity* target = Internal::FindEntity(entityId, source);
|
||||
AZ_Assert(
|
||||
@@ -74,41 +94,30 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
return clone;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source)
|
||||
AZStd::unique_ptr<AZ::Entity> ReplaceEntityWithPlaceholder(
|
||||
AZ::EntityId entityId,
|
||||
[[maybe_unused]] AZStd::string_view sourcePrefabName,
|
||||
AzToolsFramework::Prefab::Instance& source)
|
||||
{
|
||||
auto&& [instance, alias] = source.FindInstanceAndAlias(entityId);
|
||||
AZ_Assert(
|
||||
instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%s' for replacing.",
|
||||
aznumeric_cast<AZ::u64>(entityId), source.GetTemplateSourcePath().c_str());
|
||||
instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%.*s' for replacing.",
|
||||
aznumeric_cast<AZ::u64>(entityId), AZ_STRING_ARG(sourcePrefabName));
|
||||
|
||||
EntityOptionalReference entityData = instance->GetEntity(alias);
|
||||
AZ_Assert(
|
||||
entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%s' for replacing.",
|
||||
AZ_STRING_ARG(alias), source.GetTemplateSourcePath().c_str());
|
||||
auto placeholder = AZStd::make_unique<AZ::Entity>(entityData->get().GetId(), entityData->get().GetName());
|
||||
entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%.*s' for replacing.",
|
||||
AZ_STRING_ARG(alias), AZ_STRING_ARG(sourcePrefabName));
|
||||
// A new entity id can be used for the placeholder as `ReplaceEntity` will swap the entity ids.
|
||||
auto placeholder = AZStd::make_unique<AZ::Entity>(AZ::Entity::MakeId(), entityData->get().GetName());
|
||||
return instance->ReplaceEntity(AZStd::move(placeholder), alias);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzFramework::Spawnable& source)
|
||||
{
|
||||
uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source);
|
||||
AZ_Assert(
|
||||
index != InvalidEntityIndex, "SpawnbleUtils were unable to locate entity alias with id %zu in Spawnable for replacing.",
|
||||
aznumeric_cast<AZ::u64>(entityId));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> original = AZStd::move(source.GetEntities()[index]);
|
||||
AZ_Assert(
|
||||
original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.",
|
||||
aznumeric_cast<AZ::u64>(entityId));
|
||||
|
||||
source.GetEntities()[index] = AZStd::make_unique<AZ::Entity>(original->GetId(), original->GetName());
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
AZStd::pair<AZStd::unique_ptr<AZ::Entity>, AzFramework::Spawnable::EntityAliasType> ApplyAlias(
|
||||
Source& source, AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType)
|
||||
AZStd::string_view sourcePrefabName,
|
||||
AzToolsFramework::Prefab::Instance& source,
|
||||
AZ::EntityId entityId,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType)
|
||||
{
|
||||
namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils;
|
||||
using ResultPair = AZStd::pair<AZStd::unique_ptr<AZ::Entity>, AzFramework::Spawnable::EntityAliasType>;
|
||||
@@ -121,12 +130,13 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
case PCU::EntityAliasType::OptionalReplace:
|
||||
return ResultPair(CloneEntity(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace);
|
||||
case PCU::EntityAliasType::Replace:
|
||||
return ResultPair(ReplaceEntityWithPlaceholder(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace);
|
||||
return ResultPair(
|
||||
ReplaceEntityWithPlaceholder(entityId, sourcePrefabName, source),
|
||||
AzFramework::Spawnable::EntityAliasType::Replace);
|
||||
case PCU::EntityAliasType::Additional:
|
||||
ResultPair(AZStd::make_unique<AZ::Entity>(AZ::Entity::MakeId()), AzFramework::Spawnable::EntityAliasType::Additional);
|
||||
return ResultPair(AZStd::make_unique<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Additional);
|
||||
case PCU::EntityAliasType::Merge:
|
||||
// Use the same entity id as the original entity so at runtime the entity ids can be verified to match.
|
||||
ResultPair(AZStd::make_unique<AZ::Entity>(entityId), AzFramework::Spawnable::EntityAliasType::Merge);
|
||||
return ResultPair(AZStd::make_unique<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Merge);
|
||||
default:
|
||||
AZ_Assert(
|
||||
false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast<uint64_t>(aliasType));
|
||||
@@ -182,7 +192,8 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
AliasPath alias = source.GetAliasPathRelativeToInstance(entityId);
|
||||
if (!alias.empty())
|
||||
{
|
||||
auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType);
|
||||
auto&& [replacement, storedAliasType] =
|
||||
Internal::ApplyAlias(sourcePrefabName, source, entityId, aliasType);
|
||||
if (replacement)
|
||||
{
|
||||
AZ::Entity* result = replacement.get();
|
||||
@@ -212,84 +223,6 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Entity* CreateEntityAlias(
|
||||
AZStd::string sourcePrefabName,
|
||||
AzToolsFramework::Prefab::Instance& source,
|
||||
AzFramework::Spawnable& target,
|
||||
AZ::EntityId entityId,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
|
||||
|
||||
AliasPath alias = source.GetAliasPathRelativeToInstance(entityId);
|
||||
if (!alias.empty())
|
||||
{
|
||||
auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType);
|
||||
if (replacement)
|
||||
{
|
||||
AZ::Entity* result = replacement.get();
|
||||
target.GetEntities().push_back(AZStd::move(replacement));
|
||||
|
||||
EntityAliasStore store;
|
||||
store.m_aliasType = storedAliasType;
|
||||
store.m_source.emplace<EntityAliasPrefabLink>(AZStd::move(sourcePrefabName), AZStd::move(alias));
|
||||
store.m_target.emplace<EntityAliasSpawnableLink>(target, result->GetId());
|
||||
store.m_tag = tag;
|
||||
store.m_loadBehavior = loadBehavior;
|
||||
context.RegisterSpawnableEntityAlias(AZStd::move(store));
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast<AZ::u64>(entityId));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast<AZ::u64>(entityId));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Entity* CreateEntityAlias(
|
||||
AzFramework::Spawnable& source,
|
||||
AzFramework::Spawnable& target,
|
||||
AZ::EntityId entityId,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
|
||||
|
||||
auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType);
|
||||
if (replacement)
|
||||
{
|
||||
AZ::Entity* result = replacement.get();
|
||||
target.GetEntities().push_back(AZStd::move(replacement));
|
||||
|
||||
EntityAliasStore store;
|
||||
store.m_aliasType = storedAliasType;
|
||||
store.m_source.emplace<EntityAliasSpawnableLink>(source, entityId);
|
||||
store.m_target.emplace<EntityAliasSpawnableLink>(target, result->GetId());
|
||||
store.m_tag = tag;
|
||||
store.m_loadBehavior = loadBehavior;
|
||||
context.RegisterSpawnableEntityAlias(AZStd::move(store));
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast<AZ::u64>(entityId));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable)
|
||||
{
|
||||
auto begin = spawnable.GetEntities().begin();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -24,6 +24,11 @@ namespace AzToolsFramework::Prefab
|
||||
class Instance;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabProcessorContext;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
static constexpr uint32_t InvalidEntityIndex = AZStd::numeric_limits<uint32_t>::max();
|
||||
@@ -41,24 +46,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
|
||||
AZ::Entity* CreateEntityAlias(
|
||||
AZStd::string sourcePrefabName,
|
||||
AzToolsFramework::Prefab::Instance& source,
|
||||
AzFramework::Spawnable& target,
|
||||
AZ::EntityId entityId,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
|
||||
AZ::Entity* CreateEntityAlias(
|
||||
AzFramework::Spawnable& source,
|
||||
AzFramework::Spawnable& target,
|
||||
AZ::EntityId entityId,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
|
||||
uint32_t tag,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
|
||||
|
||||
|
||||
uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable);
|
||||
|
||||
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable);
|
||||
|
||||
@@ -723,6 +723,7 @@ set(FILES
|
||||
Prefab/Spawnable/EditorOnlyEntityHandler/UiEditorOnlyEntityHandler.cpp
|
||||
Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.h
|
||||
Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.cpp
|
||||
Prefab/Spawnable/EntityAliasTypes.h
|
||||
Prefab/Spawnable/InMemorySpawnableAssetContainer.h
|
||||
Prefab/Spawnable/InMemorySpawnableAssetContainer.cpp
|
||||
Prefab/Spawnable/PrefabCatchmentProcessor.h
|
||||
@@ -730,6 +731,9 @@ set(FILES
|
||||
Prefab/Spawnable/PrefabConversionPipeline.h
|
||||
Prefab/Spawnable/PrefabConversionPipeline.cpp
|
||||
Prefab/Spawnable/PrefabConverterStackProfileNames.h
|
||||
Prefab/Spawnable/PrefabDocument.h
|
||||
Prefab/Spawnable/PrefabDocument.inl
|
||||
Prefab/Spawnable/PrefabDocument.cpp
|
||||
Prefab/Spawnable/ProcesedObjectStore.h
|
||||
Prefab/Spawnable/ProcesedObjectStore.cpp
|
||||
Prefab/Spawnable/PrefabProcessor.h
|
||||
|
||||
+5
-4
@@ -25,7 +25,7 @@ namespace Benchmark
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
m_spawnTicket = new AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
state.ResumeTiming();
|
||||
|
||||
for (uint64_t spwanableCounter = 0; spwanableCounter < spawnAllEntitiesCallCount; spwanableCounter++)
|
||||
@@ -62,7 +62,7 @@ namespace Benchmark
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
m_spawnTicket = new AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
state.ResumeTiming();
|
||||
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*m_spawnTicket);
|
||||
@@ -93,15 +93,16 @@ namespace Benchmark
|
||||
|
||||
SetUpSpawnableAsset(entityCountInSpawnable);
|
||||
|
||||
auto spawner = AzFramework::SpawnableEntitiesInterface::Get();
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
m_spawnTicket = new AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
state.ResumeTiming();
|
||||
|
||||
for (uint64_t spawnCallCounter = 0; spawnCallCounter < spawnCallCount; spawnCallCounter++)
|
||||
{
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*m_spawnTicket);
|
||||
spawner->SpawnAllEntities(*m_spawnTicket);
|
||||
}
|
||||
|
||||
m_rootSpawnableInterface->ProcessSpawnableQueue();
|
||||
|
||||
+5
-5
@@ -9,6 +9,7 @@
|
||||
#include <Prefab/SpawnableRemoveEditorInfoTestFixture.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
@@ -201,15 +202,14 @@ namespace UnitTest
|
||||
{
|
||||
ConvertSourceEntitiesToPrefab();
|
||||
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument prefab("Test");
|
||||
prefab.SetPrefabDom(m_prefabDom);
|
||||
const bool actualResult =
|
||||
m_editorInfoRemover.RemoveEditorInfo(m_prefabDom, m_serializeContext, m_prefabProcessorContext).IsSuccess();
|
||||
m_editorInfoRemover.RemoveEditorInfo(prefab, m_serializeContext, m_prefabProcessorContext).IsSuccess();
|
||||
|
||||
EXPECT_EQ(expectedResult, actualResult);
|
||||
|
||||
AZStd::unique_ptr<Instance> convertedInstance(aznew Instance());
|
||||
ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*convertedInstance, m_prefabDom));
|
||||
|
||||
convertedInstance->DetachAllEntitiesInHierarchy(
|
||||
prefab.GetInstance().DetachAllEntitiesInHierarchy(
|
||||
[this](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
m_runtimeEntities.emplace_back(entity.release());
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor;
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::ProcessedObjectStore;
|
||||
|
||||
void NetworkPrefabProcessor::Process(PrefabProcessorContext& context)
|
||||
void NetworkPrefabProcessor::Process(AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context)
|
||||
{
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument;
|
||||
|
||||
IMultiplayerTools* mpTools = AZ::Interface<IMultiplayerTools>::Get();
|
||||
if (mpTools)
|
||||
{
|
||||
@@ -33,11 +32,17 @@ namespace Multiplayer
|
||||
|
||||
AZ::DataStream::StreamType serializationFormat = GetAzSerializationFormat();
|
||||
|
||||
context.ListPrefabs([&context, serializationFormat](AZStd::string_view prefabName, PrefabDom& prefab) {
|
||||
ProcessPrefab(context, prefabName, prefab, serializationFormat);
|
||||
});
|
||||
bool networkPrefabsAdded = false;
|
||||
context.ListPrefabs(
|
||||
[&networkPrefabsAdded, &context, serializationFormat](PrefabDocument& prefab)
|
||||
{
|
||||
if (ProcessPrefab(context, prefab, serializationFormat))
|
||||
{
|
||||
networkPrefabsAdded = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (mpTools && !context.GetProcessedObjects().empty())
|
||||
if (mpTools && networkPrefabsAdded)
|
||||
{
|
||||
mpTools->SetDidProcessNetworkPrefabs(true);
|
||||
}
|
||||
@@ -59,28 +64,6 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
static AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> LoadInstanceFromPrefab(const PrefabDom& prefab)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab;
|
||||
|
||||
// convert Prefab DOM into Prefab Instance.
|
||||
AZStd::unique_ptr<Instance> sourceInstance(aznew Instance());
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, PrefabDomUtils::LoadFlags::AssignRandomEntityId))
|
||||
{
|
||||
PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
||||
|
||||
AZStd::string errorMessage("NetworkPrefabProcessor: Failed to Load Prefab Instance from given Prefab Dom.");
|
||||
if (sourceReference.has_value() && sourceReference->get().IsString() && sourceReference->get().GetStringLength() != 0)
|
||||
{
|
||||
AZStd::string_view source(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
|
||||
errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source));
|
||||
}
|
||||
AZ_Error("NetworkPrefabProcessor", false, errorMessage.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
return sourceInstance;
|
||||
}
|
||||
|
||||
static void GatherNetEntities(
|
||||
AzToolsFramework::Prefab::Instance* instance,
|
||||
AZStd::unordered_map<AZ::Entity*, AzToolsFramework::Prefab::Instance*>& entityToInstanceMap,
|
||||
@@ -103,18 +86,15 @@ namespace Multiplayer
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat)
|
||||
bool NetworkPrefabProcessor::ProcessPrefab(
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat)
|
||||
{
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::ProcessedObjectStore;
|
||||
using namespace AzToolsFramework::Prefab;
|
||||
|
||||
// convert Prefab DOM into Prefab Instance.
|
||||
AZStd::unique_ptr<Instance> sourceInstance = LoadInstanceFromPrefab(prefab);
|
||||
if (!sourceInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string uniqueName = prefabName;
|
||||
AZStd::string uniqueName = prefab.GetName();
|
||||
uniqueName += ".network.spawnable";
|
||||
|
||||
auto serializer = [serializationFormat](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
|
||||
@@ -127,15 +107,16 @@ namespace Multiplayer
|
||||
ProcessedObjectStore::Create<AzFramework::Spawnable>(uniqueName, context.GetSourceUuid(), AZStd::move(serializer));
|
||||
auto& netSpawnableEntities = networkSpawnable->GetEntities();
|
||||
|
||||
Instance& sourceInstance = prefab.GetInstance();
|
||||
// Grab all net entities with their corresponding Instances to handle nested prefabs correctly
|
||||
AZStd::unordered_map<AZ::Entity*, AzToolsFramework::Prefab::Instance*> netEntityToInstanceMap;
|
||||
AZStd::vector<AZ::Entity*> prefabNetEntities;
|
||||
GatherNetEntities(sourceInstance.get(), netEntityToInstanceMap, prefabNetEntities);
|
||||
GatherNetEntities(&sourceInstance, netEntityToInstanceMap, prefabNetEntities);
|
||||
|
||||
if (prefabNetEntities.empty())
|
||||
{
|
||||
// No networked entities in the prefab, no need to do anything in this processor.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort the entities prior to processing. The entities will end up in the net spawnable in this order.
|
||||
@@ -182,7 +163,7 @@ namespace Multiplayer
|
||||
|
||||
// Add net spawnable asset holder to the prefab root
|
||||
{
|
||||
EntityOptionalReference containerEntityRef = sourceInstance->GetContainerEntity();
|
||||
EntityOptionalReference containerEntityRef = sourceInstance.GetContainerEntity();
|
||||
if (containerEntityRef.has_value())
|
||||
{
|
||||
auto* networkSpawnableHolderComponent = containerEntityRef.value().get().CreateComponent<NetworkSpawnableHolderComponent>();
|
||||
@@ -193,18 +174,12 @@ namespace Multiplayer
|
||||
AZ::Entity* networkSpawnableHolderEntity = aznew AZ::Entity(uniqueName);
|
||||
auto* networkSpawnableHolderComponent = networkSpawnableHolderEntity->CreateComponent<NetworkSpawnableHolderComponent>();
|
||||
networkSpawnableHolderComponent->SetNetworkSpawnableAsset(networkSpawnableAsset);
|
||||
sourceInstance->AddEntity(*networkSpawnableHolderEntity);
|
||||
sourceInstance.AddEntity(*networkSpawnableHolderEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// save the final result in the target Prefab DOM.
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefab))
|
||||
{
|
||||
AZ_Error("NetworkPrefabProcessor", false, "Saving exported Prefab Instance within a Prefab Dom failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::DataStream::StreamType NetworkPrefabProcessor::GetAzSerializationFormat() const
|
||||
|
||||
@@ -14,23 +14,23 @@
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabProcessorContext;
|
||||
class PrefabDocument;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor;
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
|
||||
using AzToolsFramework::Prefab::PrefabDom;
|
||||
|
||||
class NetworkPrefabProcessor : public PrefabProcessor
|
||||
class NetworkPrefabProcessor : public AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(NetworkPrefabProcessor, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(Multiplayer::NetworkPrefabProcessor, "{AF6C36DA-CBB9-4DF4-AE2D-7BC6CCE65176}", PrefabProcessor);
|
||||
AZ_RTTI(
|
||||
Multiplayer::NetworkPrefabProcessor,
|
||||
"{AF6C36DA-CBB9-4DF4-AE2D-7BC6CCE65176}",
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor);
|
||||
|
||||
~NetworkPrefabProcessor() override = default;
|
||||
|
||||
void Process(PrefabProcessorContext& context) override;
|
||||
void Process(AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
@@ -44,7 +44,10 @@ namespace Multiplayer
|
||||
AZ::DataStream::StreamType GetAzSerializationFormat() const;
|
||||
|
||||
protected:
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat);
|
||||
static bool ProcessPrefab(
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context,
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat);
|
||||
|
||||
SerializationFormats m_serializationFormat = SerializationFormats::Binary;
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace UnitTest
|
||||
TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabTwoEntities_NetEntityGoesToNetSpawnable)
|
||||
{
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
|
||||
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument;
|
||||
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
|
||||
@@ -74,7 +75,9 @@ namespace UnitTest
|
||||
// Add the prefab into the Prefab Processor Context
|
||||
const AZStd::string prefabName = "testPrefab";
|
||||
PrefabProcessorContext prefabProcessorContext{AZ::Uuid::CreateRandom()};
|
||||
prefabProcessorContext.AddPrefab(prefabName, AZStd::move(prefabDom));
|
||||
PrefabDocument document(prefabName);
|
||||
ASSERT_TRUE(document.SetPrefabDom(AZStd::move(prefabDom)));
|
||||
prefabProcessorContext.AddPrefab(AZStd::move(document));
|
||||
|
||||
// Request NetworkPrefabProcessor to process the prefab
|
||||
Multiplayer::NetworkPrefabProcessor processor;
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace AZ::Prefab
|
||||
|
||||
bool PrefabBuilderComponent::ProcessPrefab(
|
||||
const AZ::PlatformTagSet& platformTags, const char* filePath, AZ::IO::PathView tempDirPath, const AZ::Uuid& sourceFileUuid,
|
||||
AzToolsFramework::Prefab::PrefabDom& mutableRootDom, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts)
|
||||
AzToolsFramework::Prefab::PrefabDom&& rootDom, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts)
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(sourceFileUuid);
|
||||
AZStd::string rootPrefabName;
|
||||
@@ -247,7 +247,9 @@ namespace AZ::Prefab
|
||||
filePath);
|
||||
return false;
|
||||
}
|
||||
context.AddPrefab(AZStd::move(rootPrefabName), AZStd::move(mutableRootDom));
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument rootDocument(AZStd::move(rootPrefabName));
|
||||
rootDocument.SetPrefabDom(AZStd::move(rootDom));
|
||||
context.AddPrefab(AZStd::move(rootDocument));
|
||||
|
||||
context.SetPlatformTags(AZStd::move(platformTags));
|
||||
|
||||
@@ -319,8 +321,8 @@ namespace AZ::Prefab
|
||||
});
|
||||
|
||||
if (ProcessPrefab(
|
||||
platformTags, request.m_fullPath.c_str(), request.m_tempDirPath.c_str(), request.m_sourceFileUUID, mutableRootDom,
|
||||
response.m_outputProducts))
|
||||
platformTags, request.m_fullPath.c_str(), request.m_tempDirPath.c_str(), request.m_sourceFileUUID,
|
||||
AZStd::move(mutableRootDom), response.m_outputProducts))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace AZ::Prefab
|
||||
const AzToolsFramework::Prefab::PrefabDom& genericDocument);
|
||||
bool ProcessPrefab(
|
||||
const AZ::PlatformTagSet& platformTags, const char* filePath, AZ::IO::PathView tempDirPath, const AZ::Uuid& sourceFileUuid,
|
||||
AzToolsFramework::Prefab::PrefabDom& mutableRootDom,
|
||||
AzToolsFramework::Prefab::PrefabDom&& rootDom,
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -106,7 +106,8 @@ namespace UnitTest
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
prefabDom.CopyFrom(prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()), prefabDom.GetAllocator(), false);
|
||||
|
||||
ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab({AZ::Crc32("pc")}, "parent.prefab", "unused", AZ::Uuid(), prefabDom, jobProducts));
|
||||
ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab(
|
||||
{ AZ::Crc32("pc") }, "parent.prefab", "unused", AZ::Uuid(), AZStd::move(prefabDom), jobProducts));
|
||||
|
||||
ASSERT_EQ(jobProducts.size(), 1);
|
||||
ASSERT_EQ(jobProducts[0].m_dependencies.size(), 1);
|
||||
|
||||
@@ -342,7 +342,7 @@ namespace Vegetation
|
||||
// Create the EntitySpawnTicket here. This pointer is going to get handed off to the vegetation system as opaque instance data,
|
||||
// where it will be tracked and held onto for the lifetime of the vegetation instance. The vegetation system will pass it back
|
||||
// in to DestroyInstance at the end of the lifetime, so that's the one place where we will delete the ticket pointers.
|
||||
AzFramework::EntitySpawnTicket* ticket = new AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
AzFramework::EntitySpawnTicket* ticket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset);
|
||||
if (ticket->IsValid())
|
||||
{
|
||||
// Track the ticket that we've created.
|
||||
|
||||
Reference in New Issue
Block a user