Add support for creating virtual Anim Graph at runtime for a single animation playback

This commit is contained in:
Wojtek Figat
2021-07-15 16:58:26 +02:00
parent 2711bfdc82
commit 4f03acb149
3 changed files with 103 additions and 8 deletions

View File

@@ -4,13 +4,16 @@
#if USE_EDITOR
#include "AnimationGraphFunction.h"
#endif
#include "SkinnedModel.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
REGISTER_BINARY_ASSET(AnimationGraph, "FlaxEngine.AnimationGraph", false);
REGISTER_BINARY_ASSET(AnimationGraph, "FlaxEngine.AnimationGraph", true);
AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)
@@ -64,9 +67,83 @@ void AnimationGraph::OnDependencyModified(BinaryAsset* asset)
#endif
bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop)
{
if (!IsVirtual())
{
LOG(Warning, "Only virtual Anim Graph can be modified.");
return true;
}
if (!baseModel || !anim)
{
Log::ArgumentNullException();
return true;
}
// Create Graph data
MemoryWriteStream writeStream(512);
{
AnimGraph graph(nullptr);
graph.Nodes.Resize(2);
auto& rootNode = graph.Nodes[0];
rootNode.Type = GRAPH_NODE_MAKE_TYPE(9, 1);
rootNode.ID = 1;
rootNode.Values.Resize(1);
rootNode.Values[0] = (int32)RootMotionMode::NoExtraction;
rootNode.Boxes.Resize(1);
rootNode.Boxes[0] = AnimGraphBox(&rootNode, 0, VariantType::Void);
auto& animNode = graph.Nodes[1];
animNode.Type = GRAPH_NODE_MAKE_TYPE(9, 2);
animNode.ID = 2;
animNode.Values.Resize(4);
animNode.Values[0] = anim->GetID();
animNode.Values[1] = 1.0f;
animNode.Values[2] = loop;
animNode.Values[3] = 0.0f;
animNode.Boxes.Resize(8);
animNode.Boxes[0] = AnimGraphBox(&animNode, 0, VariantType::Void);
animNode.Boxes[0].Connections.Add(&rootNode.Boxes[0]);
rootNode.Boxes[0].Connections.Add(&animNode.Boxes[0]);
animNode.Boxes[1] = AnimGraphBox(&animNode, 1, VariantType::Void);
animNode.Boxes[2] = AnimGraphBox(&animNode, 2, VariantType::Void);
animNode.Boxes[3] = AnimGraphBox(&animNode, 3, VariantType::Void);
animNode.Boxes[4] = AnimGraphBox(&animNode, 4, VariantType::Void);
animNode.Boxes[5] = AnimGraphBox(&animNode, 5, VariantType::Void);
animNode.Boxes[6] = AnimGraphBox(&animNode, 6, VariantType::Void);
animNode.Boxes[7] = AnimGraphBox(&animNode, 7, VariantType::Void);
graph.Parameters.Resize(1);
AnimGraphParameter& baseModelParam = graph.Parameters[0];
baseModelParam.Identifier = ANIM_GRAPH_PARAM_BASE_MODEL_ID;
baseModelParam.Type = VariantType::Asset;
baseModelParam.IsPublic = false;
baseModelParam.Value = baseModel->GetID();
if (graph.Save(&writeStream, USE_EDITOR))
return true;
}
// Load Graph data (with initialization)
ScopeLock lock(Locker);
MemoryReadStream readStream(writeStream.GetHandle(), writeStream.GetPosition());
return Graph.Load(&readStream, USE_EDITOR);
}
BytesContainer AnimationGraph::LoadSurface()
{
ScopeLock lock(Locker);
if (IsVirtual())
{
// Serialize runtime graph
MemoryWriteStream stream(512);
if (!Graph.Save(&stream, USE_EDITOR))
{
BytesContainer result;
result.Copy(stream.GetHandle(), stream.GetPosition());
return result;
}
}
// Load data from asset
if (!LoadChunks(GET_CHUNK_FLAG(0)))
{
const auto data = GetChunk(0);
@@ -96,6 +173,12 @@ bool AnimationGraph::SaveSurface(BytesContainer& data)
ScopeLock lock(Locker);
if (IsVirtual())
{
MemoryReadStream readStream(data.Get(), data.Length());
return Graph.Load(&readStream, USE_EDITOR);
}
// Release all chunks
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
ReleaseChunk(i);
@@ -137,4 +220,12 @@ void AnimationGraph::FindDependencies(AnimGraphBase* graph)
}
}
void AnimationGraph::GetReferences(Array<Guid>& output) const
{
// Base
BinaryAsset::GetReferences(output);
Graph.GetReferences(output);
}
#endif

View File

@@ -33,6 +33,15 @@ public:
return Graph.BaseModel.Get();
}
/// <summary>
/// Initializes virtual Anim Graph to play a single animation.
/// </summary>
/// <param name="baseModel">The base model asset.</param>
/// <param name="anim">The animation to play.</param>
/// <param name="loop">True if play animation in a loop.</param>
/// <returns>True if failed, otherwise false.</returns>
API_FUNCTION() bool InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop = true);
/// <summary>
/// Tries to load surface graph from the asset.
/// </summary>
@@ -58,13 +67,7 @@ public:
// [BinaryAsset]
#if USE_EDITOR
void GetReferences(Array<Guid>& output) const override
{
// Base
BinaryAsset::GetReferences(output);
Graph.GetReferences(output);
}
void GetReferences(Array<Guid>& output) const override;
#endif
protected: