Hi, thank you for replying!
I was a bit confused about this whole topic when I made my initial post, but I think that some things have cleared up for me now. Thus, I’ll try to answer your questions and make this post more comprehensible
1) Inverse Constraints
I’m not entirely sure whether InverseRigConstraints are the way to go in this case, but the more I think about it the more I’m convinced that it should fit the purpose of my project.
I’m guessing that I could use a RigConstraint instead of the Monobehaviour component I’m saving my needed values in right now, setting up an InverseConstraint for the baking process.
2) The Recording Process
Yes, I can bind my component’s properties to the GameObjectRecorder without them needing to be of the types ‘FloatProperty’, ‘Vector3Property’, etc… The recorded clip contains only empty bindings though, meaning they all have a flat FloatCurve of value 0. Also, the bound properties don’t change within the Inspector during Animation Preview in the Editor.
Keep in mind that the bound values are calculated and set within a custom constraint I made. The following is the ConstraintJob code:
using Unity.Burst;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Animations.Rigging;
// Reads the Transform values of an animated rig during Animation Preview
// in order to calculate the necessary IKAnimValues.
// Outputs those values to a dedicated component whose values are bound to
// a GameObjectRecorder, resulting in the IKAnimClip.
[BurstCompile]
public struct IKAnimSetterConstraintJob : IWeightedAnimationJob
{
public ReadOnlyTransformHandle RigRoot;
public ReadOnlyTransformHandle LegRootL;
public ReadOnlyTransformHandle LegTipL;
public IKAnimValues IKAnimComp;
public float LegLMaxReach;
public FloatProperty jobWeight { get; set; }
public void ProcessRootMotion(AnimationStream stream) { }
public void ProcessAnimation(AnimationStream stream)
{
// Inputs
var legRootLPos = LegRootL.GetPosition(stream);
var legTipLPos = LegTipL.GetPosition(stream);
// Calculate Outputs
var rigRootPos = RigRoot.GetPosition(stream);
var rigRootRot = RigRoot.GetRotation(stream);
var legLDir = (legTipLPos - legRootLPos).normalized;
float legLScalar = Vector3.Distance(legRootLPos, legTipLPos) / LegLMaxReach;
// Set Outputs
IKAnimComp.RootPos = rigRootPos;
IKAnimComp.RootRot = rigRootRot;
IKAnimComp.LegLDir = legLDir;
IKAnimComp.LegLScalar = legLScalar;
}
}
My assumption is that the recorded animation only contains flat 0 curves because I’m not yet using an EvaluationGraph containing my constraint in the recording process (due to me thinking that InverseConstraints aren’t fit for this purpose, but they probably are).
But I’m not sure why the values don’t change during Animation Preview when I’m in the Editor. This is what lead me to believe that I needed FloatProperties instead of floats, etc…
The following code shows the recording process (called by a button press in the Inspector):
using System;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
public class GORecorderTest : MonoBehaviour
{
// Need this field because I can't get AnimationWindowReflection.activeAnimationClip to work
public AnimationClip SrcClip;
public IKAnimValues PropertiesComp;
public void RecordIKAnimClip()
{
if (!AnimationMode.InAnimationMode())
throw new ArgumentException("TEST_AnimationMode must be active during bake operation");
var animator = GetComponent<Animator>();
var recorder = new GameObjectRecorder(animator.gameObject);
// Get and bind properties
EditorCurveBinding[] bindings = PropertiesComp.GetBindings();
foreach (var binding in bindings)
recorder.Bind(binding);
// Recording Process
var frameCount = (int)(SrcClip.length * SrcClip.frameRate);
float dt = 1f / SrcClip.frameRate;
//float time = 0f;
//graph?.Evaluate(0f);
recorder.TakeSnapshot(0f);
for (int frame = 1; frame <= frameCount; ++frame)
{
//time = frame / clip.frameRate;
//graph?.Evaluate(time);
recorder.TakeSnapshot(dt);
}
// Save recorded animation in same folder as source animation
var ikAnimClip = new AnimationClip();
recorder.SaveToClip(ikAnimClip, SrcClip.frameRate);
string[] pathFragments = AssetDatabase.GetAssetPath(SrcClip).Split('/');
string path = "";
for (int i = 0; i < pathFragments.Length - 1; i++)
path += pathFragments[i] + "/";
path += "IKAnimClip.anim";
AssetDatabase.CreateAsset(ikAnimClip, path);
}
}
and this is the IKAnimValues component:
using UnityEngine;
using UnityEngine.Animations.Rigging;
using UnityEditor;
public class IKAnimValues : MonoBehaviour
{
public Vector3 RootPos;
public Quaternion RootRot;
[Space]
public Vector3 LegLDir;
public float LegLScalar;
public EditorCurveBinding[] GetBindings()
{
EditorCurveBinding[] bindings = new EditorCurveBinding[11];
bindings[0] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootPos.x");
bindings[1] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootPos.y");
bindings[2] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootPos.z");
bindings[3] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootRot.x");
bindings[4] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootRot.y");
bindings[5] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootRot.z");
bindings[6] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "RootRot.w");
bindings[7] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "LegLDir.x");
bindings[8] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "LegLDir.y");
bindings[9] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "LegLDir.z");
bindings[10] = EditorCurveBinding.FloatCurve("", typeof(IKAnimValues), "LegLScalar");
return bindings;
}
}
Again, thank you!