Files
o3de/Gems/EMotionFX/Code/Source/Integration/Assets/MotionSetAsset.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

282 lines
13 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/Asset/AssetManager.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <Integration/Assets/MotionSetAsset.h>
#include <EMotionFX/Source/MotionSet.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/Importer/Importer.h>
namespace EMotionFX
{
namespace Integration
{
AZ_CLASS_ALLOCATOR_IMPL(MotionSetAsset, EMotionFXAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL(MotionSetAssetHandler, EMotionFXAllocator, 0)
/**
* Custom callback registered with EMotion FX for the purpose of intercepting
* motion load requests. We want to pipe all requested loads through our
* asset system.
*/
class CustomMotionSetCallback
: public EMotionFX::MotionSetCallback
{
public:
AZ_CLASS_ALLOCATOR(CustomMotionSetCallback, EMotionFXAllocator, 0);
CustomMotionSetCallback(const AZ::Data::Asset<MotionSetAsset>& asset)
: MotionSetCallback(asset.Get()->m_emfxMotionSet.get())
, m_assetData(asset.Get())
{
}
EMotionFX::Motion* LoadMotion(EMotionFX::MotionSet::MotionEntry* entry) override
{
// When EMotionFX requests a motion to be loaded, retrieve it from the asset database.
// It should already be loaded through a motion set.
const char* motionFile = entry->GetFilename();
AZ::Data::AssetId motionAssetId;
EBUS_EVENT_RESULT(motionAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, motionFile, azrtti_typeid<MotionAsset>(), false);
// if it failed to find it, it might be still compiling - try forcing an immediate compile:
if (!motionAssetId.IsValid())
{
AZ_TracePrintf("EMotionFX", "Motion \"%s\" is missing, requesting the asset system to compile it now.\n", motionFile);
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::CompileAssetSync, motionFile);
// and then try again:
AZ::Data::AssetCatalogRequestBus::BroadcastResult(motionAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, motionFile, azrtti_typeid<MotionAsset>(), false);
if (motionAssetId.IsValid())
{
AZ_TracePrintf("EMotionFX", "Motion \"%s\" successfully compiled.\n", motionFile);
}
}
if (motionAssetId.IsValid())
{
for (const auto& motionAsset : m_assetData->m_motionAssets)
{
if (motionAsset.GetId() == motionAssetId)
{
AZ_Assert(motionAsset, "Motion \"%s\" was found in the asset database, but is not initialized.", entry->GetFilename());
AZ_Error("EMotionFX", motionAsset.Get()->m_emfxMotion.get(), "Motion \"%s\" was found in the asset database, but is not valid.", entry->GetFilename());
return motionAsset.Get()->m_emfxMotion.get();
}
}
}
AZ_Error("EMotionFX", false, "Failed to locate motion \"%s\" in the asset database.", entry->GetFilename());
return nullptr;
}
MotionSetAsset* m_assetData;
};
MotionSetAsset::MotionSetAsset(AZ::Data::AssetId id)
: EMotionFXAsset(id)
{}
MotionSetAsset::~MotionSetAsset()
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
}
void MotionSetAsset::SetData(EMotionFX::MotionSet* motionSet)
{
m_emfxMotionSet.reset(motionSet);
m_status = AZ::Data::AssetData::AssetStatus::Ready;
}
//////////////////////////////////////////////////////////////////////////
void MotionSetAsset::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
for (AZ::Data::Asset<MotionAsset>& motionAsset : m_motionAssets)
{
if (motionAsset.GetId() == asset.GetId())
{
motionAsset = asset;
NotifyMotionSetModified(AZ::Data::Asset<MotionSetAsset>(this, AZ::Data::AssetLoadBehavior::Default));
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
void MotionSetAsset::NotifyMotionSetModified(const AZ::Data::Asset<MotionSetAsset>& asset)
{
// When a dependent motion reloads, consider the motion set reloaded as well.
// This allows characters using this motion set to refresh state and reference the new motions.
if (!asset.Get()->m_isReloadPending)
{
AZStd::function<void()> notifyReload = [asset]()
{
using namespace AZ::Data;
AssetBus::Event(asset.GetId(), &AssetBus::Events::OnAssetReloaded, asset);
asset.Get()->m_isReloadPending = false;
};
AZ::TickBus::QueueFunction(notifyReload);
}
}
//////////////////////////////////////////////////////////////////////////
bool MotionSetAssetHandler::OnInitAsset(const AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
MotionSetAsset* assetData = asset.GetAs<MotionSetAsset>();
EMotionFX::Importer::MotionSetSettings motionSettings;
motionSettings.m_isOwnedByRuntime = true;
assetData->m_emfxMotionSet.reset(EMotionFX::GetImporter().LoadMotionSet(
assetData->m_emfxNativeData.data(),
assetData->m_emfxNativeData.size(),
&motionSettings));
if (!assetData->m_emfxMotionSet)
{
AZ_Error("EMotionFX", false, "Failed to initialize motion set asset %s", asset.GetHint().c_str());
return false;
}
// The following code is required to be set so the FileManager detects changes to the files loaded
// through this method. Once EMotionFX is integrated to the asset system this can go away.
AZStd::string assetFilename;
EBUS_EVENT_RESULT(assetFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, asset.GetId());
const char* devAssetsPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
if (devAssetsPath)
{
AZStd::string assetSourcePath = devAssetsPath;
AZ::StringFunc::AssetDatabasePath::Normalize(assetSourcePath);
AZStd::string filename;
AZ::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), assetFilename.c_str(), filename);
assetData->m_emfxMotionSet->SetFilename(filename.c_str());
}
else
{
if (GetEMotionFX().GetIsInEditorMode())
{
AZ_Warning("EMotionFX", false, "Failed to retrieve asset source path with alias '@devassets@'. Cannot set absolute filename for '%s'", assetFilename.c_str());
}
assetData->m_emfxMotionSet->SetFilename(assetFilename.c_str());
}
// now load them in:
const EMotionFX::MotionSet::MotionEntries& motionEntries = assetData->m_emfxMotionSet->GetMotionEntries();
// Get the motions in the motion set. Escalate them to the top of the build queue first so that they can be done in parallel.
// This call is fire-and-forget and is very lightweight.
for (const auto& item : motionEntries)
{
const EMotionFX::MotionSet::MotionEntry* motionEntry = item.second;
const char* motionFilename = motionEntry->GetFilename();
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, motionFilename);
}
// now that they're all escalated, the asset processor will be processing them across all threads, and we can request them one by one:
for (const auto& item : motionEntries)
{
const EMotionFX::MotionSet::MotionEntry* motionEntry = item.second;
const char* motionFilename = motionEntry->GetFilename();
// Find motion file in catalog and grab the asset.
// Jump on the AssetBus for the asset, and queue load.
AZ::Data::AssetId motionAssetId;
EBUS_EVENT_RESULT(motionAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, motionFilename, AZ::Data::s_invalidAssetType, false);
// if it failed to find it, it might be still compiling - try forcing an immediate compile. CompileAssetSync
// will block until the compilation completes AND the catalog is up to date.
if (!motionAssetId.IsValid())
{
AZ_TracePrintf("EMotionFX", "Motion \"%s\" is missing, requesting the asset system to compile it now.\n", motionFilename);
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::CompileAssetSync, motionFilename);
// and then try again:
AZ::Data::AssetCatalogRequestBus::BroadcastResult(motionAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, motionFilename, azrtti_typeid<MotionAsset>(), false);
if (motionAssetId.IsValid())
{
AZ_TracePrintf("EMotionFX", "Motion \"%s\" successfully compiled.\n", motionFilename);
}
}
if (motionAssetId.IsValid())
{
AZ::Data::Asset<MotionAsset> motionAsset = AZ::Data::AssetManager::Instance().GetAsset<MotionAsset>(motionAssetId, AZ::Data::AssetLoadBehavior::Default);
if (motionAsset)
{
motionAsset.BlockUntilLoadComplete();
assetData->BusConnect(motionAssetId);
assetData->m_motionAssets.push_back(motionAsset);
}
else
{
AZ_Warning("EMotionFX", false, "Motion \"%s\" in motion set \"%s\" could not be loaded.", motionFilename, assetFilename.c_str());
}
}
else
{
AZ_Warning("EMotionFX", false, "Motion \"%s\" in motion set \"%s\" could not be found in the asset catalog.", motionFilename, assetFilename.c_str());
}
}
// Set motion set's motion load callback, so if EMotion FX queries back for a motion,
// we can pull the one managed through an AZ::Asset.
assetData->m_emfxMotionSet->SetCallback(aznew CustomMotionSetCallback(asset));
assetData->ReleaseEMotionFXData();
return true;
}
//////////////////////////////////////////////////////////////////////////
AZ::Data::AssetType MotionSetAssetHandler::GetAssetType() const
{
return azrtti_typeid<MotionSetAsset>();
}
//////////////////////////////////////////////////////////////////////////
void MotionSetAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
{
extensions.push_back("motionset");
}
//////////////////////////////////////////////////////////////////////////
const char* MotionSetAssetHandler::GetAssetTypeDisplayName() const
{
return "EMotion FX Motion Set";
}
//////////////////////////////////////////////////////////////////////////
const char* MotionSetAssetHandler::GetBrowserIcon() const
{
return "Editor/Images/AssetBrowser/MotionSet_16.svg";
}
//////////////////////////////////////////////////////////////////////////
void MotionSetAssetBuilderHandler::InitAsset(const AZ::Data::Asset<AZ::Data::AssetData>& asset, bool loadStageSucceeded, bool isReload)
{
// Don't need to load the referenced motionset and motion assets since we only care about the product ID ot relative path of the product dependency
AZ_UNUSED(asset);
AZ_UNUSED(loadStageSucceeded);
AZ_UNUSED(isReload);
}
AZ::Data::AssetHandler::LoadResult MotionSetAssetBuilderHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
AZ_UNUSED(asset);
AZ_UNUSED(stream);
AZ_UNUSED(assetLoadFilterCB);
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
} // namespace Integration
} // namespace EMotionFX