From dc0a28c53d7959e5064a4a8d703184f2cb7b1f94 Mon Sep 17 00:00:00 2001 From: imb Date: Tue, 8 Feb 2022 12:45:43 -0500 Subject: [PATCH] Added Subscene capabilities to Timeline - Allows adding of any targets under atoms within the subscene - Supports IKCUA effector & bend goal detection to show the name of the bone being moved instead of the atom. --- .../Animatables/AnimatablesRegistry.cs | 58 ++++++++++++++----- .../FreeControllerV3s/FreeControllerV3Ref.cs | 45 ++++++++++++-- .../Operations/TargetsOperations.cs | 16 ++++- src/UI/Screens/AddRemoveTargetsScreen.cs | 56 +++++++++++++++--- 4 files changed, 144 insertions(+), 31 deletions(-) diff --git a/src/AtomAnimations/Animatables/AnimatablesRegistry.cs b/src/AtomAnimations/Animatables/AnimatablesRegistry.cs index 2496612f..23b632b1 100644 --- a/src/AtomAnimations/Animatables/AnimatablesRegistry.cs +++ b/src/AtomAnimations/Animatables/AnimatablesRegistry.cs @@ -45,25 +45,57 @@ public void RemoveStorableFloat(JSONStorableFloatRef t) public FreeControllerV3Ref GetOrCreateController(Atom atom, string controllerName) { - var t = _controllers.FirstOrDefault(x => x.Targets(controllerName)); - if (t != null) return t; - var controller = atom.freeControllers.FirstOrDefault(fc => fc.name == controllerName); - if (ReferenceEquals(controller, null)) + + bool subscene = false; + if (atom.type == "SubScene") { - SuperController.LogError($"Timeline: Atom '{atom.uid}' does not have a controller '{controllerName}'"); - return null; + string atomName = null; + string cuid = null; + subscene = true; + + if (controllerName.Contains("(")) //Will fail if someone puts a bracket in their atom name. + { + cuid = controllerName.Split('(')[0].Trim(')'); + atomName = controllerName.Split('(')[1].Trim(')'); + + if (!atomName.Contains("/")) + atomName = atom.containingAtom.name + "/" + atomName; + } + + var t = _controllers.FirstOrDefault(x => x.Targets(controllerName)); + if (t != null) return t; + + var controller = SuperController.singleton.GetAtomByUid(atomName).freeControllers.FirstOrDefault(x => x.name == cuid); + + t = new FreeControllerV3Ref(controller, subscene); + _controllers.Add(t); + RegisterAnimatableRef(t); + return t; + } + else + { + + var t = _controllers.FirstOrDefault(x => x.Targets(controllerName)); + if (t != null) return t; + var controller = atom.freeControllers.FirstOrDefault(fc => fc.name == controllerName); + if (ReferenceEquals(controller, null)) + { + SuperController.LogError($"Timeline: Atom '{atom.uid}' does not have a controller '{controllerName}'"); + return null; + } + t = new FreeControllerV3Ref(controller); + _controllers.Add(t); + RegisterAnimatableRef(t); + return t; } - t = new FreeControllerV3Ref(controller); - _controllers.Add(t); - RegisterAnimatableRef(t); - return t; } - public FreeControllerV3Ref GetOrCreateController(FreeControllerV3 controller) + public FreeControllerV3Ref GetOrCreateController(FreeControllerV3 controller, bool subscene = false) { - var t = _controllers.FirstOrDefault(x => x.Targets(controller)); + + var t = _controllers.FirstOrDefault(x => x.Targets(controller)); if (t != null) return t; - t = new FreeControllerV3Ref(controller); + t = new FreeControllerV3Ref(controller, subscene); _controllers.Add(t); RegisterAnimatableRef(t); return t; diff --git a/src/AtomAnimations/Animatables/FreeControllerV3s/FreeControllerV3Ref.cs b/src/AtomAnimations/Animatables/FreeControllerV3s/FreeControllerV3Ref.cs index 2715d823..b28435ba 100644 --- a/src/AtomAnimations/Animatables/FreeControllerV3s/FreeControllerV3Ref.cs +++ b/src/AtomAnimations/Animatables/FreeControllerV3s/FreeControllerV3Ref.cs @@ -1,21 +1,54 @@ -namespace VamTimeline +using System.Linq; + +namespace VamTimeline { public class FreeControllerV3Ref : AnimatableRefBase { public readonly FreeControllerV3 controller; + public readonly Atom parentAtom; + public readonly bool subscene; - public FreeControllerV3Ref(FreeControllerV3 controller) + public FreeControllerV3Ref(FreeControllerV3 controller, bool subscene = false) { this.controller = controller; + this.parentAtom = controller.containingAtom; + this.subscene = subscene; } - public override string name => controller.name; + public override string name => subscene ? controller.name + "("+ this.parentAtom.name+")" : controller.name; + + protected string extractEffectorName(string atomName) + { + string effectorName = atomName.Contains('/') ? atomName.Split('/')[1] : atomName; + if (effectorName.ToLower().Contains("effector") || effectorName.ToLower().Contains("bendgoal")) + { + if (effectorName.Contains("&")) //Full body effector + { + string[] tokens = effectorName.Split('&'); + if (tokens.Length > 2) + { + effectorName = tokens[1]; + } + } + else if (effectorName.Contains("_")) //Fabbrik/LimbIK/CCD Effector + { + string[] tokens = effectorName.Split('_'); + if (tokens.Length > 2) + { + effectorName = tokens[2] + " " + tokens[0]; + } + } + + } + + return effectorName; + } public override string GetShortName() { - return controller.name.EndsWith("Control") - ? controller.name.Substring(0, controller.name.Length - "Control".Length) - : controller.name; + return controller.name.EndsWith("Control") + ? controller.name.Substring(0, controller.name.Length - "Control".Length) + : subscene ? extractEffectorName(this.parentAtom.name) : controller.name ; } public bool Targets(string controllerName) diff --git a/src/AtomAnimations/Operations/TargetsOperations.cs b/src/AtomAnimations/Operations/TargetsOperations.cs index e9355e41..59d91255 100644 --- a/src/AtomAnimations/Operations/TargetsOperations.cs +++ b/src/AtomAnimations/Operations/TargetsOperations.cs @@ -17,12 +17,17 @@ public TargetsOperations(Atom containingAtom, AtomAnimation animation, AtomAnima public FreeControllerV3AnimationTarget Add(FreeControllerV3 fc) { - if (fc == null || fc.containingAtom != _containingAtom) return null; + bool subscene = false; + if (_containingAtom.type == "SubScene") + { subscene = true; if (fc == null || fc.containingAtom.containingSubScene.containingAtom != _containingAtom) return null; } + else + { if (fc == null || fc.containingAtom != _containingAtom) return null; } + var target = _clip.targetControllers.FirstOrDefault(t => t.animatableRef.Targets(fc)); if (target != null) return target; foreach (var clip in _animation.index.ByLayer(_clip.animationLayer)) { - var t = clip.Add(_animation.animatables.GetOrCreateController(fc)); + var t = clip.Add(_animation.animatables.GetOrCreateController(fc, subscene)); if (t == null) continue; t.SetKeyframeToCurrent(0f); t.SetKeyframeToCurrent(clip.animationLength); @@ -34,7 +39,12 @@ public FreeControllerV3AnimationTarget Add(FreeControllerV3 fc) public void AddSelectedController() { var selected = SuperController.singleton.GetSelectedController(); - if (selected == null || selected.containingAtom != _containingAtom) return; + + if (_containingAtom.type == "SubScene") + { if (selected == null || selected.containingAtom.containingSubScene.containingAtom != _containingAtom) return; } + else + { if (selected == null || selected.containingAtom != _containingAtom) return; } + if (_animation.index.ByController().Any(kvp => kvp.Key.Targets(selected))) return; Add(selected); } diff --git a/src/UI/Screens/AddRemoveTargetsScreen.cs b/src/UI/Screens/AddRemoveTargetsScreen.cs index 7475caf4..40203621 100644 --- a/src/UI/Screens/AddRemoveTargetsScreen.cs +++ b/src/UI/Screens/AddRemoveTargetsScreen.cs @@ -158,17 +158,40 @@ private void InitControllersUI() private IEnumerable GetEligibleFreeControllers() { + yield return ""; var reservedByOtherLayers = new HashSet(animation.clips .Where(c => c.animationLayer != current.animationLayer) .SelectMany(c => c.targetControllers) .Select(t => t.animatableRef.controller)); - foreach (var fc in plugin.containingAtom.freeControllers) + + if (plugin.containingAtom.type == "SubScene") + { + foreach (string atomName in SuperController.singleton.GetAtomUIDsWithFreeControllers()) //Get all the scene atoms + { + Atom atom = SuperController.singleton.GetAtomByUid(atomName); + if (atom.subScenePath.Split('/')[0].Equals(plugin.containingAtom.name)) //Check if the atom name has a path that matches the name of the subscene atom. + { + foreach (var fc in atom.freeControllers) + { + if (!fc.name.EndsWith("Control") && fc.name != "control") continue; + if (current.targetControllers.Any(c => c.animatableRef.Targets(fc))) continue; + if (reservedByOtherLayers.Contains(fc)) continue; + yield return fc.name + "(" + atomName.Split('/')[1] + ")"; //Attach the atom name in brackets so we can identify different control atoms. + } + } + } + } + else { - if (!fc.name.EndsWith("Control") && fc.name != "control") continue; - if (current.targetControllers.Any(c => c.animatableRef.Targets(fc))) continue; - if (reservedByOtherLayers.Contains(fc)) continue; - yield return fc.name; + + foreach (var fc in plugin.containingAtom.freeControllers) + { + if (!fc.name.EndsWith("Control") && fc.name != "control") continue; + if (current.targetControllers.Any(c => c.animatableRef.Targets(fc))) continue; + if (reservedByOtherLayers.Contains(fc)) continue; + yield return fc.name; + } } } @@ -439,7 +462,19 @@ private void AddAnimatedController() SelectNextInList(_addControllerListJSON); - var controller = plugin.containingAtom.freeControllers.FirstOrDefault(x => x.name == uid); + //new code add subscene atom controllers into targets. + string atomName = null; + bool subscene = false; + if (uid.Contains("(")) //Will fail if someone puts a bracket in their atom name. + { + var newUid = uid.Split('(')[0].Trim(')'); + atomName = uid.Split('(')[1].Trim(')'); + uid = newUid; + subscene = true; + } + + var controller = atomName != null ? SuperController.singleton.GetAtomByUid(plugin.containingAtom.name+"/" + atomName).freeControllers.FirstOrDefault(x => x.name == uid) : plugin.containingAtom.freeControllers.FirstOrDefault(x => x.name == uid); + if (controller == null) { SuperController.LogError($"Timeline: Controller {uid} in atom {plugin.containingAtom.uid} does not exist"); @@ -459,11 +494,12 @@ private void AddAnimatedController() } foreach (var clip in animation.index.ByLayer(current.animationLayer)) - { - var added = clip.Add(animation.animatables.GetOrCreateController(controller)); + { + var added = clip.Add(animation.animatables.GetOrCreateController(controller, subscene)); + if (added == null) continue; - var controllerPose = clip.pose?.GetControllerPose(controller.name); + var controllerPose = clip.pose?.GetControllerPose(controller.name); if (controllerPose == null) { added.SetKeyframeToCurrent(0f); @@ -475,8 +511,10 @@ private void AddAnimatedController() added.SetKeyframeByTime(clip.animationLength, controllerPose.position, Quaternion.Euler(controllerPose.rotation)); } + if (!clip.loop) added.ChangeCurveByTime(clip.animationLength, CurveTypeValues.CopyPrevious); + } } catch (Exception exc)