[LYN-4574] [LYN-4603] [LYN-4669] Saving motions and actors to json-based .assetinfo files in the Animation Editor fails (#1509)

* Fixes saving motions from within the Animation Editor
* Fixes saving actors from within the Animation Editor
* The motion event chunk of the .motion file format now also stores the event data as json (rather than XML) reducing motion file sizes (Example: 60KB motion went down to 49KB, containing only 4 motion events from 2 tracks).
* Fully backward compatible
* New motion meta data rule stores the event data directly rather than command strings or objects. This is the way that aligns with the Json paradigm and as side-effect bypasses the optionals that we use for the commands which fixes the issue.

* [LYN-4574] Adding new motion event meta data rule that stores the event data directly rather than via commands to align with the Json paradigm
* [LYN-4574] Preparing motion, event table and event track for Json serialization
* [LYN-4574] New chunk to store motion event data in Json format (fully backward compatible to XML)
* [LYN-4669] Json: Empty AZStd::vector<AZStd::shared_ptr<T>> serializes into 1x element with nullptr as data
* [LYN-4603] EMotion FX: Cannot save actors with physics or simulated object setup in Json format
main
Benjamin Jillich 5 years ago committed by GitHub
parent 0ad6346e8b
commit bf0816fb69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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())

@ -24,48 +24,6 @@
namespace CommandSystem
{
AZStd::vector<MCore::Command*> MetaData::GenerateMotionMetaData(EMotionFX::Motion* motion)
{
AZStd::vector<MCore::Command*> 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<MCore::Command*>& metaDataCommands)
{
for (MCore::Command* command : metaDataCommands)

@ -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<MCore::Command*> 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.

@ -5,6 +5,13 @@
*
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/document.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include "Exporter.h"
#include <MCore/Source/ReflectionSerializer.h>
#include <EMotionFX/Source/MotionEvent.h>
@ -28,17 +35,38 @@ namespace ExporterLib
return;
}
AZ::Outcome<AZStd::string> 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<uint32>(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

@ -10,6 +10,7 @@
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPIExt/Rules/MetaDataRule.h>
#include <SceneAPIExt/Rules/MotionMetaDataRule.h>
#include <SceneAPIExt/Groups/IMotionGroup.h>
#include <RCExt/Motion/MotionGroupExporter.h>
#include <RCExt/ExportContexts.h>
@ -72,7 +73,7 @@ namespace EMotionFX
result += SceneEvents::Process<MotionDataBuilderContext>(dataBuilderContext, AZ::RC::Phase::Filling);
result += SceneEvents::Process<MotionDataBuilderContext>(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<MCore::Command*> 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<EMotionFX::Pipeline::Rule::MotionMetaDataRule, EMotionFX::Pipeline::Rule::MotionMetaData>(motionGroup, motionMetaData))
{
motion->SetEventTable(AZStd::unique_ptr<MotionEventTable>(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,

@ -17,12 +17,13 @@
#include <SceneAPIExt/Behaviors/MotionGroupBehavior.h>
#include <SceneAPIExt/Groups/MotionGroup.h>
#include <SceneAPIExt/Rules/MotionScaleRule.h>
#include <SceneAPIExt/Rules/MotionAdditiveRule.h>
#include <SceneAPIExt/Rules/MotionCompressionSettingsRule.h>
#include <SceneAPIExt/Rules/MotionMetaDataRule.h>
#include <SceneAPIExt/Rules/MotionSamplingRule.h>
#include <SceneAPIExt/Rules/MotionScaleRule.h>
#include <SceneAPIExt/Rules/MotionRangeRule.h>
#include <SceneAPIExt/Rules/MorphTargetRule.h>
#include <SceneAPIExt/Rules/MotionAdditiveRule.h>
#include <SceneAPIExt/Rules/MotionSamplingRule.h>
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<AZ::SerializeContext*>(context);
if (serializeContext)

@ -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);

@ -0,0 +1,53 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPIExt/Rules/MotionMetaDataRule.h>
namespace EMotionFX::Pipeline::Rule
{
void MotionMetaData::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (!serializeContext)
{
return;
}
serializeContext->Class<MotionMetaData>()
->Version(1)
->Field("motionEventTable", &MotionMetaData::m_motionEventTable)
->Field("motionExtractionFlags", &MotionMetaData::m_motionExtractionFlags)
;
}
MotionMetaDataRule::MotionMetaDataRule()
: ExternalToolRule<MotionMetaData>()
{
}
MotionMetaDataRule::MotionMetaDataRule(const MotionMetaData& data)
: MotionMetaDataRule()
{
m_data = data;
}
void MotionMetaDataRule::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MotionMetaDataRule>()
->Version(1)
->Field("data", &MotionMetaDataRule::m_data)
;
}
}
} // EMotionFX::Pipeline::Rule

@ -0,0 +1,51 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <EMotionFX/Source/MotionEventTable.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
#include <SceneAPIExt/Rules/ExternalToolRule.h>
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<MotionMetaData>
{
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

@ -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

@ -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<AZ::SerializeContext*>(context);

@ -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);

@ -6,10 +6,17 @@
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/document.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <EMotionFX/Source/Parameter/GroupParameter.h>
#include <EMotionFX/Source/Parameter/ParameterFactory.h>
#include <MCore/Source/AttributeFactory.h>
@ -1161,10 +1168,11 @@ namespace EMotionFX
AZStd::vector<char> buffer(fileEventTable.m_size);
file->Read(&buffer[0], fileEventTable.m_size);
MotionEventTable* motionEventTable = AZ::Utils::LoadObjectFromBuffer<MotionEventTable>(&buffer[0], buffer.size(), context);
auto motionEventTable = AZStd::unique_ptr<MotionEventTable>(AZ::Utils::LoadObjectFromBuffer<MotionEventTable>(&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<char> 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;

@ -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)

@ -1061,6 +1061,7 @@ namespace EMotionFX
// shared processors
RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable());
RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable2());
RegisterChunkProcessor(aznew ChunkProcessorMotionEventTrackTable3());
// Actor file format
RegisterChunkProcessor(aznew ChunkProcessorActorInfo());

@ -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<EMotionExtractionFlags>(0);
m_motionData = nullptr;
mID = MCore::GetIDGenerator().GenerateID();
m_eventTable = AZStd::make_unique<MotionEventTable>();
mUnitType = GetEMotionFX().GetUnitType();
mFileUnitType = mUnitType;
mExtractionFlags = static_cast<EMotionExtractionFlags>(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<MotionEventTable> eventTable)
{
if (mEventTable && mEventTable != newTable)
{
mEventTable->Destroy();
}
mEventTable = newTable;
m_eventTable = AZStd::move(eventTable);
}
void Motion::SetID(uint32 id)
{
mID = id;

@ -7,7 +7,7 @@
#pragma once
// include the required headers
#include <AzCore/std/smart_ptr/unique_ptr.h>
#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<MotionEventTable> 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<MotionEventTable> 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;

@ -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<AZ::SerializeContext*>(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
{

@ -7,9 +7,7 @@
#pragma once
// include the required headers
#include "EMotionFXConfig.h"
#include "BaseObject.h"
#include "AnimGraphSyncTrack.h"
#include <AzCore/std/containers/vector.h>
@ -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<MotionEventTrack*> m_tracks;
/// A shortcut to the track containing sync events.
AnimGraphSyncTrack* m_syncTrack;
AnimGraphSyncTrack* m_syncTrack = nullptr;
};
} // namespace EMotionFX

@ -5,7 +5,6 @@
*
*/
// include the required headers
#include "MotionEventTrack.h"
#include "MotionEvent.h"
#include "EventManager.h"
@ -20,30 +19,19 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
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<MotionEventTrack>()
->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<MCore::StringIdPoolIndex>(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;

@ -7,13 +7,10 @@
#pragma once
// include the required headers
#include <AzCore/std/containers/vector.h>
#include "EMotionFXConfig.h"
#include "BaseObject.h"
#include "MotionEvent.h"
#include <MCore/Source/StringIdPool.h>
#include <AzCore/std/containers/vector.h>
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<MotionEvent> 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<void(EMotionFX::EventInfo&)>& processFunc);
template <typename Functor>
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

@ -298,14 +298,20 @@ namespace EMotionFX
{
Physics::CapsuleShapeConfiguration* capsule = static_cast<Physics::CapsuleShapeConfiguration*>(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<Physics::BoxShapeConfiguration>())
{
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<Physics::BoxShapeConfiguration*>(collider.second.get());
box->m_dimensions = AZ::Vector3(2.0f * rootMeanSquareDistanceFromBone, 2.0f * rootMeanSquareDistanceFromBone, boneLength);
}

@ -22,6 +22,7 @@
#include <SceneAPIExt/Rules/ActorPhysicsSetupRule.h>
#include <SceneAPIExt/Rules/SimulatedObjectSetupRule.h>
#include <SceneAPIExt/Rules/MetaDataRule.h>
#include <SceneAPIExt/Rules/MotionMetaDataRule.h>
#include <SceneAPIExt/Groups/MotionGroup.h>
#include <SceneAPIExt/Groups/ActorGroup.h>
@ -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<MCore::Command*> metaData = CommandSystem::MetaData::GenerateMotionMetaData(motion);
// Load the manifest from disk.
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> 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<EMotionFX::Pipeline::Group::MotionGroup>(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<EMotionFX::Pipeline::Rule::MetaDataRule, const AZStd::vector<MCore::Command*>>(*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<EMotionFX::Pipeline::Rule::MotionMetaDataRule, EMotionFX::Pipeline::Rule::MotionMetaData>(*scene, group, motionMetaData);
}
}
// Save meta data commands to the manifest.
const bool saveResult = EMotionFX::Pipeline::Rule::MetaDataRule::SaveMetaDataToFile<EMotionFX::Pipeline::Group::MotionGroup>(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;
}

@ -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

Loading…
Cancel
Save