modular-avatar/Editor/OptimizationPasses/ConstraintConverterPass.cs

115 lines
4.4 KiB
C#
Raw Normal View History

#nullable enable
using System.Collections.Generic;
2024-08-16 18:52:04 -07:00
using nadena.dev.ndmf;
using UnityEditor;
#if MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER
using UnityEngine;
using UnityEngine.Animations;
using VRC.SDK3.Avatars;
using System.Linq;
using nadena.dev.ndmf.animator;
2024-08-16 18:52:04 -07:00
using VRC.Dynamics;
#endif
namespace nadena.dev.modular_avatar.core.editor
{
internal class ConstraintConverterPass : Pass<ConstraintConverterPass>
{
#if MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER
[InitializeOnLoadMethod]
private static void Init()
{
AvatarDynamicsSetup.IsUnityConstraintAutoConverted += constraint =>
{
var component = constraint as Component;
if (component == null) return false;
var converted = component.GetComponentInParent<ModularAvatarConvertConstraints>();
return converted != null && RuntimeUtil.FindAvatarInParents(converted.transform) ==
RuntimeUtil.FindAvatarInParents(component.transform);
};
AvatarDynamicsSetup.OnConvertUnityConstraintsAcrossGameObjects += (constraints, isAutoFix) =>
{
if (!isAutoFix) return false;
var avatars = constraints.Select(c => RuntimeUtil.FindAvatarInParents(c.transform)).Distinct();
foreach (var avatar in avatars) Undo.AddComponent<ModularAvatarConvertConstraints>(avatar.gameObject);
return true;
};
}
protected override void Execute(ndmf.BuildContext context)
{
var converters = context.AvatarRootObject.GetComponentsInChildren<ModularAvatarConvertConstraints>(true)
.Select(c => c.gameObject)
.ToHashSet();
2024-08-16 18:52:04 -07:00
if (converters.Count == 0) return;
var constraintGameObjects = context.AvatarRootObject.GetComponentsInChildren<IConstraint>(true)
.Select(c => (c as Component)?.gameObject)
2025-03-21 20:20:23 -07:00
.Where(go => go != null)
2024-08-16 18:52:04 -07:00
.Distinct()
2025-03-21 20:20:23 -07:00
.Where(go => go!.GetComponentsInParent<ModularAvatarConvertConstraints>(true)
2024-08-16 18:52:04 -07:00
.Select(c => c.gameObject)
.Any(converters.Contains)
).ToArray();
var targetConstraintComponents =
2025-03-21 20:20:23 -07:00
constraintGameObjects.SelectMany(go => go!.GetComponents<IConstraint>()).ToArray();
2024-08-16 18:52:04 -07:00
AvatarDynamicsSetup.DoConvertUnityConstraints(targetConstraintComponents, null, false);
var asc = context.Extension<AnimatorServicesContext>();
2024-08-16 18:52:04 -07:00
// Also look for preexisting VRCConstraints so we can go fix up any broken animation clips from people who
// clicked auto fix :(
var existingVRCConstraints = converters.SelectMany(c => c.GetComponentsInChildren<VRCConstraintBase>(true))
.Select(c => c.gameObject)
.Distinct();
var targetPaths = constraintGameObjects
.Union(existingVRCConstraints)
2025-03-21 20:20:23 -07:00
.Select(c => asc.ObjectPathRemapper.GetVirtualPathForObject(c!))
2024-08-16 18:52:04 -07:00
.ToHashSet();
// Update animation clips
var clips = targetPaths.SelectMany(tp => asc.AnimationIndex.GetClipsForObjectPath(tp))
2024-08-16 18:52:04 -07:00
.ToHashSet();
foreach (var clip in clips) RemapSingleClip(clip, targetPaths);
}
private void RemapSingleClip(VirtualClip clip, HashSet<string> targetPaths)
2024-08-16 18:52:04 -07:00
{
var bindings = clip.GetFloatCurveBindings().ToList();
2024-08-16 18:52:04 -07:00
foreach (var ecb in bindings)
{
if (!targetPaths.Contains(ecb.path)) continue;
if (typeof(IConstraint).IsAssignableFrom(ecb.type))
if (AvatarDynamicsSetup.TryGetSubstituteAnimationBinding(ecb.type, ecb.propertyName,
out var newType, out var newProp, out var isArray))
{
var newBinding = new EditorCurveBinding
{
path = ecb.path,
type = newType,
propertyName = newProp
};
var curve = clip.GetFloatCurve(ecb);
clip.SetFloatCurve(newBinding, curve);
clip.SetFloatCurve(ecb, null);
2024-08-16 18:52:04 -07:00
}
}
}
#else
protected override void Execute(ndmf.BuildContext context) {}
#endif
}
}