diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index c659b475a..cbe9c3d85 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -28,6 +28,51 @@ namespace FlaxEditor.Surface.Archetypes } } + /// + /// Customized for Blend with Mask node. + /// + public class SkeletonMaskSample : SurfaceNode + { + private AssetSelect _assetSelect; + private Box _assetBox; + + /// + public SkeletonMaskSample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + } + + /// + public override void OnSurfaceLoaded(SurfaceNodeActions action) + { + base.OnSurfaceLoaded(action); + + if (Surface != null) + { + _assetSelect = GetChild(); + + // 4 is the id of skeleton mask parameter node. + if (TryGetBox(4, out var box)) + { + _assetBox = box; + _assetSelect.Visible = !_assetBox.HasAnyConnection; + } + } + } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + if (_assetBox == null) + return; + if (box.ID != _assetBox.ID) + return; + _assetSelect.Visible = !box.HasAnyConnection; + } + } + /// /// Customized for the animation sampling nodes /// @@ -76,7 +121,7 @@ namespace FlaxEditor.Surface.Archetypes Title = _assetBox.HasAnyConnection || asset == null ? "Animation" : asset.ShortName; else Title = asset?.ShortName ?? "Animation"; - + var style = Style.Current; Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); } @@ -524,7 +569,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 10, Title = "Blend Additive", - Description = + Description = "Blend animation poses (with additive mode)" + "\n" + "\nNote: " + @@ -552,6 +597,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 11, Title = "Blend with Mask", Description = "Blend animation poses using skeleton mask", + Create = (id, context, arch, groupArch) => new SkeletonMaskSample(id, context, arch, groupArch), Flags = NodeFlags.AnimGraph, Size = new Float2(180, 140), DefaultValues = new object[] @@ -565,7 +611,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, "Pose A", true, typeof(void), 1), NodeElementArchetype.Factory.Input(1, "Pose B", true, typeof(void), 2), NodeElementArchetype.Factory.Input(2, "Alpha", true, typeof(float), 3, 0), - NodeElementArchetype.Factory.Asset(0, 70, 1, typeof(SkeletonMask)), + NodeElementArchetype.Factory.Input(3, "Skeleton Mask Asset", true, typeof(SkeletonMask), 4), + NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 4, 1, typeof(SkeletonMask)), } }, new NodeArchetype diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index cdf1ebd84..d3b61c951 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -228,7 +228,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* trace.Value = animPos; trace.NodeId = node->ID; } - + // Evaluate nested animations bool hasNested = false; if (anim->NestedAnims.Count() != 0) @@ -1131,6 +1131,17 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { const float alpha = Math::Saturate((float)tryGetValue(node->GetBox(3), node->Values[0])); auto mask = node->Assets[0].As(); + auto maskAssetBox = node->GetBox(4); // 4 is the id of skeleton mask parameter node. + + // Check if have some mask asset connected with the mask node + if (maskAssetBox->HasConnection()) + { + const Value assetBoxValue = tryGetValue(maskAssetBox, Value::Null); + + // Use the mask connected with this node instead of default mask asset + if (assetBoxValue != Value::Null) + mask = (SkeletonMask*)assetBoxValue.AsAsset; + } // Only A or missing/invalid mask if (Math::NearEqual(alpha, 0.0f, ANIM_GRAPH_BLEND_THRESHOLD) || mask == nullptr || mask->WaitForLoaded())