diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp index 51943536e8..057c0591b0 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp @@ -483,6 +483,9 @@ namespace AZ // tell the caller of this function to write the type id and provide a default object, if requested, for // the specific polymorphic instance the pointer is pointing to. const AZ::Uuid& actualClassId = rtti.GetActualUuid(object); + + // Note: If it is crashing here, it might be that you're serializing a pointer and forgot to initialize it with nullptr. + // Check the elementClassData to identify the causing element. const AZ::Uuid& actualDefaultClassId = rtti.GetActualUuid(defaultObject); if (actualClassId != rtti.GetTypeId()) diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.cpp index 9409f1468d..0cc96d8a2e 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.cpp @@ -24,48 +24,6 @@ namespace CommandSystem { - - AZStd::vector MetaData::GenerateMotionMetaData(EMotionFX::Motion* motion) - { - AZStd::vector commands; - - if (!motion) - { - AZ_Error("EMotionFX", false, "Cannot generate meta data for motion. Motion invalid."); - return commands; - } - - // Save event tracks including motion events. - CommandAdjustMotion* adjustMotionCommand = aznew CommandAdjustMotion(); - adjustMotionCommand->SetMotionExtractionFlags(motion->GetMotionExtractionFlags()); - commands.emplace_back(adjustMotionCommand); - - const size_t eventTrackCount = motion->GetEventTable()->GetNumTracks(); - for (size_t trackIndex = 0; trackIndex < eventTrackCount; ++trackIndex) - { - const EMotionFX::MotionEventTrack* track = motion->GetEventTable()->GetTrack(trackIndex); - - CommandCreateMotionEventTrack* createMotionEventTrackCommand = aznew CommandCreateMotionEventTrack(); - createMotionEventTrackCommand->SetEventTrackName(track->GetName()); - commands.emplace_back(createMotionEventTrackCommand); - - const size_t eventCount = track->GetNumEvents(); - for (size_t eventIndex = 0; eventIndex < eventCount; ++eventIndex) - { - const EMotionFX::MotionEvent& event = track->GetEvent(eventIndex); - CommandCreateMotionEvent* createMotionEventCommand = aznew CommandCreateMotionEvent(); - commands.emplace_back(createMotionEventCommand); - createMotionEventCommand->SetEventTrackName(track->GetName()); - createMotionEventCommand->SetStartTime(event.GetStartTime()); - createMotionEventCommand->SetEndTime(event.GetEndTime()); - createMotionEventCommand->SetEventDatas(event.GetEventDatas()); - } - } - - return commands; - } - - bool MetaData::ApplyMetaDataOnMotion(EMotionFX::Motion* motion, const AZStd::vector& metaDataCommands) { for (MCore::Command* command : metaDataCommands) diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.h index c309bf04be..7fba241110 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.h +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/MetaData.h @@ -30,13 +30,6 @@ namespace CommandSystem class COMMANDSYSTEM_API MetaData { public: - /** - * Constructs a list of commands representing the changes the user did on the source asset and returns it as a string. - * @param motion The motion to read the changes from. - * @result A string containing a list of commands. - */ - static AZStd::vector GenerateMotionMetaData(EMotionFX::Motion* motion); - /** * Use the given list , prepare it for the given motion and apply the meta data. * @param motion The motion to apply the meta data on. diff --git a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/MotionEventExport.cpp b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/MotionEventExport.cpp index db2dcafbdc..78daf024f5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/MotionEventExport.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/MotionEventExport.cpp @@ -5,6 +5,13 @@ * */ +#include +#include +#include +#include +#include +#include +#include #include "Exporter.h" #include #include @@ -28,17 +35,38 @@ namespace ExporterLib return; } - AZ::Outcome serializedMotionEventTable = MCore::ReflectionSerializer::Serialize(motionEventTable); - if (!serializedMotionEventTable.IsSuccess()) + AZ::SerializeContext* context = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + if (!context) + { + AZ_Error("EMotionFX", false, "Can't save motion events. Can't get serialize context from component application."); + return; + } + + AZ::JsonSerializerSettings settings; + settings.m_serializeContext = context; + rapidjson::Document jsonDocument; + auto jsonResult = AZ::JsonSerialization::Store(jsonDocument, jsonDocument.GetAllocator(), *motionEventTable, settings); + if (jsonResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) { + AZ_Error("EMotionFX", false, "JSON serialization failed: %s", jsonResult.ToString("").c_str()); return; } - const size_t serializedTableSizeInBytes = serializedMotionEventTable.GetValue().size(); + + AZStd::string serializedMotionEventTable; + auto writeToStringOutcome = AzFramework::FileFunc::WriteJsonToString(jsonDocument, serializedMotionEventTable); + if (!writeToStringOutcome.IsSuccess()) + { + AZ_Error("EMotionFX", false, "WriteJsonToString failed: %s", writeToStringOutcome.GetError().c_str()); + return; + } + + const size_t serializedTableSizeInBytes = serializedMotionEventTable.size(); // the motion event table chunk header EMotionFX::FileFormat::FileChunk chunkHeader; chunkHeader.mChunkID = EMotionFX::FileFormat::SHARED_CHUNK_MOTIONEVENTTABLE; - chunkHeader.mVersion = 2; + chunkHeader.mVersion = 3; chunkHeader.mSizeInBytes = static_cast(serializedTableSizeInBytes + sizeof(EMotionFX::FileFormat::FileMotionEventTableSerialized)); @@ -51,6 +79,6 @@ namespace ExporterLib // save the chunk header and the chunk file->Write(&chunkHeader, sizeof(EMotionFX::FileFormat::FileChunk)); file->Write(&tableHeader, sizeof(EMotionFX::FileFormat::FileMotionEventTableSerialized)); - file->Write(serializedMotionEventTable.GetValue().c_str(), serializedTableSizeInBytes); + file->Write(serializedMotionEventTable.c_str(), serializedTableSizeInBytes); } } // namespace ExporterLib diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Motion/MotionGroupExporter.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Motion/MotionGroupExporter.cpp index c077324ef1..b44f2c36b3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Motion/MotionGroupExporter.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Motion/MotionGroupExporter.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -72,7 +73,7 @@ namespace EMotionFX result += SceneEvents::Process(dataBuilderContext, AZ::RC::Phase::Filling); result += SceneEvents::Process(dataBuilderContext, AZ::RC::Phase::Finalizing); - // Check if there is meta data and apply it to the motion. + // Legacy meta data: Check if there is legacy (XML) event data rule and apply it. AZStd::vector metaDataCommands; if (Rule::MetaDataRule::LoadMetaData(motionGroup, metaDataCommands)) { @@ -82,6 +83,15 @@ namespace EMotionFX } } + // Apply motion meta data. + EMotionFX::Pipeline::Rule::MotionMetaData motionMetaData; + if (EMotionFX::Pipeline::Rule::LoadFromGroup(motionGroup, motionMetaData)) + { + motion->SetEventTable(AZStd::unique_ptr(motionMetaData.m_motionEventTable)); + motion->GetEventTable()->InitAfterLoading(motion); + motion->SetMotionExtractionFlags(motionMetaData.m_motionExtractionFlags); + } + ExporterLib::SaveMotion(filename, motion, MCore::Endian::ENDIAN_LITTLE); static AZ::Data::AssetType emotionFXMotionAssetType("{00494B8E-7578-4BA2-8B28-272E90680787}"); // from MotionAsset.h in EMotionFX Gem context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), emotionFXMotionAssetType, diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/MotionGroupBehavior.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/MotionGroupBehavior.cpp index ceffaf29ea..42f369d992 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/MotionGroupBehavior.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/MotionGroupBehavior.cpp @@ -17,12 +17,13 @@ #include #include -#include +#include #include +#include +#include +#include #include #include -#include -#include namespace EMotionFX { @@ -35,11 +36,13 @@ namespace EMotionFX void MotionGroupBehavior::Reflect(AZ::ReflectContext* context) { Group::MotionGroup::Reflect(context); - Rule::MotionScaleRule::Reflect(context); - Rule::MotionCompressionSettingsRule::Reflect(context); - Rule::MorphTargetRuleReadOnly::Reflect(context); Rule::MotionAdditiveRule::Reflect(context); + Rule::MotionCompressionSettingsRule::Reflect(context); + Rule::MotionMetaData::Reflect(context); + Rule::MotionMetaDataRule::Reflect(context); Rule::MotionSamplingRule::Reflect(context); + Rule::MotionScaleRule::Reflect(context); + Rule::MorphTargetRuleReadOnly::Reflect(context); AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MetaDataRule.h b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MetaDataRule.h index bc9a440cc2..58250174da 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MetaDataRule.h +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MetaDataRule.h @@ -57,7 +57,7 @@ namespace EMotionFX /** * Set the meta data string which contains a list of commands representing the changes the user did on the source asset. - * This string can be constructed using CommandSystem::GenerateMotionMetaData() and CommandSystem::GenerateActorMetaData(). + * This string can be constructed using CommandSystem::GenerateActorMetaData(). * @param metaData The meta data string containing a list of commands to be applied on the source asset. */ void SetMetaData(const AZStd::string& metaData); diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.cpp new file mode 100644 index 0000000000..f57e4ec268 --- /dev/null +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include + +namespace EMotionFX::Pipeline::Rule +{ + void MotionMetaData::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (!serializeContext) + { + return; + } + + serializeContext->Class() + ->Version(1) + ->Field("motionEventTable", &MotionMetaData::m_motionEventTable) + ->Field("motionExtractionFlags", &MotionMetaData::m_motionExtractionFlags) + ; + } + + MotionMetaDataRule::MotionMetaDataRule() + : ExternalToolRule() + { + } + + MotionMetaDataRule::MotionMetaDataRule(const MotionMetaData& data) + : MotionMetaDataRule() + { + m_data = data; + } + + void MotionMetaDataRule::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1) + ->Field("data", &MotionMetaDataRule::m_data) + ; + } + } +} // EMotionFX::Pipeline::Rule diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.h b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.h new file mode 100644 index 0000000000..099451ff7e --- /dev/null +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Rules/MotionMetaDataRule.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace EMotionFX::Pipeline::Rule +{ + struct MotionMetaData + { + AZ_RTTI(EMotionFX::Pipeline::Rule::MotionMetaData, "{A381A915-3CB3-4F60-82B3-70865CFA1F4F}"); + AZ_CLASS_ALLOCATOR(MotionMetaData, AZ::SystemAllocator, 0) + + MotionMetaData() = default; + virtual ~MotionMetaData() = default; + + static void Reflect(AZ::ReflectContext* context); + + EMotionFX::MotionEventTable* m_motionEventTable = nullptr; + EMotionFX::EMotionExtractionFlags m_motionExtractionFlags; + }; + + class MotionMetaDataRule + : public ExternalToolRule + { + public: + AZ_RTTI(EMotionFX::Pipeline::Rule::MotionMetaDataRule, "{E68D0C3D-CBFF-4536-95C1-676474B351A5}", AZ::SceneAPI::DataTypes::IRule); + AZ_CLASS_ALLOCATOR(MotionMetaDataRule, AZ::SystemAllocator, 0) + + MotionMetaDataRule(); + MotionMetaDataRule(const MotionMetaData& data); + ~MotionMetaDataRule() final = default; + + const MotionMetaData& GetData() const override { return m_data; } + void SetData(const MotionMetaData& data) override { m_data = data; } + + static void Reflect(AZ::ReflectContext* context); + + private: + MotionMetaData m_data; + }; +} // EMotionFX::Pipeline::Rule diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/sceneapi_ext_files.cmake b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/sceneapi_ext_files.cmake index bf8977b317..211f85cb1c 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/sceneapi_ext_files.cmake +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/sceneapi_ext_files.cmake @@ -37,6 +37,8 @@ set(FILES Rules/IMotionCompressionSettingsRule.h Rules/MotionCompressionSettingsRule.h Rules/MotionCompressionSettingsRule.cpp + Rules/MotionMetaDataRule.h + Rules/MotionMetaDataRule.cpp Rules/IMotionScaleRule.h Rules/MotionScaleRule.h Rules/MotionScaleRule.cpp diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Event.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Event.cpp index 62c3924d78..e69ef9f20e 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Event.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Event.cpp @@ -15,6 +15,16 @@ namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(Event, MotionEventAllocator, 0) + Event::Event(EventDataPtr&& data) + : m_eventDatas{ AZStd::move(data) } + { + } + + Event::Event(EventDataSet&& datas) + : m_eventDatas(AZStd::move(datas)) + { + } + void Event::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Event.h b/Gems/EMotionFX/Code/EMotionFX/Source/Event.h index 8ac3e48a8c..6665ef1ae3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Event.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Event.h @@ -27,16 +27,9 @@ namespace EMotionFX AZ_RTTI(Event, "{67549E9F-8E3F-4336-BDB8-716AFCBD4985}"); AZ_CLASS_ALLOCATOR_DECL - Event(EventDataPtr&& data = nullptr) - : m_eventDatas{AZStd::move(data)} - { - } - - Event(EventDataSet&& datas) - : m_eventDatas(AZStd::move(datas)) - { - } - + Event() = default; + explicit Event(EventDataPtr&& data); + explicit Event(EventDataSet&& datas); virtual ~Event() = default; static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp index 1ca657af5f..ac4a44b8c0 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp @@ -6,10 +6,17 @@ */ #include +#include +#include #include #include #include #include +#include +#include +#include +#include + #include #include #include @@ -1161,10 +1168,11 @@ namespace EMotionFX AZStd::vector buffer(fileEventTable.m_size); file->Read(&buffer[0], fileEventTable.m_size); - MotionEventTable* motionEventTable = AZ::Utils::LoadObjectFromBuffer(&buffer[0], buffer.size(), context); + auto motionEventTable = AZStd::unique_ptr(AZ::Utils::LoadObjectFromBuffer(&buffer[0], buffer.size(), context)); if (motionEventTable) { - motionEventTable->InitAfterLoading(motion); + motion->SetEventTable(AZStd::move(motionEventTable)); + motion->GetEventTable()->InitAfterLoading(motion); return true; } @@ -1173,6 +1181,57 @@ namespace EMotionFX //================================================================================================= + bool ChunkProcessorMotionEventTrackTable3::Process(MCore::File* file, Importer::ImportParameters& importParams) + { + Motion* motion = importParams.mMotion; + MCORE_ASSERT(motion); + + FileFormat::FileMotionEventTableSerialized fileEventTable; + file->Read(&fileEventTable, sizeof(FileFormat::FileMotionEventTableSerialized)); + + if (GetLogging()) + { + MCore::LogDetailedInfo("- Motion Event Table:"); + MCore::LogDetailedInfo(" + size = %d", fileEventTable.m_size); + } + + AZStd::vector buffer(fileEventTable.m_size); + file->Read(&buffer[0], fileEventTable.m_size); + AZStd::string_view bufferStringView(&buffer[0], buffer.size()); + + auto readJsonOutcome = AzFramework::FileFunc::ReadJsonFromString(bufferStringView); + AZStd::string errorMsg; + if (!readJsonOutcome.IsSuccess()) + { + AZ_Error("EMotionFX", false, "Loading motion event table failed due to ReadJsonFromString. %s", readJsonOutcome.TakeError().c_str()); + return false; + } + rapidjson::Document document = readJsonOutcome.TakeValue(); + + AZ::SerializeContext* context = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + if (!context) + { + return false; + } + + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = context; + + MotionEventTable* motionEventTable = motion->GetEventTable(); + AZ::JsonSerializationResult::ResultCode jsonResult = AZ::JsonSerialization::Load(*motionEventTable, document, settings); + if (jsonResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("EMotionFX", false, "Loading motion event table failed due to AZ::JsonSerialization::Load."); + return false; + } + + motionEventTable->InitAfterLoading(motion); + return true; + } + + //================================================================================================= + bool ChunkProcessorActorInfo::Process(MCore::File* file, Importer::ImportParameters& importParams) { const MCore::Endian::EEndianType endianType = importParams.mEndianType; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h index a319af42c8..057ebea83f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h @@ -312,6 +312,7 @@ namespace EMotionFX // shared file format chunk processors EMFX_CHUNKPROCESSOR(ChunkProcessorMotionEventTrackTable, FileFormat::SHARED_CHUNK_MOTIONEVENTTABLE, 1) EMFX_CHUNKPROCESSOR(ChunkProcessorMotionEventTrackTable2, FileFormat::SHARED_CHUNK_MOTIONEVENTTABLE, 2) + EMFX_CHUNKPROCESSOR(ChunkProcessorMotionEventTrackTable3, FileFormat::SHARED_CHUNK_MOTIONEVENTTABLE, 3) // Actor file format chunk processors EMFX_CHUNKPROCESSOR(ChunkProcessorActorInfo, FileFormat::ACTOR_CHUNK_INFO, 1) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp index 2e568d2539..2c74c5f4e9 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp @@ -1061,6 +1061,7 @@ namespace EMotionFX // shared processors RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable()); RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable2()); + RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable3()); // Actor file format RegisterChunkProcessor(aznew ChunkProcessorActorInfo()); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Motion.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Motion.cpp index 3c34fb83f0..bff1f855d6 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Motion.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Motion.cpp @@ -26,29 +26,20 @@ namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(Motion, MotionAllocator, 0) - - // constructor Motion::Motion(const char* name) : BaseObject() { - mCustomData = nullptr; - mNameID = MCORE_INVALIDINDEX32; - mID = MCore::GetIDGenerator().GenerateID(); - mEventTable = aznew MotionEventTable(); - mUnitType = GetEMotionFX().GetUnitType(); - mFileUnitType = mUnitType; - mExtractionFlags = static_cast(0); - m_motionData = nullptr; + mID = MCore::GetIDGenerator().GenerateID(); + m_eventTable = AZStd::make_unique(); + mUnitType = GetEMotionFX().GetUnitType(); + mFileUnitType = mUnitType; + mExtractionFlags = static_cast(0); if (name) { SetName(name); } - mMotionFPS = 30.0f; - mDirtyFlag = false; - mAutoUnregister = true; - #if defined(EMFX_DEVELOPMENT_BUILD) mIsOwnedByRuntime = false; #endif // EMFX_DEVELOPMENT_BUILD @@ -57,8 +48,6 @@ namespace EMotionFX GetMotionManager().AddMotion(this); } - - // destructor Motion::~Motion() { // trigger the OnDeleteMotion event @@ -70,11 +59,6 @@ namespace EMotionFX GetMotionManager().RemoveMotion(this, false); } - if (mEventTable) - { - mEventTable->Destroy(); - } - delete m_motionData; } @@ -208,19 +192,14 @@ namespace EMotionFX MotionEventTable* Motion::GetEventTable() const { - return mEventTable; + return m_eventTable.get(); } - void Motion::SetEventTable(MotionEventTable* newTable) + void Motion::SetEventTable(AZStd::unique_ptr eventTable) { - if (mEventTable && mEventTable != newTable) - { - mEventTable->Destroy(); - } - mEventTable = newTable; + m_eventTable = AZStd::move(eventTable); } - void Motion::SetID(uint32 id) { mID = id; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Motion.h b/Gems/EMotionFX/Code/EMotionFX/Source/Motion.h index 7e66350cdc..e34e2ef7ab 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Motion.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Motion.h @@ -7,7 +7,7 @@ #pragma once -// include the required headers +#include #include "EMotionFXConfig.h" #include "EMotionFXManager.h" #include "PlayBackInfo.h" @@ -115,7 +115,7 @@ namespace EMotionFX * Set the event table. * @param newTable The new motion event table for the Motion to use. */ - void SetEventTable(MotionEventTable* newTable); + void SetEventTable(AZStd::unique_ptr eventTable); /** * Set the motion framerate. @@ -249,19 +249,19 @@ namespace EMotionFX void SetMotionData(MotionData* motionData, bool delOldFromMem=true); protected: - MotionData* m_motionData; /**< The motion data, which can in theory be any data representation/compression. */ + MotionData* m_motionData = nullptr; /**< The motion data, which can in theory be any data representation/compression. */ AZStd::string mFileName; /**< The filename of the motion. */ PlayBackInfo m_defaultPlayBackInfo; /**< The default/fallback motion playback info which will be used when no playback info is passed to the Play() function. */ - MotionEventTable* mEventTable; /**< The event table, which contains all events, and will make sure events get executed. */ + AZStd::unique_ptr m_eventTable; /**< The event table, which contains all events, and will make sure events get executed. */ MCore::Distance::EUnitType mUnitType; /**< The type of units used. */ MCore::Distance::EUnitType mFileUnitType; /**< The type of units used, inside the file that got loaded. */ - void* mCustomData; /**< A pointer to custom user data that is linked with this motion object. */ - float mMotionFPS; /**< The number of keyframes per second. */ - uint32 mNameID; /**< The ID represention the name or description of this motion. */ - uint32 mID; /**< The unique identification number for the motion. */ + void* mCustomData = nullptr; /**< A pointer to custom user data that is linked with this motion object. */ + float mMotionFPS = 30.0f; /**< The number of keyframes per second. */ + uint32 mNameID = MCORE_INVALIDINDEX32; /**< The ID represention the name or description of this motion. */ + uint32 mID = MCORE_INVALIDINDEX32; /**< The unique identification number for the motion. */ EMotionExtractionFlags mExtractionFlags; /**< The motion extraction flags, which define behavior of the motion extraction system when applied to this motion. */ - bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the motion since the last file save operation. */ - bool mAutoUnregister; /**< Automatically unregister the motion from the motion manager when this motion gets deleted? Default is true. */ + bool mDirtyFlag = false; /**< The dirty flag which indicates whether the user has made changes to the motion since the last file save operation. */ + bool mAutoUnregister = true; /**< Automatically unregister the motion from the motion manager when this motion gets deleted? Default is true. */ #if defined(EMFX_DEVELOPMENT_BUILD) bool mIsOwnedByRuntime; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.cpp index 2060203166..751f7e551a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.cpp @@ -5,7 +5,6 @@ * */ -// include the required headers #include "MotionEventTable.h" #include "MotionEvent.h" #include "MotionEventTrack.h" @@ -19,22 +18,11 @@ namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(MotionEventTable, MotionEventAllocator, 0) - - // constructor - MotionEventTable::MotionEventTable() - : BaseObject() - , m_syncTrack(nullptr) - { - } - - - // destructor MotionEventTable::~MotionEventTable() { RemoveAllTracks(); } - void MotionEventTable::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -56,7 +44,6 @@ namespace EMotionFX track->SetMotion(motion); } - motion->SetEventTable(this); AutoCreateSyncTrack(motion); } @@ -96,7 +83,7 @@ namespace EMotionFX { for (MotionEventTrack* track : m_tracks) { - track->Destroy(); + delete track; } } @@ -109,7 +96,7 @@ namespace EMotionFX { if (delFromMem) { - m_tracks[index]->Destroy(); + delete m_tracks[index]; } m_tracks.erase(AZStd::next(m_tracks.begin(), index)); @@ -196,9 +183,8 @@ namespace EMotionFX AnimGraphSyncTrack* syncTrack; if (!track) { - // create and add the sync track syncTrack = aznew AnimGraphSyncTrack("Sync", motion); - AddTrack(syncTrack); + InsertTrack(0, syncTrack); } else { diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.h b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.h index 25726134ae..2101843a52 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTable.h @@ -7,9 +7,7 @@ #pragma once -// include the required headers #include "EMotionFXConfig.h" -#include "BaseObject.h" #include "AnimGraphSyncTrack.h" #include @@ -40,17 +38,15 @@ namespace EMotionFX * The handling of those events is done by the MotionEventHandler class that you specify to the MotionEventManager singleton. */ class EMFX_API MotionEventTable - : public BaseObject { friend class MotionEvent; public: AZ_CLASS_ALLOCATOR_DECL - AZ_RTTI(MotionEventTable, "{DB5BF142-99BE-4026-8D3E-3E5B30C14714}", BaseObject) + AZ_RTTI(MotionEventTable, "{DB5BF142-99BE-4026-8D3E-3E5B30C14714}") - MotionEventTable(); - - ~MotionEventTable(); + MotionEventTable() = default; + virtual ~MotionEventTable(); static void Reflect(AZ::ReflectContext* context); @@ -100,7 +96,6 @@ namespace EMotionFX AZStd::vector m_tracks; /// A shortcut to the track containing sync events. - AnimGraphSyncTrack* m_syncTrack; - + AnimGraphSyncTrack* m_syncTrack = nullptr; }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.cpp index ea984b82be..f1ec5950fb 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.cpp @@ -5,7 +5,6 @@ * */ -// include the required headers #include "MotionEventTrack.h" #include "MotionEvent.h" #include "EventManager.h" @@ -20,30 +19,19 @@ #include #include - namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(MotionEventTrack, MotionEventAllocator, 0) - // constructor MotionEventTrack::MotionEventTrack(Motion* motion) - : BaseObject() - , mMotion(motion) - , mNameID(MCORE_INVALIDINDEX32) - , mEnabled(true) - , mDeletable(true) + : mMotion(motion) { } - - // extended constructor MotionEventTrack::MotionEventTrack(const char* name, Motion* motion) - : BaseObject() - , mMotion(motion) - , mEnabled(true) - , mDeletable(true) + : mMotion(motion) + , m_name(name) { - SetName(name); } MotionEventTrack::MotionEventTrack(const MotionEventTrack& other) @@ -59,7 +47,7 @@ namespace EMotionFX } m_events = other.m_events; mMotion = other.mMotion; - mNameID = other.mNameID; + m_name = other.m_name; return *this; } @@ -72,8 +60,8 @@ namespace EMotionFX } serializeContext->Class() - ->Version(1) - ->Field("name", &MotionEventTrack::mNameID) + ->Version(2, VersionConverter) + ->Field("name", &MotionEventTrack::m_name) ->Field("enabled", &MotionEventTrack::mEnabled) ->Field("deletable", &MotionEventTrack::mDeletable) ->Field("events", &MotionEventTrack::m_events) @@ -94,6 +82,31 @@ namespace EMotionFX ; } + bool MotionEventTrack::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + const unsigned int version = classElement.GetVersion(); + if (version < 2) + { + int nameElementIndex = classElement.FindElement(AZ_CRC_CE("name")); + if (nameElementIndex < 0) + { + return false; + } + AZ::SerializeContext::DataElementNode& nameElement = classElement.GetSubElement(nameElementIndex); + + MCore::StringIdPoolIndex oldName; + const bool result = nameElement.GetData(oldName); + + classElement.RemoveElement(nameElementIndex); + if (result) + { + AZStd::string newName = MCore::GetStringIdPool().GetName(oldName.m_index); + classElement.AddElementWithData(context, "name", newName); + } + } + return true; + } + // creation MotionEventTrack* MotionEventTrack::Create(Motion* motion) @@ -112,7 +125,7 @@ namespace EMotionFX // set the name of the motion event track void MotionEventTrack::SetName(const char* name) { - mNameID = MCore::GetStringIdPool().GenerateIdForString(name); + m_name = name; } @@ -362,60 +375,31 @@ namespace EMotionFX RemoveAllEvents(); } - - // get the name const char* MotionEventTrack::GetName() const { - if (mNameID == MCORE_INVALIDINDEX32) - { - return ""; - } - - return MCore::GetStringIdPool().GetName(mNameID).c_str(); + return m_name.c_str(); } - - // get the name as string object const AZStd::string& MotionEventTrack::GetNameString() const { - if (mNameID == MCORE_INVALIDINDEX32) - { - return MCore::GetStringIdPool().GetName(0); - } - - return MCore::GetStringIdPool().GetName(mNameID); + return m_name; } - // copy the track contents to a target track // this overwrites all existing contents of the target track void MotionEventTrack::CopyTo(MotionEventTrack* targetTrack) const { - targetTrack->mNameID = mNameID; + targetTrack->m_name = m_name; targetTrack->m_events = m_events; targetTrack->mEnabled = mEnabled; } - // reserve memory for a given amount of events void MotionEventTrack::ReserveNumEvents(size_t numEvents) { m_events.reserve(numEvents); } - - uint32 MotionEventTrack::GetNameID() const - { - return mNameID; - } - - - void MotionEventTrack::SetNameID(uint32 id) - { - mNameID = id; - } - - void MotionEventTrack::SetIsEnabled(bool enabled) { mEnabled = enabled; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.h b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.h index 40e8f24405..b7cfc526cc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionEventTrack.h @@ -7,13 +7,10 @@ #pragma once -// include the required headers +#include #include "EMotionFXConfig.h" #include "BaseObject.h" #include "MotionEvent.h" -#include - -#include namespace AZ { @@ -38,15 +35,15 @@ namespace EMotionFX * The handling of those events is done by the MotionEventHandler class that you specify to the MotionEventManager singleton. */ class EMFX_API MotionEventTrack - : public BaseObject { friend class MotionEvent; public: - AZ_RTTI(MotionEventTrack, "{D142399D-C7DF-4E4A-A099-7E4E662F1E81}", BaseObject) + AZ_RTTI(MotionEventTrack, "{D142399D-C7DF-4E4A-A099-7E4E662F1E81}") AZ_CLASS_ALLOCATOR_DECL - MotionEventTrack() {} + MotionEventTrack() = default; + virtual ~MotionEventTrack() = default; /** * The constructor. @@ -167,8 +164,6 @@ namespace EMotionFX const char* GetName() const; const AZStd::string& GetNameString() const; - uint32 GetNameID() const; - void SetNameID(uint32 id); void SetIsEnabled(bool enabled); bool GetIsEnabled() const; @@ -182,23 +177,22 @@ namespace EMotionFX void ReserveNumEvents(size_t numEvents); protected: - /// The collection of motion events. AZStd::vector m_events; + AZStd::string m_name; /// The motion where this track belongs to. Motion* mMotion; - /// The name ID. - MCore::StringIdPoolIndex mNameID; - /// Is this track enabled? - bool mEnabled; - bool mDeletable; + bool mEnabled = true; + bool mDeletable = true; private: void ProcessEventsImpl(float startTime, float endTime, ActorInstance* actorInstance, const MotionInstance* motionInstance, const AZStd::function& processFunc); template void ExtractEvents(float startTime, float endTime, const MotionInstance* motionInstance, const Functor& processFunc, bool handleLoops = true) const; + + static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/PhysicsSetup.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/PhysicsSetup.cpp index 9fab1b18c3..33e5e3dbc8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/PhysicsSetup.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/PhysicsSetup.cpp @@ -298,14 +298,20 @@ namespace EMotionFX { Physics::CapsuleShapeConfiguration* capsule = static_cast(collider.second.get()); capsule->m_height = boneDirection.GetLength(); - collider.first->m_rotation = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), localBoneDirection.GetNormalized()); + if (AZ::IsClose(localBoneDirection.GetLength(), 1.0f)) + { + collider.first->m_rotation = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), localBoneDirection.GetNormalized()); + } capsule->m_height = boneLength; const float radius = AZ::GetMin(rootMeanSquareDistanceFromBone, minRadiusRatio * boneLength); capsule->m_radius = radius; } else if (colliderType == azrtti_typeid()) { - collider.first->m_rotation = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), localBoneDirection.GetNormalized()); + if (AZ::IsClose(localBoneDirection.GetLength(), 1.0f)) + { + collider.first->m_rotation = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), localBoneDirection.GetNormalized()); + } Physics::BoxShapeConfiguration* box = static_cast(collider.second.get()); box->m_dimensions = AZ::Vector3(2.0f * rootMeanSquareDistanceFromBone, 2.0f * rootMeanSquareDistanceFromBone, boneLength); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Commands.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Commands.cpp index c2b9babf70..920006b1d4 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Commands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Commands.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -293,16 +294,65 @@ namespace EMStudio AZStd::string sourceAssetFilename; EBUS_EVENT_RESULT(fullPathFound, AzToolsFramework::AssetSystemRequestBus, GetFullSourcePathFromRelativeProductPath, productFilename, sourceAssetFilename); - // Generate meta data command for all changes being made to the motion. - const AZStd::vector metaData = CommandSystem::MetaData::GenerateMotionMetaData(motion); + // Load the manifest from disk. + AZStd::shared_ptr scene; + AZ::SceneAPI::Events::SceneSerializationBus::BroadcastResult(scene, &AZ::SceneAPI::Events::SceneSerializationBus::Events::LoadScene, sourceAssetFilename, AZ::Uuid::CreateNull()); + if (!scene) + { + AZ_Error("EMotionFX", false, "Unable to save meta data to manifest due to failed scene loading."); + return false; + } + + AZ::SceneAPI::Containers::SceneManifest& manifest = scene->GetManifest(); + auto values = manifest.GetValueStorage(); + auto groupView = AZ::SceneAPI::Containers::MakeDerivedFilterView(values); + for (EMotionFX::Pipeline::Group::MotionGroup& group : groupView) + { + // Non-case sensitive group name comparison. Product filenames are lower case only and might mismatch casing of the entered group name. + if (AzFramework::StringFunc::Equal(group.GetName().c_str(), groupName.c_str())) + { + // Remove legacy meta data rule. + EMotionFX::Pipeline::Rule::RemoveRuleFromGroup>(*scene, group); + + // Add motion meta data. + EMotionFX::Pipeline::Rule::MotionMetaData motionMetaData; + motionMetaData.m_motionEventTable = motion->GetEventTable(); + motionMetaData.m_motionExtractionFlags = motion->GetMotionExtractionFlags(); + EMotionFX::Pipeline::Rule::SaveToGroup(*scene, group, motionMetaData); + } + } - // Save meta data commands to the manifest. - const bool saveResult = EMotionFX::Pipeline::Rule::MetaDataRule::SaveMetaDataToFile(sourceAssetFilename, groupName, metaData, outResult); + const AZStd::string& manifestFilename = scene->GetManifestFilename(); + const bool fileExisted = AZ::IO::FileIOBase::GetInstance()->Exists(manifestFilename.c_str()); + + // Source Control: Checkout file. + if (fileExisted) + { + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + bool checkoutResult = false; + ApplicationBus::BroadcastResult(checkoutResult, &ApplicationBus::Events::RequestEditForFileBlocking, manifestFilename.c_str(), "Checking out manifest from source control.", []([[maybe_unused]] int& current, [[maybe_unused]] int& max) {}); + if (!checkoutResult) + { + AZ_Error("EMotionFX", false, "Cannot checkout file '%s' from source control.", manifestFilename.c_str()); + return false; + } + } + + const bool saveResult = manifest.SaveToFile(manifestFilename.c_str()); if (saveResult) { motion->SetDirtyFlag(false); } + // Source Control: Add file in case it did not exist before (when saving it the first time). + if (saveResult && !fileExisted) + { + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + bool checkoutResult = false; + ApplicationBus::BroadcastResult(checkoutResult, &ApplicationBus::Events::RequestEditForFileBlocking, manifestFilename.c_str(), "Adding manifest to source control.", []([[maybe_unused]] int& current, [[maybe_unused]] int& max) {}); + AZ_Error("EMotionFX", checkoutResult, "Cannot add file '%s' to source control.", manifestFilename.c_str()); + } + return saveResult; } diff --git a/Gems/EMotionFX/Code/Tests/EventManagerTests.cpp b/Gems/EMotionFX/Code/Tests/EventManagerTests.cpp index 6ab796691c..c717a1b27e 100644 --- a/Gems/EMotionFX/Code/Tests/EventManagerTests.cpp +++ b/Gems/EMotionFX/Code/Tests/EventManagerTests.cpp @@ -31,8 +31,8 @@ TEST_F(SystemComponentFixture, DISABLED_EventDataFactoryMakesUniqueData) EXPECT_EQ(loadedTrack->GetEvent(0).GetEventDatas()[0], track->GetEvent(0).GetEventDatas()[0]); EXPECT_EQ(loadedTrack->GetEvent(0).GetEventDatas()[0].use_count(), 2); - track->Destroy(); - loadedTrack->Destroy(); + delete track; + delete loadedTrack; } } // end namespace EMotionFX