diff --git a/Editor/Animation/AnimationServicesContext.cs b/Editor/Animation/AnimationServicesContext.cs
index e00231e5..95d68ec3 100644
--- a/Editor/Animation/AnimationServicesContext.cs
+++ b/Editor/Animation/AnimationServicesContext.cs
@@ -97,30 +97,7 @@ namespace nadena.dev.modular_avatar.animation
fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray();
}
- ///
- /// Returns a parameter which proxies the "activeSelf" state of the specified GameObject.
- ///
- ///
- ///
- ///
- ///
- 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)
+ public string GetActiveSelfProxy(GameObject obj)
{
if (_selfProxies.TryGetValue(obj, out var paramName) && !string.IsNullOrEmpty(paramName)) return paramName;
@@ -131,5 +108,12 @@ namespace nadena.dev.modular_avatar.animation
return paramName;
}
+
+ public bool ObjectHasAnimations(GameObject obj)
+ {
+ var path = PathMappings.GetObjectIdentifier(obj);
+ var clips = AnimationDatabase.ClipsForPath(path);
+ return clips != null && !clips.IsEmpty;
+ }
}
}
\ No newline at end of file
diff --git a/Editor/ReactiveObjects/ControlCondition.cs b/Editor/ReactiveObjects/ControlCondition.cs
index ba9904c8..de90bfb5 100644
--- a/Editor/ReactiveObjects/ControlCondition.cs
+++ b/Editor/ReactiveObjects/ControlCondition.cs
@@ -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 bool IsConstant;
public float ParameterValueLo, ParameterValueHi, InitialValue;
public bool InitiallyActive => InitialValue > ParameterValueLo && InitialValue < ParameterValueHi;
+ public bool IsConstantActive => InitiallyActive && IsConstant;
+
+ public GameObject ReferenceObject;
}
}
\ No newline at end of file
diff --git a/Editor/ReactiveObjects/PropertyOverlayPass.cs b/Editor/ReactiveObjects/PropertyOverlayPass.cs
index 8d479303..db894d67 100644
--- a/Editor/ReactiveObjects/PropertyOverlayPass.cs
+++ b/Editor/ReactiveObjects/PropertyOverlayPass.cs
@@ -133,30 +133,17 @@ namespace nadena.dev.modular_avatar.core.editor
while (cursor != null && !RuntimeUtil.IsAvatarRoot(cursor))
{
- if (asc.TryGetActiveSelfProxy(cursor.gameObject, out var paramName))
- conditions.Add(new ControlCondition
- {
- Parameter = paramName,
- DebugName = cursor.gameObject.name,
- IsConstant = false,
- InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
- ParameterValueLo = 0.5f,
- ParameterValueHi = 1.5f
- });
- else if (!cursor.gameObject.activeSelf)
- conditions = new List
- {
- new ControlCondition
- {
- Parameter = "",
- DebugName = cursor.gameObject.name,
- IsConstant = true,
- InitialValue = 0,
- ParameterValueLo = 0.5f,
- ParameterValueHi = 1.5f
- }
- };
-
+ conditions.Add(new ControlCondition
+ {
+ Parameter = asc.GetActiveSelfProxy(cursor.gameObject),
+ DebugName = cursor.gameObject.name,
+ IsConstant = false,
+ InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
+ ParameterValueLo = 0.5f,
+ ParameterValueHi = 1.5f,
+ ReferenceObject = cursor.gameObject
+ });
+
foreach (var mami in cursor.GetComponents())
conditions.Add(ParameterAssignerPass.AssignMenuItemParameter(context, mami));
@@ -178,6 +165,7 @@ namespace nadena.dev.modular_avatar.core.editor
public bool IsDelete;
public bool IsConstant => ControllingConditions.Count == 0 || ControllingConditions.All(c => c.IsConstant);
+ public bool IsConstantOn => IsConstant && InitiallyActive;
public override string ToString()
{
@@ -198,6 +186,9 @@ namespace nadena.dev.modular_avatar.core.editor
private readonly ndmf.BuildContext context;
private Dictionary initialValues = new();
+ // Properties that are being driven, either by foreign animations or Object Toggles
+ private HashSet activeProps = new();
+
private AnimationClip _initialStateClip;
public PropertyOverlayPass(ndmf.BuildContext context)
@@ -209,6 +200,8 @@ namespace nadena.dev.modular_avatar.core.editor
{
Dictionary shapes = FindShapes(context);
FindObjectToggles(shapes, context);
+
+ AnalyzeConstants(shapes);
PreprocessShapes(shapes, out var initialStates, out var deletedShapes);
@@ -223,6 +216,38 @@ namespace nadena.dev.modular_avatar.core.editor
ProcessMeshDeletion(deletedShapes);
}
+ private void AnalyzeConstants(Dictionary shapes)
+ {
+ HashSet 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 shapes)
{
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();
- if (deletions.Any(d => d.ControllingConditions.Count == 0))
+ if (deletions.Any(d => d.ControllingConditions.All(c => c.IsConstantActive)))
{
// always deleted
shapes.Remove(key);
@@ -589,11 +614,9 @@ namespace nadena.dev.modular_avatar.core.editor
if (key.TargetObject is GameObject obj && key.PropertyName == "m_IsActive")
{
var asc = context.Extension();
- if (asc.TryGetActiveSelfProxy(obj, out var propName))
- {
- binding = EditorCurveBinding.FloatCurve("", typeof(Animator), propName);
- AnimationUtility.SetEditorCurve(clip, binding, curve);
- }
+ var propName = asc.GetActiveSelfProxy(obj);
+ binding = EditorCurveBinding.FloatCurve("", typeof(Animator), propName);
+ AnimationUtility.SetEditorCurve(clip, binding, curve);
}
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
// animations for them.
- asc.ForceGetActiveSelfProxy(target);
+ asc.GetActiveSelfProxy(target);
var key = new TargetProp
{
@@ -700,16 +723,6 @@ namespace nadena.dev.modular_avatar.core.editor
var value = obj.Active ? 1 : 0;
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)
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();
if (renderer == null) continue;
- int rendererInstanceId = renderer.GetInstanceID();
var mesh = renderer.sharedMesh;
if (mesh == null) continue;
@@ -774,21 +786,6 @@ namespace nadena.dev.modular_avatar.core.editor
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);
if (info.actionGroups.Count == 0)
{