EMFX - Refactor the emfx actor asset loading. (#1101)

Refactor emfx asset loading. The mesh asset, skinMetaAsset and morphTargetMeta asset are part of the dependency.
main
rhongAMZ 5 years ago committed by GitHub
parent 64872e024e
commit fe98c34f50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -94,8 +94,9 @@ namespace CommandSystem
return false; return false;
} }
actor->LoadRemainingAssets(); // Because the actor is directly loaded from disk (without going through an actor asset), we need to ask for a blocking
actor->CheckFinalizeActor(); // load for the asset that actor is depend on.
actor->Finalize(EMotionFX::Actor::LoadRequirement::RequireBlockingLoad);
// set the actor id in case we have specified it as parameter // set the actor id in case we have specified it as parameter
if (actorID != MCORE_INVALIDINDEX32) if (actorID != MCORE_INVALIDINDEX32)

@ -52,7 +52,7 @@ namespace EMotionFX
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext) if (serializeContext)
{ {
serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(1); serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(2);
} }
} }
@ -117,21 +117,6 @@ namespace EMotionFX
ExporterLib::SaveActor(filename, m_actor.get(), MCore::Endian::ENDIAN_LITTLE, GetMeshAssetId(context)); ExporterLib::SaveActor(filename, m_actor.get(), MCore::Endian::ENDIAN_LITTLE, GetMeshAssetId(context));
#ifdef EMOTIONFX_ACTOR_DEBUG
// Use there line to create a log file and inspect detail debug info
AZStd::string folderPath;
AzFramework::StringFunc::Path::GetFolderPath(filename.c_str(), folderPath);
AZStd::string logFilename = folderPath;
logFilename += "EMotionFXExporter_Log.txt";
MCore::GetLogManager().CreateLogFile(logFilename.c_str());
EMotionFX::GetImporter().SetLogDetails(true);
filename += ".xac";
// use this line to load the actor from the saved actor file
EMotionFX::Actor* testLoadingActor = EMotionFX::GetImporter().LoadActor(AZStd::string(filename.c_str()));
MCore::Destroy(testLoadingActor);
#endif // EMOTIONFX_ACTOR_DEBUG
static AZ::Data::AssetType emotionFXActorAssetType("{F67CC648-EA51-464C-9F5D-4A9CE41A7F86}"); // from ActorAsset.h in EMotionFX Gem static AZ::Data::AssetType emotionFXActorAssetType("{F67CC648-EA51-464C-9F5D-4A9CE41A7F86}"); // from ActorAsset.h in EMotionFX Gem
AZ::SceneAPI::Events::ExportProduct& product = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), emotionFXActorAssetType, AZ::SceneAPI::Events::ExportProduct& product = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), emotionFXActorAssetType,
AZStd::nullopt, AZStd::nullopt); AZStd::nullopt, AZStd::nullopt);
@ -141,6 +126,26 @@ namespace EMotionFX
product.m_legacyPathDependencies.emplace_back(AZStd::move(materialPathReference)); product.m_legacyPathDependencies.emplace_back(AZStd::move(materialPathReference));
} }
// Mesh asset, skin meta asset and morph target meta asset are sub assets for actor asset.
// In here we set them as the dependency of the actor asset. That make sure those assets get automatically loaded before actor asset.
// Default to the first product until we are able to establish a link between mesh and actor (ATOM-13590).
const AZ::Data::AssetType assetDependencyList[] = {
azrtti_typeid<AZ::RPI::ModelAsset>(),
azrtti_typeid<AZ::RPI::SkinMetaAsset>(),
azrtti_typeid<AZ::RPI::MorphTargetMetaAsset>()
};
for (const AZ::Data::AssetType& assetDependency : assetDependencyList)
{
AZStd::optional<AZ::SceneAPI::Events::ExportProduct> result = GetFirstProductByType(context, assetDependency);
if (result != AZStd::nullopt)
{
AZ::SceneAPI::Events::ExportProduct exportProduct = result.value();
exportProduct.m_dependencyFlags = AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad);
product.m_productDependencies.emplace_back(exportProduct);
}
}
return SceneEvents::ProcessingResult::Success; return SceneEvents::ProcessingResult::Success;
} }
@ -171,5 +176,20 @@ namespace EMotionFX
return AZStd::nullopt; return AZStd::nullopt;
} }
AZStd::optional<AZ::SceneAPI::Events::ExportProduct> ActorGroupExporter::GetFirstProductByType(
const ActorGroupExportContext& context, AZ::Data::AssetType type)
{
const AZStd::vector<AZ::SceneAPI::Events::ExportProduct>& products = context.m_products.GetProducts();
for (const AZ::SceneAPI::Events::ExportProduct& product : products)
{
if (product.m_assetType == type)
{
return product;
}
}
return AZStd::nullopt;
}
} // namespace Pipeline } // namespace Pipeline
} // namespace EMotionFX } // namespace EMotionFX

@ -14,6 +14,7 @@
#include <AzCore/std/optional.h> #include <AzCore/std/optional.h>
#include <EMotionFX/Source/AutoRegisteredActor.h> #include <EMotionFX/Source/AutoRegisteredActor.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h> #include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <Integration/System/SystemCommon.h> #include <Integration/System/SystemCommon.h>
@ -44,6 +45,8 @@ namespace EMotionFX
//! Get the mesh asset id to which the actor is linked to by default. //! Get the mesh asset id to which the actor is linked to by default.
AZStd::optional<AZ::Data::AssetId> GetMeshAssetId(const ActorGroupExportContext& context) const; AZStd::optional<AZ::Data::AssetId> GetMeshAssetId(const ActorGroupExportContext& context) const;
static AZStd::optional<AZ::SceneAPI::Events::ExportProduct> GetFirstProductByType(
const ActorGroupExportContext& context, AZ::Data::AssetType type);
AutoRegisteredActor m_actor; AutoRegisteredActor m_actor;
AZStd::vector<AZStd::string> m_actorMaterialReferences; AZStd::vector<AZStd::string> m_actorMaterialReferences;

@ -125,7 +125,6 @@ namespace EMotionFX
Actor::~Actor() Actor::~Actor()
{ {
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorDestroyed, this); ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorDestroyed, this);
GetEventManager().OnDeleteActor(this); GetEventManager().OnDeleteActor(this);
@ -1463,50 +1462,60 @@ namespace EMotionFX
return morphTargetMetaAssetInfo.m_assetId.IsValid(); return morphTargetMetaAssetInfo.m_assetId.IsValid();
} }
void Actor::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) void Actor::Finalize(LoadRequirement loadReq)
{ {
if (asset == m_meshAsset) AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_mutex);
// Load the mesh asset, skin meta asset and morph target asset.
// Those sub assets should have already been setup as dependency of actor asset, so they should already be loaded when we reach here.
// Only exception is that when the actor is not loaded by an actor asset, for which we need to do a blocking load.
if (m_meshAssetId.IsValid())
{ {
m_meshAsset = asset; // Get the mesh asset.
} m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(m_meshAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
if (asset == m_skinMetaAsset)
// Get the skin meta asset.
const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId);
if (DoesSkinMetaAssetExist(m_meshAssetId) && skinMetaAssetId.IsValid())
{ {
m_skinMetaAsset = asset; m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::SkinMetaAsset>(
skinMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
} }
if (asset == m_morphTargetMetaAsset)
// Get the morph target meta asset.
const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId);
if (DoesMorphTargetMetaAssetExist(m_meshAssetId) && morphTargetMetaAssetId.IsValid())
{ {
m_morphTargetMetaAsset = asset; m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::MorphTargetMetaAsset>(
morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
} }
CheckFinalizeActor(); if (loadReq == LoadRequirement::RequireBlockingLoad)
{
if (m_skinMetaAsset.IsLoading())
{
m_skinMetaAsset.BlockUntilLoadComplete();
} }
if (m_morphTargetMetaAsset.IsLoading())
void Actor::CheckFinalizeActor()
{ {
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_mutex); m_morphTargetMetaAsset.BlockUntilLoadComplete();
}
if (m_meshAsset.IsReady()) if (m_meshAsset.IsLoading())
{ {
const AZ::Data::AssetId meshAssetId = m_meshAsset.GetId(); m_meshAsset.BlockUntilLoadComplete();
const bool skinMetaAssetExists = DoesSkinMetaAssetExist(meshAssetId); }
const bool morphTargetMetaAssetExists = DoesMorphTargetMetaAssetExist(m_meshAsset.GetId()); }
}
m_skinToSkeletonIndexMap.clear();
// Skin and morph target meta assets are ready, fill the runtime mesh data. if (m_meshAsset.IsReady())
if ((!skinMetaAssetExists || m_skinMetaAsset.IsReady()) &&
(!morphTargetMetaAssetExists || m_morphTargetMetaAsset.IsReady()))
{ {
// Optional, not all actors have a skinned meshes. if (m_skinMetaAsset.IsReady())
if (skinMetaAssetExists)
{ {
m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset); m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset);
} }
ConstructMeshes();
ConstructMeshes(m_skinToSkeletonIndexMap); if (m_morphTargetMetaAsset.IsReady())
// Optional, not all actors have morph targets.
if (morphTargetMetaAssetExists)
{ {
ConstructMorphTargets(); ConstructMorphTargets();
} }
@ -1520,51 +1529,11 @@ namespace EMotionFX
mMorphSetups[i] = nullptr; mMorphSetups[i] = nullptr;
} }
} }
SetActorReady();
// Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances.
//m_meshAsset.Release();
//m_skinMetaAsset.Release();
//m_morphTargetMetaAsset.Release();
}
}
}
void Actor::LoadRemainingAssets()
{
// Everything is ready already or no (skeleton-only) or an invalid mesh asset assigned. Emit ready signal directly.
if (m_isReady || !m_meshAssetId.IsValid())
{
SetActorReady();
return;
} }
LoadMeshAssetsQueued();
}
void Actor::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (asset == m_meshAsset)
{
m_meshAsset = asset;
}
if (asset == m_skinMetaAsset)
{
m_skinMetaAsset = asset;
}
if (asset == m_morphTargetMetaAsset)
{
m_morphTargetMetaAsset = asset;
}
CheckFinalizeActor();
}
void Actor::SetActorReady()
{
m_isReady = true; m_isReady = true;
ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorReady, this); ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorReady, this);
// Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances.
} }
// update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc) // update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc)
@ -2792,37 +2761,6 @@ namespace EMotionFX
m_meshAssetId = assetId; m_meshAssetId = assetId;
} }
void Actor::LoadMeshAssetsQueued()
{
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_mutex);
// Mesh asset will be queue loaded on post init.
if (m_meshAssetId.IsValid())
{
m_isReady = false;
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
AZ::Data::AssetBus::MultiHandler::BusConnect(m_meshAssetId);
m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(m_meshAssetId, AZ::Data::AssetLoadBehavior::Default);
// Skin meta asset
if (DoesSkinMetaAssetExist(m_meshAssetId))
{
const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId);
AZ::Data::AssetBus::MultiHandler::BusConnect(skinMetaAssetId);
m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::SkinMetaAsset>(skinMetaAssetId, AZ::Data::AssetLoadBehavior::Default);
}
// Morph target meta asset
if (DoesMorphTargetMetaAssetExist(m_meshAssetId))
{
const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId);
AZ::Data::AssetBus::MultiHandler::BusConnect(morphTargetMetaAssetId);
m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::MorphTargetMetaAsset>(morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::Default);
}
}
}
Node* Actor::FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const Node* Actor::FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const
{ {
const AZStd::array_view<AZ::RPI::ModelLodAsset::Mesh>& sourceMeshes = lodModelAsset->GetMeshes(); const AZStd::array_view<AZ::RPI::ModelLodAsset::Mesh>& sourceMeshes = lodModelAsset->GetMeshes();
@ -2843,7 +2781,7 @@ namespace EMotionFX
return mSkeleton->GetNode(0); return mSkeleton->GetNode(0);
} }
void Actor::ConstructMeshes(const AZStd::unordered_map<AZ::u16, AZ::u16>& skinToSkeletonIndexMap) void Actor::ConstructMeshes()
{ {
AZ_Assert(m_meshAsset.IsReady(), "Mesh asset should be fully loaded and ready."); AZ_Assert(m_meshAsset.IsReady(), "Mesh asset should be fully loaded and ready.");
@ -2855,7 +2793,8 @@ namespace EMotionFX
SetNumLODLevels(numLODLevels, /*adjustMorphSetup=*/false); SetNumLODLevels(numLODLevels, /*adjustMorphSetup=*/false);
const uint32 numNodes = mSkeleton->GetNumNodes(); const uint32 numNodes = mSkeleton->GetNumNodes();
// Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and GLActor. // Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and
// GLActor.
RemoveAllMaterials(); RemoveAllMaterials();
mMaterials.Resize(numLODLevels); mMaterials.Resize(numLODLevels);
@ -2866,7 +2805,7 @@ namespace EMotionFX
lodLevels[lodLevel].mNodeInfos.Resize(numNodes); lodLevels[lodLevel].mNodeInfos.Resize(numNodes);
// Create a single mesh for the actor. // Create a single mesh for the actor.
Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, skinToSkeletonIndexMap); Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, m_skinToSkeletonIndexMap);
// Find an owning joint for the mesh. // Find an owning joint for the mesh.
Node* meshJoint = FindMeshJoint(lodAsset); Node* meshJoint = FindMeshJoint(lodAsset);
@ -2896,13 +2835,14 @@ namespace EMotionFX
continue; continue;
} }
EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer = static_cast<EMotionFX::SkinningInfoVertexAttributeLayer*>(vertexAttributeLayer); EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer =
static_cast<EMotionFX::SkinningInfoVertexAttributeLayer*>(vertexAttributeLayer);
const AZ::u32 numOrgVerts = skinLayer->GetNumAttributes(); const AZ::u32 numOrgVerts = skinLayer->GetNumAttributes();
AZStd::set<AZ::u32> localJointIndices = skinLayer->CalcLocalJointIndices(numOrgVerts); AZStd::set<AZ::u32> localJointIndices = skinLayer->CalcLocalJointIndices(numOrgVerts);
const AZ::u32 numLocalJoints = static_cast<AZ::u32>(localJointIndices.size()); const AZ::u32 numLocalJoints = static_cast<AZ::u32>(localJointIndices.size());
// The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that anymore. // The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that
// Default to dual quat skinning. // anymore. Default to dual quat skinning.
const bool dualQuatSkinning = true; const bool dualQuatSkinning = true;
if (dualQuatSkinning) if (dualQuatSkinning)
{ {
@ -2970,7 +2910,8 @@ namespace EMotionFX
void Actor::ConstructMorphTargets() void Actor::ConstructMorphTargets()
{ {
AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(), "Mesh as well as morph target meta asset asset should be fully loaded and ready."); AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(),
"Mesh as well as morph target meta asset asset should be fully loaded and ready.");
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels; AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
const AZStd::array_view<AZ::Data::Asset<AZ::RPI::ModelLodAsset>>& lodAssets = m_meshAsset->GetLodAssets(); const AZStd::array_view<AZ::Data::Asset<AZ::RPI::ModelLodAsset>>& lodAssets = m_meshAsset->GetLodAssets();
const size_t numLODLevels = lodAssets.size(); const size_t numLODLevels = lodAssets.size();

@ -63,7 +63,6 @@ namespace EMotionFX
* still share the same data from the Actor class. The Actor contains information about the hierarchy/structure of the characters. * still share the same data from the Actor class. The Actor contains information about the hierarchy/structure of the characters.
*/ */
class EMFX_API Actor class EMFX_API Actor
: private AZ::Data::AssetBus::MultiHandler
{ {
public: public:
AZ_CLASS_ALLOCATOR_DECL AZ_CLASS_ALLOCATOR_DECL
@ -101,6 +100,12 @@ namespace EMotionFX
uint8 mFlags; // bitfield with MIRRORFLAG_ prefix uint8 mFlags; // bitfield with MIRRORFLAG_ prefix
}; };
enum class LoadRequirement : bool
{
RequireBlockingLoad,
AllowAsyncLoad
};
//------------------------------------------------ //------------------------------------------------
/** /**
@ -885,36 +890,35 @@ namespace EMotionFX
bool GetOptimizeSkeleton() const { return m_optimizeSkeleton; } bool GetOptimizeSkeleton() const { return m_optimizeSkeleton; }
void SetMeshAssetId(const AZ::Data::AssetId& assetId); void SetMeshAssetId(const AZ::Data::AssetId& assetId);
void CheckFinalizeActor(); AZ::Data::AssetId GetMeshAssetId() const { return m_meshAssetId; };
void LoadMeshAssetsQueued();
void LoadRemainingAssets();
const AZ::Data::Asset<AZ::RPI::ModelAsset>& GetMeshAsset() const { return m_meshAsset; } const AZ::Data::Asset<AZ::RPI::ModelAsset>& GetMeshAsset() const { return m_meshAsset; }
const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& GetSkinMetaAsset() const { return m_skinMetaAsset; } const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& GetSkinMetaAsset() const { return m_skinMetaAsset; }
const AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset>& GetMorphTargetMetaAsset() const { return m_morphTargetMetaAsset; } const AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset>& GetMorphTargetMetaAsset() const { return m_morphTargetMetaAsset; }
const AZStd::unordered_map<AZ::u16, AZ::u16>& GetSkinToSkeletonIndexMap() const { return m_skinToSkeletonIndexMap; } const AZStd::unordered_map<AZ::u16, AZ::u16>& GetSkinToSkeletonIndexMap() const { return m_skinToSkeletonIndexMap; }
void SetMeshAsset(AZ::Data::Asset<AZ::RPI::ModelAsset> asset) { m_meshAsset = asset; }
void SetSkinMetaAsset(AZ::Data::Asset<AZ::RPI::SkinMetaAsset> asset) { m_skinMetaAsset = asset; }
void SetMorphTargetMetaAsset(AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset> asset) { m_morphTargetMetaAsset = asset; }
/** /**
* Is the actor fully ready? * Is the actor fully ready?
* @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized. * @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized.
**/ **/
bool IsReady() const { return m_isReady; } bool IsReady() const { return m_isReady; }
/**
* Finalize the actor with preload assets (mesh, skinmeta and morph target assets).
* LoadRequirement - We won't need a blocking load if the actor is part of the actor asset, as that will trigger the preload assets
* to load and get ready before finalize has been reached.
* However, if we are calling this on an actor that bypassed the asset system (e.g loading the actor directly from disk), it will require
* a blocking load. This option is now being used because emfx editor does not fully integrate with the asset system.
*/
void Finalize(LoadRequirement loadReq = LoadRequirement::AllowAsyncLoad);
private: private:
void InsertJointAndParents(AZ::u32 jointIndex, AZStd::unordered_set<AZ::u32>& includedJointIndices); void InsertJointAndParents(AZ::u32 jointIndex, AZStd::unordered_set<AZ::u32>& includedJointIndices);
// AZ::Data::AssetBus::Handler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
AZStd::unordered_map<AZ::u16, AZ::u16> ConstructSkinToSkeletonIndexMap(const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& skinMetaAsset); AZStd::unordered_map<AZ::u16, AZ::u16> ConstructSkinToSkeletonIndexMap(const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& skinMetaAsset);
void ConstructMeshes(const AZStd::unordered_map<AZ::u16, AZ::u16>& skinToSkeletonIndexMap); void ConstructMeshes();
void ConstructMorphTargets(); void ConstructMorphTargets();
Node* FindJointByMeshName(const AZStd::string_view meshName) const; Node* FindJointByMeshName(const AZStd::string_view meshName) const;
// per node info (shared between lods) // per node info (shared between lods)
@ -966,9 +970,6 @@ namespace EMotionFX
Node* FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const; Node* FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const;
void SetActorReady();
bool m_isReady = false;
Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */ Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */
MCore::Array<Dependency> mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */ MCore::Array<Dependency> mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */
AZStd::vector<NodeInfo> mNodeInfos; /**< The per node info, shared between lods. */ AZStd::vector<NodeInfo> mNodeInfos; /**< The per node info, shared between lods. */
@ -992,7 +993,7 @@ namespace EMotionFX
bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */ bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */
bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */ bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */
bool m_optimizeSkeleton; /**< Indicates if we should perform/ */ bool m_optimizeSkeleton; /**< Indicates if we should perform/ */
bool m_isReady = false; /**< If actor as well as its dependent files are fully loaded and initialized.*/
#if defined(EMFX_DEVELOPMENT_BUILD) #if defined(EMFX_DEVELOPMENT_BUILD)
bool mIsOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */ bool mIsOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */
#endif // EMFX_DEVELOPMENT_BUILD #endif // EMFX_DEVELOPMENT_BUILD

@ -68,6 +68,9 @@ namespace EMotionFX
&actorSettings, &actorSettings,
""); "");
assetData->m_emfxActor->Finalize();
// Clear out the EMFX raw asset data.
assetData->ReleaseEMotionFXData(); assetData->ReleaseEMotionFXData();
if (!assetData->m_emfxActor) if (!assetData->m_emfxActor)

@ -154,17 +154,15 @@ namespace EMotionFX
Actor* actor = m_configuration.m_actorAsset->GetActor(); Actor* actor = m_configuration.m_actorAsset->GetActor();
if (actor) if (actor)
{ {
OnActorReady(actor); CheckActorCreation();
} }
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
ActorComponent::ActorComponent(const Configuration* configuration) ActorComponent::ActorComponent(const Configuration* configuration)
: m_debugDrawRoot(false) : m_debugDrawRoot(false)
, m_sceneFinishSimHandler([this]( , m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle, float fixedDeltatime)
float fixedDeltatime
)
{ {
if (m_actorInstance) if (m_actorInstance)
{ {
@ -192,18 +190,9 @@ namespace EMotionFX
if (cfg.m_actorAsset.GetId().IsValid()) if (cfg.m_actorAsset.GetId().IsValid())
{ {
EMotionFX::ActorNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect();
EMotionFX::ActorNotificationBus::Handler::BusConnect();
AZ::Data::AssetBus::Handler::BusConnect(cfg.m_actorAsset.GetId()); AZ::Data::AssetBus::Handler::BusConnect(cfg.m_actorAsset.GetId());
cfg.m_actorAsset.QueueLoad(); cfg.m_actorAsset.QueueLoad();
// In case the asset was already loaded fully, create the actor directly.
if (cfg.m_actorAsset.IsReady() &&
cfg.m_actorAsset->GetActor())
{
cfg.m_actorAsset->GetActor()->LoadRemainingAssets();
}
} }
AZ::TickBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect();
@ -231,7 +220,6 @@ namespace EMotionFX
LmbrCentral::AttachmentComponentNotificationBus::Handler::BusDisconnect(); LmbrCentral::AttachmentComponentNotificationBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect();
EMotionFX::ActorNotificationBus::Handler::BusDisconnect();
DestroyActor(); DestroyActor();
m_configuration.m_actorAsset.Release(); m_configuration.m_actorAsset.Release();
@ -314,28 +302,12 @@ namespace EMotionFX
Actor* actor = m_configuration.m_actorAsset->GetActor(); Actor* actor = m_configuration.m_actorAsset->GetActor();
AZ_Assert(m_configuration.m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); AZ_Assert(m_configuration.m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid.");
actor->LoadRemainingAssets(); CheckActorCreation();
actor->CheckFinalizeActor();
} }
void ActorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) void ActorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{ {
DestroyActor(); OnAssetReady(asset);
m_configuration.m_actorAsset = asset;
const Actor* oldActor = m_configuration.m_actorAsset->GetActor();
AZ::Data::Asset<AZ::RPI::ModelAsset> meshAsset = oldActor->GetMeshAsset();
AZ::Data::Asset<AZ::RPI::SkinMetaAsset> skinMetaAsset = oldActor->GetSkinMetaAsset();
AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset> morphTargetMetaAsset = oldActor->GetMorphTargetMetaAsset();
m_configuration.m_actorAsset = asset;
Actor* newActor = m_configuration.m_actorAsset->GetActor();
AZ_Assert(m_configuration.m_actorAsset.IsReady() && newActor, "Actor asset should be loaded and actor valid.");
newActor->SetMeshAsset(meshAsset);
newActor->SetSkinMetaAsset(skinMetaAsset);
newActor->SetMorphTargetMetaAsset(morphTargetMetaAsset);
newActor->CheckFinalizeActor();
} }
bool ActorComponent::IsPhysicsSceneSimulationFinishEventConnected() const bool ActorComponent::IsPhysicsSceneSimulationFinishEventConnected() const
@ -850,13 +822,5 @@ namespace EMotionFX
m_actorInstance->RemoveAttachment(targetActorInstance); m_actorInstance->RemoveAttachment(targetActorInstance);
} }
} }
void ActorComponent::OnActorReady(Actor* actor)
{
if (m_configuration.m_actorAsset && m_configuration.m_actorAsset->GetActor() == actor)
{
CheckActorCreation();
}
}
} // namespace Integration } // namespace Integration
} // namespace EMotionFX } // namespace EMotionFX

@ -44,7 +44,6 @@ namespace EMotionFX
, private LmbrCentral::AttachmentComponentNotificationBus::Handler , private LmbrCentral::AttachmentComponentNotificationBus::Handler
, private AzFramework::CharacterPhysicsDataRequestBus::Handler , private AzFramework::CharacterPhysicsDataRequestBus::Handler
, private AzFramework::RagdollPhysicsNotificationBus::Handler , private AzFramework::RagdollPhysicsNotificationBus::Handler
, private EMotionFX::ActorNotificationBus::Handler
{ {
public: public:
AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}"); AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}");
@ -168,9 +167,6 @@ namespace EMotionFX
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override; int GetTickOrder() override;
// ActorNotificationBus::Handler
void OnActorReady(Actor* actor) override;
void CheckActorCreation(); void CheckActorCreation();
void DestroyActor(); void DestroyActor();
void CheckAttachToEntity(); void CheckAttachToEntity();

@ -156,8 +156,6 @@ namespace EMotionFX
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void EditorActorComponent::Activate() void EditorActorComponent::Activate()
{ {
EMotionFX::ActorNotificationBus::Handler::BusConnect();
LoadActorAsset(); LoadActorAsset();
const AZ::EntityId entityId = GetEntityId(); const AZ::EntityId entityId = GetEntityId();
@ -186,8 +184,6 @@ namespace EMotionFX
AZ::TickBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect();
EMotionFX::ActorNotificationBus::Handler::BusDisconnect();
DestroyActorInstance(); DestroyActorInstance();
m_actorAsset.Release(); m_actorAsset.Release();
} }
@ -234,13 +230,6 @@ namespace EMotionFX
AZ::Data::AssetBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(m_actorAsset.GetId()); AZ::Data::AssetBus::Handler::BusConnect(m_actorAsset.GetId());
m_actorAsset.QueueLoad(); m_actorAsset.QueueLoad();
// In case the asset was already loaded fully, create the actor directly.
if (m_actorAsset.IsReady() &&
m_actorAsset->GetActor())
{
m_actorAsset->GetActor()->LoadRemainingAssets();
}
} }
else else
{ {
@ -475,27 +464,13 @@ namespace EMotionFX
Actor* actor = m_actorAsset->GetActor(); Actor* actor = m_actorAsset->GetActor();
AZ_Assert(m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); AZ_Assert(m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid.");
actor->LoadRemainingAssets(); CheckActorCreation();
actor->CheckFinalizeActor();
} }
void EditorActorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) void EditorActorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{ {
DestroyActorInstance(); DestroyActorInstance();
OnAssetReady(asset);
const Actor* oldActor = m_actorAsset->GetActor();
AZ::Data::Asset<AZ::RPI::ModelAsset> meshAsset = oldActor->GetMeshAsset();
AZ::Data::Asset<AZ::RPI::SkinMetaAsset> skinMetaAsset = oldActor->GetSkinMetaAsset();
AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset> morphTargetMetaAsset = oldActor->GetMorphTargetMetaAsset();
m_actorAsset = asset;
Actor* newActor = m_actorAsset->GetActor();
AZ_Assert(m_actorAsset.IsReady() && newActor, "Actor asset should be loaded and actor valid.");
newActor->SetMeshAsset(meshAsset);
newActor->SetSkinMetaAsset(skinMetaAsset);
newActor->SetMorphTargetMetaAsset(morphTargetMetaAsset);
newActor->CheckFinalizeActor();
} }
void EditorActorComponent::SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset) void EditorActorComponent::SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset)
@ -505,7 +480,7 @@ namespace EMotionFX
Actor* actor = m_actorAsset->GetActor(); Actor* actor = m_actorAsset->GetActor();
if (actor) if (actor)
{ {
OnActorReady(actor); CheckActorCreation();
} }
} }
@ -818,14 +793,6 @@ namespace EMotionFX
return false; return false;
} }
void EditorActorComponent::OnActorReady(Actor* actor)
{
if (m_actorAsset && m_actorAsset->GetActor() == actor)
{
CheckActorCreation();
}
}
void EditorActorComponent::CheckActorCreation() void EditorActorComponent::CheckActorCreation()
{ {
// Enable/disable debug drawing. // Enable/disable debug drawing.

@ -45,7 +45,6 @@ namespace EMotionFX
, private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
, private AzToolsFramework::EditorVisibilityNotificationBus::Handler , private AzToolsFramework::EditorVisibilityNotificationBus::Handler
, public AzFramework::BoundsRequestBus::Handler , public AzFramework::BoundsRequestBus::Handler
, private EMotionFX::ActorNotificationBus::Handler
{ {
public: public:
AZ_EDITOR_COMPONENT(EditorActorComponent, "{A863EE1B-8CFD-4EDD-BA0D-1CEC2879AD44}"); AZ_EDITOR_COMPONENT(EditorActorComponent, "{A863EE1B-8CFD-4EDD-BA0D-1CEC2879AD44}");
@ -142,9 +141,6 @@ namespace EMotionFX
void OnAttached(AZ::EntityId targetId) override; void OnAttached(AZ::EntityId targetId) override;
void OnDetached(AZ::EntityId targetId) override; void OnDetached(AZ::EntityId targetId) override;
// ActorNotificationBus::Handler
void OnActorReady(Actor* actor) override;
void CheckActorCreation(); void CheckActorCreation();
void BuildGameEntity(AZ::Entity* gameEntity) override; void BuildGameEntity(AZ::Entity* gameEntity) override;

Loading…
Cancel
Save