a0d7048fd4
Entity aliases can be used to have a request to spawn an entity: - spawn the original entity as normal - be disabled - redirected to another entity in another spawnable - also spawn an entity from another spawnable - add the components from an entity in another spawnable An entity alias can indicate whether or not to load the spawnable dependency. If the spawnable dependency is loaded it will be loaded asynchronously because starting blocking loads in an asset handler can lead to deadlocks once there are no more jobs available to deserialize assets. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com>
134 lines
5.0 KiB
C++
134 lines
5.0 KiB
C++
/*
|
|
* 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 <AzCore/Casting/lossy_cast.h>
|
|
#include <AzCore/Serialization/Utils.h>
|
|
#include <AzCore/std/string/string.h>
|
|
#include <AzCore/std/sort.h>
|
|
#include <AzFramework/Spawnable/Spawnable.h>
|
|
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
|
|
#include <AzFramework/Spawnable/SpawnableAssetBus.h>
|
|
|
|
namespace AzFramework
|
|
{
|
|
SpawnableAssetHandler::SpawnableAssetHandler()
|
|
{
|
|
AZ::AssetTypeInfoBus::MultiHandler::BusConnect(AZ::AzTypeInfo<Spawnable>::Uuid());
|
|
}
|
|
|
|
SpawnableAssetHandler::~SpawnableAssetHandler()
|
|
{
|
|
AZ::AssetTypeInfoBus::MultiHandler::BusDisconnect();
|
|
}
|
|
|
|
AZ::Data::AssetPtr SpawnableAssetHandler::CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type)
|
|
{
|
|
AZ_Assert(type == AZ::AzTypeInfo<Spawnable>::Uuid(),
|
|
"Asset handler for Spawnable was given a type that's not a Spawnable: %s", type.ToString<AZStd::string>().c_str());
|
|
return aznew Spawnable(id);
|
|
}
|
|
|
|
void SpawnableAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr)
|
|
{
|
|
delete ptr;
|
|
}
|
|
|
|
void SpawnableAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
|
|
{
|
|
assetTypes.push_back(AZ::AzTypeInfo<Spawnable>::Uuid());
|
|
}
|
|
|
|
auto SpawnableAssetHandler::LoadAssetData(
|
|
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
|
|
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
|
|
const AZ::Data::AssetFilterCB& assetLoadFilterCB) -> LoadResult
|
|
{
|
|
Spawnable* spawnable = asset.GetAs<Spawnable>();
|
|
AZ_Assert(spawnable, "Loaded asset data handed to the SpawnableAssetHandler didn't contain a Spawanble.");
|
|
|
|
AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB);
|
|
if (AZ::Utils::LoadObjectFromStreamInPlace(*stream, *spawnable, nullptr /*SerializeContext*/, filter))
|
|
{
|
|
ResolveEntityAliases(spawnable, asset, stream->GetStreamingDeadline(), stream->GetStreamingPriority(), assetLoadFilterCB);
|
|
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
|
|
}
|
|
else
|
|
{
|
|
AZ_Error("Spawnable", false, "Failed to deserialize asset %s.", asset->GetId().ToString<AZStd::string>().c_str());
|
|
return AZ::Data::AssetHandler::LoadResult::Error;
|
|
}
|
|
}
|
|
|
|
AZ::Data::AssetType SpawnableAssetHandler::GetAssetType() const
|
|
{
|
|
return AZ::AzTypeInfo<Spawnable>::Uuid();
|
|
}
|
|
|
|
const char* SpawnableAssetHandler::GetAssetTypeDisplayName() const
|
|
{
|
|
return "Spawnable";
|
|
}
|
|
|
|
const char* SpawnableAssetHandler::GetGroup() const
|
|
{
|
|
return "Prefab";
|
|
}
|
|
|
|
const char* SpawnableAssetHandler::GetBrowserIcon() const
|
|
{
|
|
return "Icons/Components/Viewport/EntityInSlice.png";
|
|
}
|
|
|
|
void SpawnableAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
|
|
{
|
|
extensions.push_back(Spawnable::FileExtension);
|
|
}
|
|
|
|
uint32_t SpawnableAssetHandler::BuildSubId(AZStd::string_view id)
|
|
{
|
|
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
|
|
return azlossy_caster(subIdHash.GetHash());
|
|
}
|
|
|
|
void SpawnableAssetHandler::ResolveEntityAliases(
|
|
Spawnable* spawnable,
|
|
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
|
|
AZStd::chrono::milliseconds streamingDeadline,
|
|
AZ::IO::IStreamerTypes::Priority streamingPriority,
|
|
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
|
|
{
|
|
Spawnable::EntityAliasVisitor aliases = spawnable->TryGetAliases();
|
|
AZ_Assert(aliases.HasLock(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str());
|
|
if (aliases.HasAliases())
|
|
{
|
|
AZ_Assert(
|
|
AZStd::is_sorted(
|
|
aliases.begin(), aliases.end(),
|
|
[](const Spawnable::EntityAlias& lhs, const Spawnable::EntityAlias& rhs)
|
|
{
|
|
return lhs.HasLowerIndex(rhs);
|
|
}),
|
|
"Spawnable '%s' has an unsorted entity alias list.", asset.GetHint().c_str());
|
|
|
|
SpawnableAssetEventsBus::Broadcast(
|
|
&SpawnableAssetEvents::OnResolveAliases, aliases, spawnable->GetMetaData(), spawnable->GetEntities());
|
|
|
|
aliases.Optimize();
|
|
aliases.ListSpawnablesPendingLoad(
|
|
[&assetLoadFilterCB, streamingDeadline, streamingPriority](AZ::Data::Asset<Spawnable>& assetPendingLoad)
|
|
{
|
|
AZ::Data::AssetLoadParameters loadInfo;
|
|
loadInfo.m_assetLoadFilterCB = assetLoadFilterCB;
|
|
loadInfo.m_deadline = streamingDeadline;
|
|
loadInfo.m_priority = streamingPriority;
|
|
assetPendingLoad.QueueLoad(loadInfo);
|
|
});
|
|
}
|
|
}
|
|
} // namespace AzFramework
|