private void CropAt(float delta, float time) { var keyframeOps = new KeyframesOperations(_clip); foreach (var target in _clip.GetAllTargets()) { // TODO: Create new keyframe if missing from evaluate curve var snapshots = target .GetAllKeyframesTime() .Where(t => t <= time || t >= time - delta) .Select(t => { var newTime = t <= time ? t : t + delta; return(new SnapshotAt { time = newTime, snapshot = target.GetSnapshot(t) }); }) .ToList(); keyframeOps.RemoveAll(target, true); foreach (var s in snapshots) { target.SetSnapshot(s.time, s.snapshot); } target.AddEdgeFramesIfMissing(_clip.animationLength); } }
public void Loop(float newAnimationLength) { var keyframeOps = new KeyframesOperations(_clip); var originalAnimationLength = _clip.animationLength; _clip.animationLength = newAnimationLength; foreach (var target in _clip.GetAllTargets()) { var snapshots = target .GetAllKeyframesTime() .Select(t => new SnapshotAt { time = t, snapshot = target.GetSnapshot(t) }) .ToList(); snapshots.RemoveAt(snapshots.Count - 1); keyframeOps.RemoveAll(target, true); var iteration = 0; var i = 0; while (true) { var snapshot = snapshots[i++]; var time = snapshot.time + iteration * originalAnimationLength; if (time > newAnimationLength) { break; } target.SetSnapshot(time, snapshot.snapshot); if (i >= snapshots.Count) { i = 0; iteration++; } } target.AddEdgeFramesIfMissing(_clip.animationLength); } }
public void Stretch(float newAnimationLength) { var keyframeOps = new KeyframesOperations(_clip); var originalAnimationLength = _clip.animationLength; _clip.animationLength = newAnimationLength; var ratio = newAnimationLength / originalAnimationLength; foreach (var target in _clip.GetAllTargets()) { var snapshots = target .GetAllKeyframesTime() .Select(t => new SnapshotAt { time = t, snapshot = target.GetSnapshot(t) }) .ToList(); keyframeOps.RemoveAll(target, true); foreach (var s in snapshots) { target.SetSnapshot((s.time * ratio).Snap(), s.snapshot); } } }
public IEnumerator Execute(List <FreeControllerV3> controllers) { var containingAtom = _containingAtom; var keyOps = new KeyframesOperations(clip); var targetOps = new TargetsOperations(_containingAtom, _animation, clip); yield return(0); var controlCounter = 0; var motControls = containingAtom.motionAnimationControls .Where(m => m?.clip?.clipLength > 0.1f) .Where(m => controllers.Count == 0 || controllers.Contains(m.controller)) .Where(m => m.clip.steps.Any(s => s.positionOn || s.rotationOn)) .ToList(); foreach (var mot in motControls) { FreeControllerAnimationTarget target = null; FreeControllerV3 ctrl; yield return(new Progress { controllersProcessed = ++controlCounter, controllersTotal = motControls.Count }); try { ctrl = mot.controller; target = clip.targetControllers.FirstOrDefault(t => t.controller == ctrl); if (_animation.index.ByLayer().Where(l => l.Key != clip.animationLayer).Select(l => l.Value.First()).SelectMany(c => c.targetControllers).Any(t2 => t2.controller == ctrl)) { SuperController.LogError($"Skipping controller {ctrl.name} because it was used in another layer."); continue; } if (target == null) { target = targetOps.Add(ctrl); target.AddEdgeFramesIfMissing(clip.animationLength); } else { keyOps.RemoveAll(target); } target.StartBulkUpdates(); target.Validate(clip.animationLength); var enumerator = ProcessController(mot.clip, target, ctrl).GetEnumerator(); while (enumerator.MoveNext()) { yield return(enumerator.Current); } } finally { // NOTE: This should not be necessary, but for some reason dirty is set back to false too early and some changes are not picked up target.dirty = true; target?.EndBulkUpdates(); } } }
public IEnumerator StartRecording(List <FreeControllerAnimationTarget> controllers, List <FloatParamAnimationTarget> floatParams) { // TODO: Handle stopping in the middle of it // TODO: Handle starting while it's already recording // TODO: Counter should use in-game rather than helptext _animation.StopAll(); _animation.ResetAll(); var keyframesOps = new KeyframesOperations(_clip); for (var i = 5; i > 0; i--) { SuperController.singleton.helpText = $"Start recording in {i}..."; var next = Time.realtimeSinceStartup + 1f; while (Time.realtimeSinceStartup < next) { yield return(0); if (Input.GetKeyDown(KeyCode.Escape)) { yield break; } } } SuperController.singleton.helpText = "Recording..."; foreach (var target in controllers) { keyframesOps.RemoveAll(target); } foreach (var target in floatParams) { keyframesOps.RemoveAll(target); } _clip.DirtyAll(); _animation.RebuildAnimationNow(); yield return(0); foreach (var target in controllers) { target.recording = true; target.StartBulkUpdates(); } foreach (var target in floatParams) { target.recording = true; target.StartBulkUpdates(); } _animation.PlayClip(_clip, false); while (_animation.playTime <= _clip.animationLength && _animation.isPlaying) { if (Input.GetKeyDown(KeyCode.Escape)) { break; } yield return(0); } _animation.StopAll(); _animation.ResetAll(); SuperController.singleton.helpText = ""; // TODO: This needs to be guaranteed. We could register the enumerator inside a disposable class, dispose being called in different cancel situations foreach (var target in controllers) { target.recording = false; target.EndBulkUpdates(); } foreach (var target in floatParams) { target.recording = false; target.EndBulkUpdates(); } _clip.DirtyAll(); }