chore: automatically save assets referenced at the end of processing

This commit is contained in:
bd_ 2023-07-30 00:35:07 +09:00
parent dec90cb6ca
commit 7ce6d93083
6 changed files with 87 additions and 29 deletions

View File

@ -31,8 +31,6 @@ namespace nadena.dev.modular_avatar.core.editor
private Dictionary<string, HashSet<ClipHolder>> _pathToClip = private Dictionary<string, HashSet<ClipHolder>> _pathToClip =
new Dictionary<string, HashSet<ClipHolder>>(); new Dictionary<string, HashSet<ClipHolder>>();
private HashSet<BlendTree> _processedBlendTrees = new HashSet<BlendTree>();
internal void Commit() internal void Commit()
{ {
foreach (var clip in _clips) foreach (var clip in _clips)
@ -115,11 +113,10 @@ namespace nadena.dev.modular_avatar.core.editor
// Protect the original animations from mutations by creating temporary clones; in the case of a proxy // Protect the original animations from mutations by creating temporary clones; in the case of a proxy
// animation, we'll restore the original in a later pass // animation, we'll restore the original in a later pass
var placeholder = Object.Instantiate(state.motion); var placeholder = Object.Instantiate(state.motion);
AssetDatabase.AddObjectToAsset(placeholder, state);
clipHolder.CurrentClip = placeholder; clipHolder.CurrentClip = placeholder;
if (isProxyAnim) if (isProxyAnim)
{ {
_clipCommitActions.Add(() => { Object.DestroyImmediate(placeholder, true); }); _clipCommitActions.Add(() => { Object.DestroyImmediate(placeholder); });
} }
} }
@ -177,16 +174,6 @@ namespace nadena.dev.modular_avatar.core.editor
processClip(holder); processClip(holder);
recordPaths(holder); recordPaths(holder);
_clips.Add(holder); _clips.Add(holder);
_clipCommitActions.Add(() =>
{
if (holder.CurrentClip != holder.OriginalClip)
{
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(holder.CurrentClip)))
{
AssetDatabase.AddObjectToAsset(holder.CurrentClip, AssetDatabase.GetAssetPath(state));
}
}
});
break; break;
} }
case BlendTree tree: case BlendTree tree:
@ -260,10 +247,6 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
children[i].motion = curClip; children[i].motion = curClip;
dirty = true; dirty = true;
if (string.IsNullOrWhiteSpace(AssetDatabase.GetAssetPath(curClip)))
{
AssetDatabase.AddObjectToAsset(curClip, AssetDatabase.GetAssetPath(state));
}
} }
} }

View File

@ -37,6 +37,7 @@ namespace nadena.dev.modular_avatar.core.editor
internal class AnimatorCombiner internal class AnimatorCombiner
{ {
private readonly AnimatorController _combined; private readonly AnimatorController _combined;
private bool isSaved;
private AnimatorOverrideController _overrideController; private AnimatorOverrideController _overrideController;
@ -55,9 +56,11 @@ namespace nadena.dev.modular_avatar.core.editor
private int controllerBaseLayer = 0; private int controllerBaseLayer = 0;
public AnimatorCombiner(BuildContext context) public AnimatorCombiner(BuildContext context, String assetName)
{ {
_combined = context.CreateAnimator(); _combined = context.CreateAnimator();
isSaved = !string.IsNullOrEmpty(AssetDatabase.GetAssetPath(_combined));
_combined.name = assetName;
} }
public AnimatorController Finish() public AnimatorController Finish()
@ -289,7 +292,10 @@ namespace nadena.dev.modular_avatar.core.editor
AnimationClip newClip = new AnimationClip(); AnimationClip newClip = new AnimationClip();
newClip.name = "rebased " + clip.name; newClip.name = "rebased " + clip.name;
AssetDatabase.AddObjectToAsset(newClip, _combined); if (isSaved)
{
AssetDatabase.AddObjectToAsset(newClip, _combined);
}
foreach (var binding in AnimationUtility.GetCurveBindings(clip)) foreach (var binding in AnimationUtility.GetCurveBindings(clip))
{ {
@ -396,7 +402,10 @@ namespace nadena.dev.modular_avatar.core.editor
cloneMap[original] = obj; cloneMap[original] = obj;
AssetDatabase.AddObjectToAsset(obj, _combined); if (isSaved)
{
AssetDatabase.AddObjectToAsset(obj, _combined);
}
SerializedObject so = new SerializedObject(obj); SerializedObject so = new SerializedObject(obj);
SerializedProperty prop = so.GetIterator(); SerializedProperty prop = so.GetIterator();

View File

@ -211,11 +211,15 @@ namespace nadena.dev.modular_avatar.core.editor
new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(avatarGameObject, context); new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(avatarGameObject, context);
PhysboneBlockerPass.Process(avatarGameObject); PhysboneBlockerPass.Process(avatarGameObject);
context.CommitReferencedAssets();
AfterProcessing?.Invoke(avatarGameObject, context); AfterProcessing?.Invoke(avatarGameObject, context);
context.AnimationDatabase.Commit(); context.AnimationDatabase.Commit();
new GCGameObjectsPass(context, avatarGameObject).OnPreprocessAvatar(); new GCGameObjectsPass(context, avatarGameObject).OnPreprocessAvatar();
context.CommitReferencedAssets();
} }
finally finally
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using UnityEditor; using UnityEditor;
using UnityEditor.Animations; using UnityEditor.Animations;
@ -16,6 +17,8 @@ namespace nadena.dev.modular_avatar.core.editor
internal readonly AnimationDatabase AnimationDatabase = new AnimationDatabase(); internal readonly AnimationDatabase AnimationDatabase = new AnimationDatabase();
internal readonly UnityEngine.Object AssetContainer; internal readonly UnityEngine.Object AssetContainer;
private bool SaveImmediate = false;
internal readonly Dictionary<VRCExpressionsMenu, VRCExpressionsMenu> ClonedMenus internal readonly Dictionary<VRCExpressionsMenu, VRCExpressionsMenu> ClonedMenus
= new Dictionary<VRCExpressionsMenu, VRCExpressionsMenu>(); = new Dictionary<VRCExpressionsMenu, VRCExpressionsMenu>();
@ -41,7 +44,7 @@ namespace nadena.dev.modular_avatar.core.editor
public void SaveAsset(Object obj) public void SaveAsset(Object obj)
{ {
if (AssetDatabase.IsMainAsset(obj) || AssetDatabase.IsSubAsset(obj)) return; if (!SaveImmediate || AssetDatabase.IsMainAsset(obj) || AssetDatabase.IsSubAsset(obj)) return;
AssetDatabase.AddObjectToAsset(obj, AssetContainer); AssetDatabase.AddObjectToAsset(obj, AssetContainer);
} }
@ -67,7 +70,7 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
if (controller == null) return null; if (controller == null) return null;
var merger = new AnimatorCombiner(this); var merger = new AnimatorCombiner(this, controller.name + " (clone)");
switch (controller) switch (controller)
{ {
case AnimatorController ac: case AnimatorController ac:
@ -85,7 +88,7 @@ namespace nadena.dev.modular_avatar.core.editor
public AnimatorController ConvertAnimatorController(AnimatorOverrideController overrideController) public AnimatorController ConvertAnimatorController(AnimatorOverrideController overrideController)
{ {
var merger = new AnimatorCombiner(this); var merger = new AnimatorCombiner(this, overrideController.name + " (clone)");
merger.AddOverrideController("", overrideController, null); merger.AddOverrideController("", overrideController, null);
return merger.Finish(); return merger.Finish();
} }
@ -122,5 +125,64 @@ namespace nadena.dev.modular_avatar.core.editor
return newMenu; return newMenu;
} }
public void CommitReferencedAssets()
{
HashSet<UnityEngine.Object> referencedAssets = new HashSet<UnityEngine.Object>();
HashSet<UnityEngine.Object> sceneAssets = new HashSet<UnityEngine.Object>();
Walk(AvatarDescriptor.gameObject);
foreach (var asset in referencedAssets
.Where(o => !sceneAssets.Contains(o))
.Where(o => string.IsNullOrEmpty(AssetDatabase.GetAssetPath(o))))
{
AssetDatabase.AddObjectToAsset(asset, AssetContainer);
}
SaveImmediate = true;
void Walk(GameObject obj)
{
sceneAssets.Add(obj);
foreach (var component in obj.GetComponents<Component>())
{
sceneAssets.Add(component);
if (component is Transform t)
{
foreach (Transform child in t)
{
Walk(child.gameObject);
}
}
var so = new SerializedObject(component);
var sp = so.GetIterator();
bool enterChildren = true;
while (sp.Next(enterChildren))
{
enterChildren = true;
if (sp.name == "m_GameObject") continue;
if (sp.propertyType == SerializedPropertyType.String)
{
enterChildren = false;
continue;
}
if (sp.propertyType != SerializedPropertyType.ObjectReference)
{
continue;
}
if (sp.objectReferenceValue != null)
{
referencedAssets.Add(sp.objectReferenceValue);
}
}
}
}
}
} }
} }

View File

@ -92,11 +92,11 @@ namespace nadena.dev.modular_avatar.core.editor
if (!mergeSessions.TryGetValue(merge.layerType, out var session)) if (!mergeSessions.TryGetValue(merge.layerType, out var session))
{ {
session = new AnimatorCombiner(context); session = new AnimatorCombiner(context, merge.layerType.ToString() + " (merged)");
mergeSessions[merge.layerType] = session; mergeSessions[merge.layerType] = session;
if (defaultControllers_.ContainsKey(merge.layerType)) if (defaultControllers_.TryGetValue(merge.layerType, out var defaultController))
{ {
session.AddController("", defaultControllers_[merge.layerType], null); session.AddController("", defaultController, null);
} }
} }
@ -142,7 +142,7 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
// For non-default layers, ensure we always clone the controller for the benefit of subsequent // For non-default layers, ensure we always clone the controller for the benefit of subsequent
// processing phases // processing phases
mergeSessions[layer.type] = new AnimatorCombiner(_context); mergeSessions[layer.type] = new AnimatorCombiner(_context, layer.type.ToString());
mergeSessions[layer.type].AddController("", controller, null); mergeSessions[layer.type].AddController("", controller, null);
} }
} }

View File

@ -195,7 +195,7 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
var path = AssetDatabase.GetAssetPath(obj); var path = AssetDatabase.GetAssetPath(obj);
return path != null && path.StartsWith(GetGeneratedAssetsFolder() + "/"); return string.IsNullOrEmpty(path) || path.StartsWith(GetGeneratedAssetsFolder() + "/");
} }
public static Type FindType(string typeName) public static Type FindType(string typeName)