-
Notifications
You must be signed in to change notification settings - Fork 8
-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WeightedMaskMixer feature. #154
Comments
I don't have much time to develop new features at the moment, but here's a quick implementation of the weighted mixer sample. Before you can use it you'll need to make the following changes:
Then you can import It only supports 2 layers and has a few other things that might be inconvenient to use, so please do let me know if you make any changes to it. |
Thanks for the heads up. I will integrate it later tonight and will let you know how it goes. |
Animancer v7.2 is now available with the necessary changes to simply add a weighted mask mixer. But I didn't actually add the mixer since it's not really suitable for general use with its current limitations. If this gets more interest I'll look into it more. |
For info, you mentioned that;
But I found this is still needed to be done:
and;
Although you do have the IsAdditive method. Without the #if UNITY_EDITOR directive I was getting spammed with errors in Editor play mode |
Looks like I have actually made some changes to the Weighted Mask Mixer since I first posted it so here's the new version compatible with Animancer v7.2: Exposing the |
Thanks :) I can confirm that the new import now works out of the box. The WeightedMaskMixerExample script has lost I've added it back in using Also the new example scene has a gameObject named 'DefaultHumanoid Regular Layers' which has a missing script on it. Not sure if it was intended as another example? |
Here's a re-upload with the weight field back in and removed that extra character. Not sure how it go in there, it was something completely unrelated. Animancer Weighted Mask Mixer v2.1.zip |
Animancer v7.3 is now available and has those changes in it. |
It has required a little bit of fiddling to get what I actually want, but I am impressed that it's even possible in unity. Things that I noticed:
So result looks like this (closest is naive, second is AvatarMask, furthest is customized WightedMaskLayer): The main idea of my modification is this: [Serializable]
public struct TransformWeight
{
public Transform transform;
public BoneState state;
}
public enum BoneState : byte
{
UseBaseLayerAnimation,
UseTopLayerAnimation,
Boundary,
}
public struct WeightedMaskMixerJob : IAnimationJob
{
void IAnimationJob.ProcessAnimation(AnimationStream stream)
{
var stream0 = stream.GetInputStream(0);
var stream1 = stream.GetInputStream(1);
if (stream1.isValid)
{
var handleCount = handles.Length;
for (var i = 0; i < handleCount; i++)
{
var handle = handles[i];
var state = boneWeights[i];
switch (state)
{
case BoneState.UseTopLayerAnimation:
{
var positionB = handle.GetLocalPosition(stream1);
var rotationB = handle.GetLocalRotation(stream1);
handle.SetLocalPosition(stream, positionB);
handle.SetLocalRotation(stream, rotationB);
break;
}
case BoneState.UseBaseLayerAnimation:
{
var positionA = handle.GetLocalPosition(stream0);
var rotationA = handle.GetLocalRotation(stream0);
handle.SetLocalPosition(stream, positionA);
handle.SetLocalRotation(stream, rotationA);
break;
}
case BoneState.Boundary:
{
var positionA = handle.GetLocalPosition(stream0);
var rotationB = handle.GetRotation(stream1);
handle.SetLocalPosition(stream, positionA);
handle.SetRotation(stream, rotationB);
break;
}
}
}
}
else
{
var handleCount = handles.Length;
for (var i = 0; i < handleCount; i++)
{
var handle = handles[i];
handle.SetLocalPosition(stream, handle.GetLocalPosition(stream0));
handle.SetLocalRotation(stream, handle.GetLocalRotation(stream0));
}
}
}
} Now I need to figure out how to do transitions, so animations do not pop. |
To do smooth transitions you would need to go back to using If you still need I'm considering making an Editor Window for configuring the system similar to the one I made for FlexiMotion where it shows all the Transforms under the character so you can tick the ones you want. Then each column on the right represents a group of weights so you can call |
I see you're using world rotation on the |
Night later I realized that it's possible to chim in boundary node correction after WeightedMaskMixerJob did its job: public struct WeightedMaskMixerJob : IAnimationJob
{
void IAnimationJob.ProcessAnimation(AnimationStream stream)
{
// ...
// Blending code
// ...
ApplyBoundaryNodeCorrection(ref stream, 11); // hardcoded boundaryNodeIndex
}
private void ApplyBoundaryNodeCorrection(ref AnimationStream stream, int boundaryNodeIndex)
{
var stream0 = stream.GetInputStream(0);
var stream1 = stream.GetInputStream(1);
if (!stream1.isValid)
{
return;
}
var layerWeight = stream.GetInputWeight(1);
var handle = handles[boundaryNodeIndex];
var localPositionA = handle.GetLocalPosition(stream0);
var localPositionB = handle.GetLocalPosition(stream1);
var rotationA = handle.GetRotation(stream0);
var rotationB = handle.GetRotation(stream1);
handle.SetLocalPosition(stream, Vector3.LerpUnclamped(localPositionA, localPositionB, layerWeight));
handle.SetRotation(stream, Quaternion.SlerpUnclamped(rotationA, rotationB, layerWeight));
}
} Which produces indistinguishable results. public class CharacterAnimator : MonoBehaviour
{
// ...
[SerializeField] private AnimancerComponent _animancer;
[SerializeField] private Transform _boundaryBone;
// ...
private AnimancerLayer _locomotionLayer;
private AnimancerLayer _actionLayer;
private WeightedMaskLayerList _weightedLayers;
private void Awake()
{
_weightedLayers = new WeightedMaskLayerList(_animancer);
_locomotionLayer = _animancer.Layers[0];
_actionLayer = _animancer.Layers[1];
SetupWeights();
var job = new BoundaryNodeCorrectionJob
{
BoundaryNodeHandle = _animancer.Animator.BindStreamTransform(_boundaryBone)
};
_animancer.Playable.InsertOutputJob(job);
}
//...
}
public struct BoundaryNodeCorrectionJob : IAnimationJob
{
public TransformStreamHandle BoundaryNodeHandle;
public void ProcessAnimation(AnimationStream stream)
{
var stream0 = stream.GetInputStream(0);
var stream1 = stream.GetInputStream(1);
if (!stream1.isValid)
{
return;
}
var layerWeight = stream.GetInputWeight(1);
var localPositionA = BoundaryNodeHandle.GetLocalPosition(stream0);
var localPositionB = BoundaryNodeHandle.GetLocalPosition(stream1);
var rotationA = BoundaryNodeHandle.GetRotation(stream0);
var rotationB = BoundaryNodeHandle.GetRotation(stream1);
BoundaryNodeHandle.SetLocalPosition(stream, Vector3.LerpUnclamped(localPositionA, localPositionB, layerWeight));
BoundaryNodeHandle.SetRotation(stream, Quaternion.SlerpUnclamped(rotationA, rotationB, layerWeight));
}
public void ProcessRootMotion(AnimationStream stream) { }
} The problem is... It doesn't work, stream1 is always invalid. I guess job like this requires more complicated setup. How can I do it? |
The mixer job combines its input streams into a single stream so a second job won't also be able to mix them differently. Best you could do would probably just be to have one struct use the other for the sake of encapsulation. |
Use Case
Hi, I need a way to properly set Humanoid Avatar masks. Instead of the whole trunk (or the whole head) I need to be able to selectivelly check what humanoid bones can affect the mask. We have a set of custom animations for our characters. Such as In-place reloading/shoting. We do have running, walk, strafing, crouching animations that are mixed with shoting/meleeing and reloading animations. The problem is that those animations are meant to be mixed not from the pelvis (otherwise the characters start shottings while swinging their bodies all over the place very weird), but instead from spine2 bone (the equivalent of the upper chest in Humanoid rig). But the mask doesn't allow us to chose individual bones inside the mask, is either the whole trunk or nothing.
As you suggested in this thread, there's a solution to control individual bones mask and weight.
Solution
Implement WeightedMaskMixer in Animancer
Alternatives
There's currently no other known alternative other than create an explosive combination of animations which forces us to be stuck with clunky unwanted mix of masked animations without fine grain control.
The text was updated successfully, but these errors were encountered: