mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-01-17 11:50:11 +08:00
fix: shape changer would not generate animations when controlled only by object toggle (#951)
This commit is contained in:
parent
098a85af50
commit
9a974f5f09
@ -97,30 +97,7 @@ namespace nadena.dev.modular_avatar.animation
|
|||||||
fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray();
|
fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public string GetActiveSelfProxy(GameObject obj)
|
||||||
/// Returns a parameter which proxies the "activeSelf" state of the specified GameObject.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="paramName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
public bool TryGetActiveSelfProxy(GameObject obj, out string paramName)
|
|
||||||
{
|
|
||||||
if (_selfProxies.TryGetValue(obj, out paramName)) return !string.IsNullOrEmpty(paramName);
|
|
||||||
|
|
||||||
var path = PathMappings.GetObjectIdentifier(obj);
|
|
||||||
var clips = AnimationDatabase.ClipsForPath(path);
|
|
||||||
if (clips == null || clips.IsEmpty)
|
|
||||||
{
|
|
||||||
_selfProxies[obj] = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
paramName = _readableProperty.ForActiveSelf(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ForceGetActiveSelfProxy(GameObject obj)
|
|
||||||
{
|
{
|
||||||
if (_selfProxies.TryGetValue(obj, out var paramName) && !string.IsNullOrEmpty(paramName)) return paramName;
|
if (_selfProxies.TryGetValue(obj, out var paramName) && !string.IsNullOrEmpty(paramName)) return paramName;
|
||||||
|
|
||||||
@ -131,5 +108,12 @@ namespace nadena.dev.modular_avatar.animation
|
|||||||
|
|
||||||
return paramName;
|
return paramName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ObjectHasAnimations(GameObject obj)
|
||||||
|
{
|
||||||
|
var path = PathMappings.GetObjectIdentifier(obj);
|
||||||
|
var clips = AnimationDatabase.ClipsForPath(path);
|
||||||
|
return clips != null && !clips.IsEmpty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,15 @@
|
|||||||
namespace nadena.dev.modular_avatar.core.editor
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
{
|
{
|
||||||
internal struct ControlCondition
|
internal class ControlCondition
|
||||||
{
|
{
|
||||||
public string Parameter, DebugName;
|
public string Parameter, DebugName;
|
||||||
public bool IsConstant;
|
public bool IsConstant;
|
||||||
public float ParameterValueLo, ParameterValueHi, InitialValue;
|
public float ParameterValueLo, ParameterValueHi, InitialValue;
|
||||||
public bool InitiallyActive => InitialValue > ParameterValueLo && InitialValue < ParameterValueHi;
|
public bool InitiallyActive => InitialValue > ParameterValueLo && InitialValue < ParameterValueHi;
|
||||||
|
public bool IsConstantActive => InitiallyActive && IsConstant;
|
||||||
|
|
||||||
|
public GameObject ReferenceObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -133,30 +133,17 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
while (cursor != null && !RuntimeUtil.IsAvatarRoot(cursor))
|
while (cursor != null && !RuntimeUtil.IsAvatarRoot(cursor))
|
||||||
{
|
{
|
||||||
if (asc.TryGetActiveSelfProxy(cursor.gameObject, out var paramName))
|
conditions.Add(new ControlCondition
|
||||||
conditions.Add(new ControlCondition
|
{
|
||||||
{
|
Parameter = asc.GetActiveSelfProxy(cursor.gameObject),
|
||||||
Parameter = paramName,
|
DebugName = cursor.gameObject.name,
|
||||||
DebugName = cursor.gameObject.name,
|
IsConstant = false,
|
||||||
IsConstant = false,
|
InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
|
||||||
InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
|
ParameterValueLo = 0.5f,
|
||||||
ParameterValueLo = 0.5f,
|
ParameterValueHi = 1.5f,
|
||||||
ParameterValueHi = 1.5f
|
ReferenceObject = cursor.gameObject
|
||||||
});
|
});
|
||||||
else if (!cursor.gameObject.activeSelf)
|
|
||||||
conditions = new List<ControlCondition>
|
|
||||||
{
|
|
||||||
new ControlCondition
|
|
||||||
{
|
|
||||||
Parameter = "",
|
|
||||||
DebugName = cursor.gameObject.name,
|
|
||||||
IsConstant = true,
|
|
||||||
InitialValue = 0,
|
|
||||||
ParameterValueLo = 0.5f,
|
|
||||||
ParameterValueHi = 1.5f
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var mami in cursor.GetComponents<ModularAvatarMenuItem>())
|
foreach (var mami in cursor.GetComponents<ModularAvatarMenuItem>())
|
||||||
conditions.Add(ParameterAssignerPass.AssignMenuItemParameter(context, mami));
|
conditions.Add(ParameterAssignerPass.AssignMenuItemParameter(context, mami));
|
||||||
|
|
||||||
@ -178,6 +165,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
public bool IsDelete;
|
public bool IsDelete;
|
||||||
|
|
||||||
public bool IsConstant => ControllingConditions.Count == 0 || ControllingConditions.All(c => c.IsConstant);
|
public bool IsConstant => ControllingConditions.Count == 0 || ControllingConditions.All(c => c.IsConstant);
|
||||||
|
public bool IsConstantOn => IsConstant && InitiallyActive;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@ -198,6 +186,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
private readonly ndmf.BuildContext context;
|
private readonly ndmf.BuildContext context;
|
||||||
private Dictionary<string, float> initialValues = new();
|
private Dictionary<string, float> initialValues = new();
|
||||||
|
|
||||||
|
// Properties that are being driven, either by foreign animations or Object Toggles
|
||||||
|
private HashSet<string> activeProps = new();
|
||||||
|
|
||||||
private AnimationClip _initialStateClip;
|
private AnimationClip _initialStateClip;
|
||||||
|
|
||||||
public PropertyOverlayPass(ndmf.BuildContext context)
|
public PropertyOverlayPass(ndmf.BuildContext context)
|
||||||
@ -209,6 +200,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
Dictionary<TargetProp, PropGroup> shapes = FindShapes(context);
|
Dictionary<TargetProp, PropGroup> shapes = FindShapes(context);
|
||||||
FindObjectToggles(shapes, context);
|
FindObjectToggles(shapes, context);
|
||||||
|
|
||||||
|
AnalyzeConstants(shapes);
|
||||||
|
|
||||||
PreprocessShapes(shapes, out var initialStates, out var deletedShapes);
|
PreprocessShapes(shapes, out var initialStates, out var deletedShapes);
|
||||||
|
|
||||||
@ -223,6 +216,38 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
ProcessMeshDeletion(deletedShapes);
|
ProcessMeshDeletion(deletedShapes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AnalyzeConstants(Dictionary<TargetProp, PropGroup> shapes)
|
||||||
|
{
|
||||||
|
HashSet<GameObject> toggledObjects = new();
|
||||||
|
|
||||||
|
foreach (var targetProp in shapes.Keys)
|
||||||
|
if (targetProp is { TargetObject: GameObject go, PropertyName: "m_IsActive" })
|
||||||
|
toggledObjects.Add(go);
|
||||||
|
|
||||||
|
foreach (var group in shapes.Values)
|
||||||
|
{
|
||||||
|
foreach (var actionGroup in group.actionGroups)
|
||||||
|
{
|
||||||
|
foreach (var condition in actionGroup.ControllingConditions)
|
||||||
|
if (condition.ReferenceObject != null && !toggledObjects.Contains(condition.ReferenceObject))
|
||||||
|
condition.IsConstant = true;
|
||||||
|
|
||||||
|
var firstAlwaysOn =
|
||||||
|
actionGroup.ControllingConditions.FindIndex(c => c.InitiallyActive && c.IsConstant);
|
||||||
|
if (firstAlwaysOn > 0) actionGroup.ControllingConditions.RemoveRange(0, firstAlwaysOn - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any action groups with always-off conditions
|
||||||
|
group.actionGroups.RemoveAll(agk =>
|
||||||
|
agk.ControllingConditions.Any(c => !c.InitiallyActive && c.IsConstant));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove shapes with no action groups
|
||||||
|
foreach (var kvp in shapes.ToList())
|
||||||
|
if (kvp.Value.actionGroups.Count == 0)
|
||||||
|
shapes.Remove(kvp.Key);
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessInitialAnimatorVariables(Dictionary<TargetProp, PropGroup> shapes)
|
private void ProcessInitialAnimatorVariables(Dictionary<TargetProp, PropGroup> shapes)
|
||||||
{
|
{
|
||||||
foreach (var group in shapes.Values)
|
foreach (var group in shapes.Values)
|
||||||
@ -254,7 +279,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
var deletions = info.actionGroups.Where(agk => agk.IsDelete).ToList();
|
var deletions = info.actionGroups.Where(agk => agk.IsDelete).ToList();
|
||||||
if (deletions.Any(d => d.ControllingConditions.Count == 0))
|
if (deletions.Any(d => d.ControllingConditions.All(c => c.IsConstantActive)))
|
||||||
{
|
{
|
||||||
// always deleted
|
// always deleted
|
||||||
shapes.Remove(key);
|
shapes.Remove(key);
|
||||||
@ -589,11 +614,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
if (key.TargetObject is GameObject obj && key.PropertyName == "m_IsActive")
|
if (key.TargetObject is GameObject obj && key.PropertyName == "m_IsActive")
|
||||||
{
|
{
|
||||||
var asc = context.Extension<AnimationServicesContext>();
|
var asc = context.Extension<AnimationServicesContext>();
|
||||||
if (asc.TryGetActiveSelfProxy(obj, out var propName))
|
var propName = asc.GetActiveSelfProxy(obj);
|
||||||
{
|
binding = EditorCurveBinding.FloatCurve("", typeof(Animator), propName);
|
||||||
binding = EditorCurveBinding.FloatCurve("", typeof(Animator), propName);
|
AnimationUtility.SetEditorCurve(clip, binding, curve);
|
||||||
AnimationUtility.SetEditorCurve(clip, binding, curve);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return clip;
|
return clip;
|
||||||
@ -683,7 +706,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
// Make sure we generate an animator prop for each controlled object, as we intend to generate
|
// Make sure we generate an animator prop for each controlled object, as we intend to generate
|
||||||
// animations for them.
|
// animations for them.
|
||||||
asc.ForceGetActiveSelfProxy(target);
|
asc.GetActiveSelfProxy(target);
|
||||||
|
|
||||||
var key = new TargetProp
|
var key = new TargetProp
|
||||||
{
|
{
|
||||||
@ -700,16 +723,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
var value = obj.Active ? 1 : 0;
|
var value = obj.Active ? 1 : 0;
|
||||||
var action = new ActionGroupKey(context, key, toggle.gameObject, value);
|
var action = new ActionGroupKey(context, key, toggle.gameObject, value);
|
||||||
|
|
||||||
if (action.IsConstant)
|
|
||||||
{
|
|
||||||
if (action.InitiallyActive)
|
|
||||||
// always active control
|
|
||||||
group.actionGroups.Clear();
|
|
||||||
else
|
|
||||||
// never active control
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.actionGroups.Count == 0)
|
if (group.actionGroups.Count == 0)
|
||||||
group.actionGroups.Add(action);
|
group.actionGroups.Add(action);
|
||||||
else if (!group.actionGroups[^1].TryMerge(action)) group.actionGroups.Add(action);
|
else if (!group.actionGroups[^1].TryMerge(action)) group.actionGroups.Add(action);
|
||||||
@ -730,7 +743,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
var renderer = changer.targetRenderer.Get(changer)?.GetComponent<SkinnedMeshRenderer>();
|
var renderer = changer.targetRenderer.Get(changer)?.GetComponent<SkinnedMeshRenderer>();
|
||||||
if (renderer == null) continue;
|
if (renderer == null) continue;
|
||||||
|
|
||||||
int rendererInstanceId = renderer.GetInstanceID();
|
|
||||||
var mesh = renderer.sharedMesh;
|
var mesh = renderer.sharedMesh;
|
||||||
|
|
||||||
if (mesh == null) continue;
|
if (mesh == null) continue;
|
||||||
@ -774,21 +786,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
|
if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
|
||||||
|
|
||||||
// TODO: lift controlling object resolution out of loop?
|
|
||||||
if (action.IsConstant)
|
|
||||||
{
|
|
||||||
if (action.InitiallyActive)
|
|
||||||
{
|
|
||||||
// always active control
|
|
||||||
info.actionGroups.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// never active control
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Log("Trying merge: " + action);
|
Debug.Log("Trying merge: " + action);
|
||||||
if (info.actionGroups.Count == 0)
|
if (info.actionGroups.Count == 0)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user