Merge branch 'development' into memory/overrideshim_removal

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2022-01-20 13:43:12 -08:00
1545 changed files with 24982 additions and 22444 deletions
@@ -129,6 +129,32 @@ namespace Camera
GetFrustumHeight()
};
}
//! Unprojects a position in screen space pixel coordinates to world space.
//! With a depth of zero, the position returned will be on the near clip plane of the camera
//! in world space.
//! @param screenPosition The absolute screen position
//! @param depth The depth offset into the world relative to the near clip plane of the camera
//! @return the position in world space
virtual AZ::Vector3 ScreenToWorld(const AZ::Vector2& screenPosition, float depth) = 0;
//! Unprojects a position in screen space normalized device coordinates to world space.
//! With a depth of zero, the position returned will be on the near clip plane of the camera
//! in world space.
//! @param screenNdcPosition The normalized device coordinates in the range [0,1]
//! @param depth The depth offset into the world relative to the near clip plane of the camera
//! @return the position in world space
virtual AZ::Vector3 ScreenNdcToWorld(const AZ::Vector2& screenNdcPosition, float depth) = 0;
//! Projects a position in world space to screen space for the given camera.
//! @param worldPosition The world position
//! @return The absolute screen position
virtual AZ::Vector2 WorldToScreen(const AZ::Vector3& worldPosition) = 0;
//! Projects a position in world space to screen space normalized device coordinates.
//! @param worldPosition The world position
//! @return The normalized device coordinates in the range [0,1]
virtual AZ::Vector2 WorldToScreenNdc(const AZ::Vector3& worldPosition) = 0;
};
using CameraRequestBus = AZ::EBus<CameraComponentRequests>;
@@ -97,19 +97,38 @@ namespace AzFramework
AZStd::vector<AZStd::string> registeredAssetPaths;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(registeredAssetPaths, &AZ::Data::AssetCatalogRequests::GetRegisteredAssetPaths);
const char* dependencyXmlPattern = "*_dependencies.xml";
constexpr const char* dependencyXmlPattern = "_dependencies.xml";
for (const AZStd::string& assetPath : registeredAssetPaths)
{
if (!AZStd::wildcard_match(dependencyXmlPattern, assetPath.c_str()))
if (assetPath.ends_with(dependencyXmlPattern))
{
continue;
}
if (!m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath))
{
AZ_Error("ExcludeFileComponent", false, "Failed to add assets referenced from %s to the blocked list", assetPath.c_str());
AZ_VerifyError("ExcludeFileComponent", m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath),
"Failed to add assets referenced from %s to the blocked list", assetPath.c_str());
}
}
}
void ExcludeFileComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
// Reload any modified "<name>_dependencies.xml" files
AZ::IO::Path assetPath;
auto GetAssetPath = [&assetId, &assetPath](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
{
assetPath = assetCatalogRequests->GetAssetPathById(assetId);
};
AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(GetAssetPath));
constexpr const char* dependencyXmlPattern = "_dependencies.xml";
if (assetPath.Native().ends_with(dependencyXmlPattern))
{
AZ_VerifyError("ExcludeFileComponent", m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath.Native()),
"Failed to add assets referenced from %s to the blocked list", assetPath.c_str());
}
}
void ExcludeFileComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
OnCatalogAssetChanged(assetId);
}
}
}
@@ -65,6 +65,8 @@ namespace AzFramework
void Deactivate() override;
void OnCatalogLoaded(const char* catalogFile) override;
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
static void Reflect(AZ::ReflectContext* context);
@@ -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;
@@ -8,6 +8,7 @@
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Spawnable/SpawnableMetaData.h>
@@ -61,15 +62,6 @@ namespace AzFramework
m_entitiesManager.ProcessQueue(SpawnableEntitiesManager::CommandQueuePriority::High);
}
void SpawnableSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
if (!m_catalogAvailable)
{
m_catalogAvailable = true;
LoadRootSpawnableFromSettingsRegistry();
}
}
uint64_t SpawnableSystemComponent::AssignRootSpawnable(AZ::Data::Asset<Spawnable> rootSpawnable)
{
uint32_t generation = 0;
@@ -157,20 +149,29 @@ namespace AzFramework
// Register with AssetDatabase
AZ_Assert(AZ::Data::AssetManager::IsReady(), "Spawnables can't be registered because the Asset Manager is not ready yet.");
AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, AZ::AzTypeInfo<Spawnable>::Uuid());
// Register with AssetCatalog
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::EnableCatalogForAsset, AZ::AzTypeInfo<Spawnable>::Uuid());
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::AddExtension, Spawnable::FileExtension);
AssetCatalogEventBus::Handler::BusConnect();
// Register for the CriticalAssetsCompiled lifecycle event to trigger the loading of the root spawnable
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ_Assert(settingsRegistry, "Unable to change root spawnable callback because Settings Registry is not available.");
auto LifecycleCallback = [this](AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
LoadRootSpawnableFromSettingsRegistry();
};
AZ::ComponentApplicationLifecycle::RegisterHandler(*settingsRegistry, m_criticalAssetsHandler,
AZStd::move(LifecycleCallback), "CriticalAssetsCompiled");
RootSpawnableNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
auto registry = AZ::SettingsRegistry::Get();
AZ_Assert(registry, "Unable to change root spawnable callback because Settings Registry is not available.");
m_registryChangeHandler = registry->RegisterNotifier([this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type /*type*/)
m_registryChangeHandler = settingsRegistry->RegisterNotifier([this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type /*type*/)
{
if (path.starts_with(RootSpawnableRegistryKey))
{
@@ -187,13 +188,14 @@ namespace AzFramework
AZ::TickBus::Handler::BusDisconnect();
RootSpawnableNotificationBus::Handler::BusDisconnect();
AssetCatalogEventBus::Handler::BusDisconnect();
// Unregister Lifecycle event handler
m_criticalAssetsHandler = {};
if (m_catalogAvailable)
if (m_rootSpawnableId.IsValid())
{
ReleaseRootSpawnable();
// The SpawnalbleSystemComponent needs to guarantee there's no more processing left to do by the
// The SpawnableSystemComponent needs to guarantee there's no more processing left to do by the
// entity manager before it can safely destroy it on shutdown, but also to make sure that are no
// more calls to the callback registered to the root spawnable as that accesses this component.
m_rootSpawnableContainer.Clear();
@@ -210,8 +212,6 @@ namespace AzFramework
void SpawnableSystemComponent::LoadRootSpawnableFromSettingsRegistry()
{
AZ_Assert(m_catalogAvailable, "Attempting to load root spawnable while the catalog is not available yet.");
auto registry = AZ::SettingsRegistry::Get();
AZ_Assert(registry, "Unable to check for root spawnable because the Settings Registry is not available.");
@@ -12,7 +12,6 @@
#include <AzCore/Component/TickBus.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
@@ -25,7 +24,6 @@ namespace AzFramework
: public AZ::Component
, public AZ::TickBus::Handler
, public AZ::SystemTickBus::Handler
, public AssetCatalogEventBus::Handler
, public RootSpawnableInterface::Registrar
, public RootSpawnableNotificationBus::Handler
{
@@ -63,12 +61,6 @@ namespace AzFramework
void OnSystemTick() override;
//
// AssetCatalogEventBus
//
void OnCatalogLoaded(const char* catalogFile) override;
//
// RootSpawnableInterface
//
@@ -97,6 +89,6 @@ namespace AzFramework
AZ::SettingsRegistryInterface::NotifyEventHandler m_registryChangeHandler;
AZ::Data::AssetId m_rootSpawnableId;
bool m_catalogAvailable{ false };
AZ::SettingsRegistryInterface::NotifyEventHandler m_criticalAssetsHandler;
};
} // namespace AzFramework
@@ -11,12 +11,15 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/span.h>
#include <AzFramework/SurfaceData/SurfaceData.h>
namespace AzFramework
{
namespace Terrain
{
typedef AZStd::function<void(size_t xIndex, size_t yIndex, const SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)> SurfacePointRegionFillCallback;
typedef AZStd::function<void(const SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)> SurfacePointListFillCallback;
//! Shared interface for terrain system implementations
class TerrainDataRequests
@@ -131,6 +134,58 @@ namespace AzFramework
Sampler sampleFilter = Sampler::DEFAULT,
bool* terrainExistsPtr = nullptr) const = 0;
//! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
//! XY coordinate in the list.
virtual void ProcessHeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessNormalsFromList(const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfaceWeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfacePointsFromList(const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessHeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessNormalsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
//! Returns the number of samples for a given region and step size. The first and second
//! elements of the pair correspond to the X and Y sample counts respectively.
virtual AZStd::pair<size_t, size_t> GetNumSamplesFromRegion(const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize) const = 0;
//! Given a region(aabb) and a step size, call the provided callback function with surface data corresponding to the
//! coordinates in the region.
virtual void ProcessHeightsFromRegion(const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessNormalsFromRegion(const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfaceWeightsFromRegion(const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
private:
// Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of
// using an "out" parameter. The "out" parameter is useful for reusing memory allocated in SurfacePoint when
+6 -6
View File
@@ -8,8 +8,8 @@
include(AzFramework/feature_options.cmake)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${O3DE_ENGINE_RESTRICTED_PATH} ${LY_ROOT_FOLDER})
set(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
ly_add_target(
NAME AzFramework STATIC
@@ -44,7 +44,7 @@ ly_add_source_properties(
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Tests/Platform/${PAL_PLATFORM_NAME})
o3de_pal_dir(test_pal_dir ${CMAKE_CURRENT_LIST_DIR}/Tests/Platform/${PAL_PLATFORM_NAME} ${O3DE_ENGINE_RESTRICTED_PATH} ${LY_ROOT_FOLDER})
ly_add_target(
NAME AzFrameworkTestShared STATIC
@@ -86,11 +86,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
NAMESPACE AZ
FILES_CMAKE
Tests/frameworktests_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
${test_pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
${pal_dir}
${test_pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzFramework
@@ -104,7 +104,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
NAME AZ::AzFramework.Tests
)
include(${pal_dir}/platform_specific_test_targets.cmake)
include(${test_pal_dir}/platform_specific_test_targets.cmake)
endif()
@@ -76,5 +76,31 @@ namespace UnitTest
GetSurfacePointFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*));
MOCK_CONST_METHOD5(
GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*));
MOCK_CONST_METHOD3(
ProcessHeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessNormalsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessSurfaceWeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessSurfacePointsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessHeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessNormalsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3(
ProcessSurfacePointsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD2(
GetNumSamplesFromRegion, AZStd::pair<size_t, size_t>(const AZ::Aabb&, const AZ::Vector2&));
MOCK_CONST_METHOD4(
ProcessHeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD4(
ProcessNormalsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD4(
ProcessSurfaceWeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD4(
ProcessSurfacePointsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
};
} // namespace UnitTest
@@ -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) {};