mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 02:35:06 +08:00
feat: MA Convert Constraints (#1010)
This commit is contained in:
parent
d83c3351d7
commit
7384715059
8
.github/ProjectRoot/vpm-manifest-2022.json
vendored
8
.github/ProjectRoot/vpm-manifest-2022.json
vendored
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.vrchat.avatars": {
|
"com.vrchat.avatars": {
|
||||||
"version": "3.5.0"
|
"version": "3.7.0"
|
||||||
},
|
},
|
||||||
"nadena.dev.ndmf": {
|
"nadena.dev.ndmf": {
|
||||||
"version": "1.4.0"
|
"version": "1.4.0"
|
||||||
@ -9,13 +9,13 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"com.vrchat.avatars": {
|
"com.vrchat.avatars": {
|
||||||
"version": "3.6.1",
|
"version": "3.7.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.vrchat.base": "3.6.1"
|
"com.vrchat.base": "3.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"com.vrchat.base": {
|
"com.vrchat.base": {
|
||||||
"version": "3.6.1",
|
"version": "3.7.0",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
"nadena.dev.ndmf": {
|
"nadena.dev.ndmf": {
|
||||||
|
14
Editor/Inspector/MAConvertConstraintsEditor.cs
Normal file
14
Editor/Inspector/MAConvertConstraintsEditor.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(ModularAvatarConvertConstraints))]
|
||||||
|
[CanEditMultipleObjects]
|
||||||
|
internal class MAConvertConstraintsEditor : MAEditorBase
|
||||||
|
{
|
||||||
|
protected override void OnInnerInspectorGUI()
|
||||||
|
{
|
||||||
|
// no UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
Editor/Inspector/MAConvertConstraintsEditor.cs.meta
Normal file
3
Editor/Inspector/MAConvertConstraintsEditor.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 484ea04548b945ce9cf5fd6d49b50244
|
||||||
|
timeCreated: 1723778102
|
125
Editor/OptimizationPasses/ConstraintConverterPass.cs
Normal file
125
Editor/OptimizationPasses/ConstraintConverterPass.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
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.modular_avatar.animation;
|
||||||
|
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(new ObjectIdentityComparer<GameObject>());
|
||||||
|
if (converters.Count == 0) return;
|
||||||
|
|
||||||
|
var constraintGameObjects = context.AvatarRootObject.GetComponentsInChildren<IConstraint>(true)
|
||||||
|
.Select(c => (c as Component)?.gameObject)
|
||||||
|
.Distinct()
|
||||||
|
.Where(go => go.GetComponentsInParent<ModularAvatarConvertConstraints>(true)
|
||||||
|
.Select(c => c.gameObject)
|
||||||
|
.Any(converters.Contains)
|
||||||
|
).ToArray();
|
||||||
|
var targetConstraintComponents =
|
||||||
|
constraintGameObjects.SelectMany(go => go.GetComponents<IConstraint>()).ToArray();
|
||||||
|
|
||||||
|
AvatarDynamicsSetup.DoConvertUnityConstraints(targetConstraintComponents, null, false);
|
||||||
|
|
||||||
|
var asc = context.Extension<AnimationServicesContext>();
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
.Select(c => asc.PathMappings.GetObjectIdentifier(c))
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
// Update animation clips
|
||||||
|
var clips = targetPaths.SelectMany(tp => asc.AnimationDatabase.ClipsForPath(tp))
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
foreach (var clip in clips) RemapSingleClip(clip, targetPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemapSingleClip(AnimationDatabase.ClipHolder clip, HashSet<string> targetPaths)
|
||||||
|
{
|
||||||
|
var motion = clip.CurrentClip as AnimationClip;
|
||||||
|
if (motion == null) return;
|
||||||
|
|
||||||
|
var bindings = AnimationUtility.GetCurveBindings(motion);
|
||||||
|
var toUpdateBindings = new List<EditorCurveBinding>();
|
||||||
|
var toUpdateCurves = new List<AnimationCurve>();
|
||||||
|
|
||||||
|
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 = AnimationUtility.GetEditorCurve(motion, ecb);
|
||||||
|
if (curve != null)
|
||||||
|
{
|
||||||
|
toUpdateBindings.Add(newBinding);
|
||||||
|
toUpdateCurves.Add(curve);
|
||||||
|
|
||||||
|
toUpdateBindings.Add(ecb);
|
||||||
|
toUpdateCurves.Add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toUpdateBindings.Count == 0) return;
|
||||||
|
AnimationUtility.SetEditorCurves(motion, toUpdateBindings.ToArray(), toUpdateCurves.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
protected override void Execute(ndmf.BuildContext context) {}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5c172d4eac3d4902826a96656cf1ce34
|
||||||
|
timeCreated: 1723776385
|
@ -70,6 +70,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
||||||
#endif
|
#endif
|
||||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
seq.Run(GameObjectDelayDisablePass.Instance);
|
||||||
|
seq.Run(ConstraintConverterPass.Instance);
|
||||||
});
|
});
|
||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
seq.Run(MenuInstallPluginPass.Instance);
|
seq.Run(MenuInstallPluginPass.Instance);
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
"nadena.dev.ndmf",
|
"nadena.dev.ndmf",
|
||||||
"nadena.dev.ndmf.vrchat",
|
"nadena.dev.ndmf.vrchat",
|
||||||
"nadena.dev.ndmf.reactive-query.core",
|
"nadena.dev.ndmf.reactive-query.core",
|
||||||
"nadena.dev.ndmf.runtime"
|
"nadena.dev.ndmf.runtime",
|
||||||
|
"VRC.SDK3A.Editor"
|
||||||
],
|
],
|
||||||
"includePlatforms": [
|
"includePlatforms": [
|
||||||
"Editor"
|
"Editor"
|
||||||
@ -19,7 +20,6 @@
|
|||||||
"precompiledReferences": [
|
"precompiledReferences": [
|
||||||
"Newtonsoft.Json.dll",
|
"Newtonsoft.Json.dll",
|
||||||
"System.Collections.Immutable.dll",
|
"System.Collections.Immutable.dll",
|
||||||
"System.Memory.dll",
|
|
||||||
"VRCSDKBase.dll",
|
"VRCSDKBase.dll",
|
||||||
"VRCSDKBase-Editor.dll",
|
"VRCSDKBase-Editor.dll",
|
||||||
"VRCSDK3A.dll",
|
"VRCSDK3A.dll",
|
||||||
@ -48,6 +48,11 @@
|
|||||||
"name": "com.vrchat.avatars",
|
"name": "com.vrchat.avatars",
|
||||||
"expression": "3.5.2",
|
"expression": "3.5.2",
|
||||||
"define": "MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER"
|
"define": "MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.vrchat.avatars",
|
||||||
|
"expression": "3.7.0-beta.2",
|
||||||
|
"define": "MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"noEngineReferences": false
|
"noEngineReferences": false
|
||||||
|
15
Runtime/ModularAvatarConvertConstraints.cs
Normal file
15
Runtime/ModularAvatarConvertConstraints.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
[AddComponentMenu("Modular Avatar/MA Convert Constraints")]
|
||||||
|
#else
|
||||||
|
[AddComponentMenu("")]
|
||||||
|
#endif
|
||||||
|
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/convert-constraints?lang=auto")]
|
||||||
|
public class ModularAvatarConvertConstraints : AvatarTagComponent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
11
Runtime/ModularAvatarConvertConstraints.cs.meta
Normal file
11
Runtime/ModularAvatarConvertConstraints.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e362b3df8a3d478c82bf5ffe18f622e6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
17
docs~/docs/reference/convert-constraints.md
Normal file
17
docs~/docs/reference/convert-constraints.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Convert Constraints
|
||||||
|
|
||||||
|
The Convert Constraints component directs Modular Avatar to nondestructively convert Unity constraints to VRChat
|
||||||
|
constraints on build. It will convert any constraints (and animations referencing them) on the same object it is
|
||||||
|
attached to, and any children of that object. It will also attempt to fix animations broken by using VRCSDK's Auto Fix
|
||||||
|
button with older versions of Modular Avatar.
|
||||||
|
|
||||||
|
## When should I use this?
|
||||||
|
|
||||||
|
It's probably a good idea to put this on your avatar root in most cases, as preconverting constraints improves
|
||||||
|
performance significantly. When MA is installed, the VRChat Auto Fix button will automatically add this component to
|
||||||
|
your avatar root if it's not already there.
|
||||||
|
|
||||||
|
## When should I not use this?
|
||||||
|
|
||||||
|
This component is primarily provided to allow users to disable this functionality (by removing this component) if it is
|
||||||
|
suspected to be causing problems.
|
@ -0,0 +1,15 @@
|
|||||||
|
# Convert Constraints
|
||||||
|
|
||||||
|
Convert Constraintsコンポーネントは、ビルド時にUnityのConstraintsをVRChatのConstraintに非破壊的に変換するようにModular
|
||||||
|
Avatarに指示します。
|
||||||
|
アタッチされているオブジェクトとその子オブジェクトにあるConstraint、およびそれらを参照しているアニメーションを変換します。
|
||||||
|
また、VRCSDKのAuto Fixを古いバージョンのModular Avatarで使用して壊れたアニメーションも修正しようとします。
|
||||||
|
|
||||||
|
## いつ使うもの?
|
||||||
|
|
||||||
|
あらかじめ変換するとパフォーマンスが大幅に向上するため、ほとんどの場合はアバタールートにこれを配置するのが良いでしょう。MAがインストールされている場合、
|
||||||
|
VRChatのAuto Fixボタンは、アバタールートにこのコンポーネントがまだ存在しない場合、自動的にこのコンポーネントを追加します。
|
||||||
|
|
||||||
|
## 非推奨の場合
|
||||||
|
|
||||||
|
このコンポーネントは、問題の原因となる可能性がある場合に、このコンポーネントを削除してこの機能を無効にするために提供されています。
|
@ -15,7 +15,7 @@
|
|||||||
"com.unity.nuget.newtonsoft-json": "2.0.0"
|
"com.unity.nuget.newtonsoft-json": "2.0.0"
|
||||||
},
|
},
|
||||||
"vpmDependencies": {
|
"vpmDependencies": {
|
||||||
"com.vrchat.avatars": ">=3.6.1",
|
"com.vrchat.avatars": ">=3.7.0",
|
||||||
"nadena.dev.ndmf": ">=1.5.0-beta.3 <2.0.0-a"
|
"nadena.dev.ndmf": ">=1.5.0-beta.3 <2.0.0-a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user