Refactor Multi Blend nodes to support up to 255 blend points

This commit is contained in:
Wojtek Figat
2024-04-19 16:30:34 +02:00
parent 71fe280464
commit 7653fba381
10 changed files with 168 additions and 150 deletions

View File

@@ -23,9 +23,7 @@ namespace FlaxEditor.Surface.Archetypes
private readonly bool _is2D;
private Float2 _rangeX, _rangeY;
private Float2 _debugPos = Float2.Minimum;
private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount];
private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount];
private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount];
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
/// <summary>
/// Represents single blend point.
@@ -44,6 +42,11 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
public const float DefaultSize = 8.0f;
/// <summary>
/// Blend point index.
/// </summary>
public int Index => _index;
/// <summary>
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
/// </summary>
@@ -190,6 +193,11 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
public bool Is2D => _is2D;
/// <summary>
/// Blend points count.
/// </summary>
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
/// <summary>
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
/// </summary>
@@ -206,28 +214,6 @@ namespace FlaxEditor.Surface.Archetypes
_is2D = is2D;
}
/// <summary>
/// Gets the blend space data.
/// </summary>
/// <param name="rangeX">The space range for X axis (X-width, Y-height).</param>
/// <param name="rangeY">The space range for Y axis (X-width, Y-height).</param>
/// <param name="pointsAnims">The points anims (input array to fill of size equal 14).</param>
/// <param name="pointsLocations">The points locations (input array to fill of size equal 14).</param>
public void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations)
{
var data0 = (Float4)_node.Values[0];
rangeX = new Float2(data0.X, data0.Y);
rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++)
{
var dataA = (Float4)_node.Values[4 + i * 2];
var dataB = (Guid)_node.Values[5 + i * 2];
pointsAnims[i] = dataB;
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y) : 0.0f);
}
}
private void AddAsset(Float2 location)
{
// Reuse existing animation
@@ -259,16 +245,22 @@ namespace FlaxEditor.Surface.Archetypes
public void AddAsset(Guid asset, Float2 location)
{
// Find the first free slot
var count = PointsCount;
if (count == Animation.MultiBlend.MaxAnimationsCount)
return;
var values = (object[])_node.Values.Clone();
var index = 0;
for (; index < Animation.MultiBlend.MaxAnimationsCount; index++)
for (; index < count; index++)
{
var dataB = (Guid)_node.Values[5 + index * 2];
if (dataB == Guid.Empty)
break;
}
if (index == Animation.MultiBlend.MaxAnimationsCount)
return; // TODO: unlimited amount of blend points
if (index == count)
{
// Add another blend point
Array.Resize(ref values, values.Length + 2);
}
values[4 + index * 2] = new Float4(location.X, _is2D ? location.Y : 0.0f, 0, 1.0f);
values[5 + index * 2] = asset;
@@ -382,11 +374,22 @@ namespace FlaxEditor.Surface.Archetypes
public override void Update(float deltaTime)
{
// Synchronize blend points collection
GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations);
for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++)
var data0 = (Float4)_node.Values[0];
_rangeX = new Float2(data0.X, data0.Y);
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
var count = PointsCount;
while (_blendPoints.Count > count)
{
var animId = _pointsAnims[i];
var location = _pointsLocations[i];
_blendPoints[count].Dispose();
_blendPoints.RemoveAt(count);
}
while (_blendPoints.Count < count)
_blendPoints.Add(null);
for (int i = 0; i < count; i++)
{
var animId = (Guid)_node.Values[5 + i * 2];
var dataA = (Float4)_node.Values[4 + i * 2];
var location = new Float2(Mathf.Clamp(dataA.X, _rangeX.X, _rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, _rangeY.X, _rangeY.Y) : 0.0f);
if (animId != Guid.Empty)
{
if (_blendPoints[i] == null)
@@ -394,7 +397,6 @@ namespace FlaxEditor.Surface.Archetypes
// Create missing blend point
_blendPoints[i] = new BlendPoint(this, i)
{
Tag = i,
Parent = this,
};
}
@@ -477,10 +479,11 @@ namespace FlaxEditor.Surface.Archetypes
var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu();
var b = menu.AddButton("Add point", OnAddPoint);
b.Tag = location;
b.Enabled = PointsCount < Animation.MultiBlend.MaxAnimationsCount;
if (GetChildAt(location) is BlendPoint blendPoint)
{
b = menu.AddButton("Remove point", OnRemovePoint);
b.Tag = blendPoint.Tag;
b.Tag = blendPoint.Index;
b.TooltipText = blendPoint.TooltipText;
}
menu.Show(this, location);
@@ -538,8 +541,6 @@ namespace FlaxEditor.Surface.Archetypes
GetPointsArea(out var pointsArea);
var data0 = (Float4)_node.Values[0];
var rangeX = new Float2(data0.X, data0.Y);
var rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.One;
var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f);
// Background
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
@@ -562,6 +563,7 @@ namespace FlaxEditor.Surface.Archetypes
}
if (_is2D)
{
var rangeY = new Float2(data0.Z, data0.W);
for (int i = 0; i <= splits; i++)
{
float alpha = (float)i / splits;
@@ -605,6 +607,11 @@ namespace FlaxEditor.Surface.Archetypes
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
public abstract class MultiBlend : SurfaceNode
{
/// <summary>
/// The blend space editor.
/// </summary>
protected BlendPointsEditor _editor;
/// <summary>
/// The selected animation label.
/// </summary>
@@ -638,7 +645,7 @@ namespace FlaxEditor.Surface.Archetypes
/// <summary>
/// The maximum animations amount to blend per node.
/// </summary>
public const int MaxAnimationsCount = 14;
public const int MaxAnimationsCount = 255;
/// <summary>
/// Gets or sets the index of the selected animation.
@@ -661,20 +668,13 @@ namespace FlaxEditor.Surface.Archetypes
Text = "Selected Animation:",
Parent = this
};
_selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width)
{
Parent = this
};
_selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing;
_selectedAnimation.SelectedIndexChanged += OnSelectedAnimationChanged;
var items = new List<string>(MaxAnimationsCount);
while (items.Count < MaxAnimationsCount)
items.Add(string.Empty);
_selectedAnimation.Items = items;
_animationPicker = new AssetPicker(new ScriptType(typeof(FlaxEngine.Animation)), new Float2(_selectedAnimation.Left, _selectedAnimation.Bottom + 4))
{
Parent = this
@@ -687,7 +687,6 @@ namespace FlaxEditor.Surface.Archetypes
Text = "Speed:",
Parent = this
};
_animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4)
{
SlideSpeed = 0.01f,
@@ -700,7 +699,8 @@ namespace FlaxEditor.Surface.Archetypes
{
var items = comboBox.Items;
items.Clear();
for (var i = 0; i < MaxAnimationsCount; i++)
var count = _editor.PointsCount;
for (var i = 0; i < count; i++)
{
var animId = (Guid)Values[5 + i * 2];
var path = string.Empty;
@@ -808,6 +808,16 @@ namespace FlaxEditor.Surface.Archetypes
UpdateUI();
}
/// <inheritdoc />
public override void OnSpawned(SurfaceNodeActions action)
{
base.OnSpawned(action);
// Select the first animation to make setup easier
OnSelectedAnimationPopupShowing(_selectedAnimation);
_selectedAnimation.SelectedIndex = 0;
}
/// <inheritdoc />
public override void OnValuesChanged()
{
@@ -825,7 +835,6 @@ namespace FlaxEditor.Surface.Archetypes
{
private readonly Label _animationXLabel;
private readonly FloatValueBox _animationX;
private readonly BlendPointsEditor _editor;
/// <inheritdoc />
public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
@@ -896,7 +905,6 @@ namespace FlaxEditor.Surface.Archetypes
private readonly FloatValueBox _animationX;
private readonly Label _animationYLabel;
private readonly FloatValueBox _animationY;
private readonly BlendPointsEditor _editor;
/// <inheritdoc />
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)

View File

@@ -621,7 +621,7 @@ namespace FlaxEditor.Surface.Archetypes
Create = (id, context, arch, groupArch) => new MultiBlend1D(id, context, arch, groupArch),
Title = "Multi Blend 1D",
Description = "Animation blending in 1D",
Flags = NodeFlags.AnimGraph,
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
Size = new Float2(420, 300),
DefaultValues = new object[]
{
@@ -633,19 +633,6 @@ namespace FlaxEditor.Surface.Archetypes
// Per blend sample data
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
},
Elements = new[]
{
@@ -670,7 +657,7 @@ namespace FlaxEditor.Surface.Archetypes
Create = (id, context, arch, groupArch) => new MultiBlend2D(id, context, arch, groupArch),
Title = "Multi Blend 2D",
Description = "Animation blending in 2D",
Flags = NodeFlags.AnimGraph,
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
Size = new Float2(420, 620),
DefaultValues = new object[]
{
@@ -682,19 +669,6 @@ namespace FlaxEditor.Surface.Archetypes
// Per blend sample data
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
new Float4(0, 0, 0, 1.0f), Guid.Empty,
},
Elements = new[]
{

View File

@@ -73,6 +73,11 @@ namespace FlaxEditor.Surface
/// </summary>
BehaviorTreeGraph = 1024,
/// <summary>
/// Node can have different amount of items in values array.
/// </summary>
VariableValuesSize = 2048,
/// <summary>
/// Node can be used in the all visual graphs.
/// </summary>

View File

@@ -951,15 +951,20 @@ namespace FlaxEditor.Surface
{
if (_isDuringValuesEditing || !Surface.CanEdit)
return;
if (values == null || Values == null || values.Length != Values.Length)
if (values == null || Values == null)
throw new ArgumentException();
bool resize = values.Length != Values.Length;
if (resize && (Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
throw new ArgumentException();
_isDuringValuesEditing = true;
var before = Surface.Undo != null ? (object[])Values.Clone() : null;
Array.Copy(values, Values, values.Length);
if (resize)
Values = (object[])values.Clone();
else
Array.Copy(values, Values, values.Length);
OnValuesChanged();
Surface.MarkAsEdited(graphEdited);

View File

@@ -14,6 +14,7 @@ namespace FlaxEditor.Surface.Undo
private ContextHandle _context;
private readonly uint _nodeId;
private readonly bool _graphEdited;
private readonly bool _resize;
private object[] _before;
private object[] _after;
@@ -23,7 +24,8 @@ namespace FlaxEditor.Surface.Undo
throw new ArgumentNullException(nameof(before));
if (node?.Values == null)
throw new ArgumentNullException(nameof(node));
if (before.Length != node.Values.Length)
_resize = before.Length != node.Values.Length;
if (_resize && (node.Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
throw new ArgumentException(nameof(before));
_surface = node.Surface;
@@ -48,7 +50,10 @@ namespace FlaxEditor.Surface.Undo
throw new Exception("Missing node.");
node.SetIsDuringValuesEditing(true);
Array.Copy(_after, node.Values, _after.Length);
if (_resize)
node.Values = (object[])_after.Clone();
else
Array.Copy(_after, node.Values, _after.Length);
node.OnValuesChanged();
context.MarkAsModified(_graphEdited);
node.SetIsDuringValuesEditing(false);
@@ -65,7 +70,10 @@ namespace FlaxEditor.Surface.Undo
throw new Exception("Missing node.");
node.SetIsDuringValuesEditing(true);
Array.Copy(_before, node.Values, _before.Length);
if (_resize)
node.Values = (object[])_before.Clone();
else
Array.Copy(_before, node.Values, _before.Length);
node.OnValuesChanged();
context.MarkAsModified(_graphEdited);
node.SetIsDuringValuesEditing(false);

View File

@@ -630,6 +630,15 @@ namespace FlaxEditor.Surface
stream.ReadCommonValue(ref node.Values[j]);
}
}
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
{
node.Values = new object[valuesCnt];
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
// ReSharper disable once PossibleNullReferenceException
stream.ReadCommonValue(ref node.Values[j]);
}
}
else
{
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
@@ -795,6 +804,12 @@ namespace FlaxEditor.Surface
for (int j = firstValueReadIdx; j < valuesCnt; j++)
node.Values[j] = stream.ReadVariant();
}
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
{
node.Values = new object[valuesCnt];
for (int j = firstValueReadIdx; j < valuesCnt; j++)
node.Values[j] = stream.ReadVariant();
}
else
{
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));

View File

@@ -117,11 +117,11 @@ void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket)
bucket.InstanceData.Init = true;
}
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
bool SortMultiBlend1D(const ANIM_GRAPH_MULTI_BLEND_INDEX& a, const ANIM_GRAPH_MULTI_BLEND_INDEX& b, AnimGraphNode* n)
{
// Sort items by X location from the lowest to the highest
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
return aX < bX;
}
@@ -133,8 +133,6 @@ bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
bool AnimGraphBase::onNodeLoaded(Node* n)
{
((AnimGraphNode*)n)->Graph = _graph;
// Check if this node needs a state container
switch (n->GroupID)
{
// Tools
@@ -173,45 +171,41 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
break;
// Multi Blend 1D
case 12:
{
ADD_BUCKET(MultiBlendBucketInit);
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
n->Data.MultiBlend1D.Length = -1;
n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS);
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
n->Data.MultiBlend1D.IndicesSorted = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX) * n->Data.MultiBlend1D.Count);
n->Assets.Resize(n->Data.MultiBlend1D.Count);
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
{
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
n->Data.MultiBlend1D.IndicesSorted[i] = n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
n->Data.MultiBlend1D.IndicesSorted[i] = (ANIM_GRAPH_MULTI_BLEND_INDEX)(n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_INVALID);
}
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS, &SortMultiBlend1D, n);
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, n->Data.MultiBlend1D.Count, &SortMultiBlend1D, n);
break;
}
// Multi Blend 2D
case 13:
{
ADD_BUCKET(MultiBlendBucketInit);
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
n->Data.MultiBlend2D.Length = -1;
// Get blend points locations
Array<Float2, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS + 3>> vertices;
byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS);
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
Array<Float2> vertices;
Array<ANIM_GRAPH_MULTI_BLEND_INDEX> vertexToAnim;
n->Assets.Resize(n->Data.MultiBlend1D.Count);
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
{
n->Assets[i] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
if (n->Assets[i])
{
const int32 vertexIndex = vertices.Count();
vertexIndexToAnimIndex[vertexIndex] = i;
vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4()));
}
else
{
vertexIndexToAnimIndex[i] = -1;
vertexToAnim.Add((ANIM_GRAPH_MULTI_BLEND_INDEX)i);
}
}
// Triangulate
Array<Delaunay2D::Triangle, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS>> triangles;
Array<Delaunay2D::Triangle> triangles;
Delaunay2D::Triangulate(vertices, triangles);
if (triangles.Count() == 0)
{
@@ -229,15 +223,14 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
}
// Store triangles vertices indices (map the back to the anim node slots)
for (int32 i = 0; i < triangles.Count(); i++)
n->Data.MultiBlend2D.TrianglesCount = triangles.Count();
n->Data.MultiBlend2D.Triangles = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(triangles.Count() * 3 - sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX));
for (int32 i = 0, t = 0; i < triangles.Count(); i++)
{
n->Data.MultiBlend2D.TrianglesP0[i] = vertexIndexToAnimIndex[triangles[i].Indices[0]];
n->Data.MultiBlend2D.TrianglesP1[i] = vertexIndexToAnimIndex[triangles[i].Indices[1]];
n->Data.MultiBlend2D.TrianglesP2[i] = vertexIndexToAnimIndex[triangles[i].Indices[2]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[0]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[1]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[2]];
}
if (triangles.Count() < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS)
n->Data.MultiBlend2D.TrianglesP0[triangles.Count()] = ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
break;
}
// Blend Pose

View File

@@ -102,6 +102,28 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani
return out;
}
AnimGraphNode::~AnimGraphNode()
{
// Free allocated memory
switch (GroupID)
{
// Animation
case 9:
switch (TypeID)
{
// Multi Blend 1D
case 12:
Allocator::Free(Data.MultiBlend1D.IndicesSorted);
break;
// Multi Blend 2D
case 13:
Allocator::Free(Data.MultiBlend2D.Triangles);
break;
}
break;
}
}
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
{
auto& context = *AnimGraphExecutor::Context.Get();

View File

@@ -13,8 +13,8 @@
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
#define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr)
#define ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS 14
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
#define ANIM_GRAPH_MULTI_BLEND_INDEX byte
#define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
#define ANIM_GRAPH_MAX_CALL_STACK 100
#define ANIM_GRAPH_MAX_EVENTS 64
@@ -434,38 +434,24 @@ class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
public:
struct MultiBlend1DData
{
/// <summary>
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
/// </summary>
// Amount of blend points.
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
float Length;
/// <summary>
/// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS which is invalid.
/// </summary>
byte IndicesSorted[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_INVALID which is invalid.
ANIM_GRAPH_MULTI_BLEND_INDEX* IndicesSorted;
};
struct MultiBlend2DData
{
/// <summary>
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
/// </summary>
// Amount of blend points.
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
float Length;
/// <summary>
/// Cached triangles vertices (vertex 0). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP0[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
/// <summary>
/// Cached triangles vertices (vertex 1). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP1[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
/// <summary>
/// Cached triangles vertices (vertex 2). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP2[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
// Amount of triangles.
int32 TrianglesCount;
// Cached triangles vertices (3 bytes per triangle). Contains list of indices for triangles to use for blending.
ANIM_GRAPH_MULTI_BLEND_INDEX* Triangles;
};
struct StateMachineData
@@ -582,6 +568,8 @@ public:
{
}
~AnimGraphNode();
public:
/// <summary>
/// Gets the per-node node transformations cache (cached).
@@ -784,7 +772,7 @@ struct AnimGraphContext
bool StackOverFlow;
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK> > NodePath;
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> NodePath;
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
int32 PoseCacheSize;

View File

@@ -1269,7 +1269,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto& data = node->Data.MultiBlend1D;
// Check if not valid animation binded
if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (data.Count == 0)
break;
// Get axis X
@@ -1300,7 +1300,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
// Find 2 animations to blend (line)
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS - 1; i++)
for (int32 i = 0; i < data.Count - 1; i++)
{
const auto a = data.IndicesSorted[i];
const auto b = data.IndicesSorted[i + 1];
@@ -1310,14 +1310,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto aData = node->Values[4 + a * 2].AsFloat4();
// Check single A case or the last valid animation
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_INVALID)
{
value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W);
break;
}
// Get B animation data
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS);
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_INVALID);
const auto bAnim = node->Assets[b].As<Animation>();
auto bData = node->Values[4 + b * 2].AsFloat4();
@@ -1365,7 +1365,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto& data = node->Data.MultiBlend2D;
// Check if not valid animation binded
if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (data.TrianglesCount == 0)
break;
// Get axis X
@@ -1406,20 +1406,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
Float2 bestPoint;
float bestWeight = 0.0f;
byte bestAnims[2];
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS && data.TrianglesP0[i] != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
for (int32 i = 0, t = 0; i < data.TrianglesCount; i++)
{
// Get A animation data
const auto a = data.TrianglesP0[i];
const auto a = data.Triangles[t++];
const auto aAnim = node->Assets[a].As<Animation>();
const auto aData = node->Values[4 + a * 2].AsFloat4();
// Get B animation data
const auto b = data.TrianglesP1[i];
const auto b = data.Triangles[t++];
const auto bAnim = node->Assets[b].As<Animation>();
const auto bData = node->Values[4 + b * 2].AsFloat4();
// Get C animation data
const auto c = data.TrianglesP2[i];
const auto c = data.Triangles[t++];
const auto cAnim = node->Assets[c].As<Animation>();
const auto cData = node->Values[4 + c * 2].AsFloat4();