public void Copy() { try { if (!CanEdit()) { return; } var time = clipTime; var entry = AtomAnimationClip.Copy(time, GetAllOrSelectedTargets().ToList()); if (entry.empty) { SuperController.LogMessage("Timeline: Nothing to copy"); } else { clipboard.Clear(); clipboard.time = time; clipboard.entries.Add(entry); } } catch (Exception exc) { SuperController.LogError($"Timeline.{nameof(AtomAnimationEditContext)}.{nameof(Copy)}: {exc}"); } }
public void CopyDeleteSelected(bool copy, bool delete) { plugin.animationEditContext.clipboard.Clear(); plugin.animationEditContext.clipboard.time = _startJSON.valNoCallback; foreach (var target in animationEditContext.GetAllOrSelectedTargets()) { target.StartBulkUpdates(); try { var keyframes = target.GetAllKeyframesTime(); for (var key = keyframes.Length - 1; key >= 0; key--) { var keyTime = keyframes[key]; if (keyTime < _startJSON.val || keyTime > _endJSON.val) { continue; } if (copy) { plugin.animationEditContext.clipboard.entries.Insert(0, AtomAnimationClip.Copy(keyTime, animationEditContext.GetAllOrSelectedTargets().ToList())); } if (delete && !keyTime.IsSameFrame(0) && !keyTime.IsSameFrame(current.animationLength)) { target.DeleteFrame(keyTime); } } } finally { target.EndBulkUpdates(); } } }
public void StopClip(AtomAnimationClip clip) { if (clip.playbackEnabled) { clip.Leave(); clip.Reset(false); if (clip.animationPattern) { clip.animationPattern.SetBoolParamValue("loopOnce", true); } } else { clip.playbackMainInLayer = false; } if (isPlaying) { if (!clips.Any(c => c.playbackMainInLayer)) { isPlaying = false; sequencing = false; } onIsPlayingChanged.Invoke(clip); } }
private void OnClipIsPlayingChanged(AtomAnimationClip clip) { if (animation.master && clip.playbackEnabled && animation.sequencing) { peers.SendMasterClipState(clip); } }
public virtual void Bind(IAtomPlugin plugin, AtomAnimationClip clip, T target) { this.plugin = plugin; this.clip = clip; this.target = target; CreateToggle(plugin); toggle.label = target.GetShortName(); CreateCustom(); valueText = CreateValueText(); _expandButton = CreateExpandButton(); var expandListener = _expandButton.AddComponent <Clickable>(); expandListener.onClick.AddListener(pointerEvent => ToggleExpanded()); this.plugin.animationEditContext.onTimeChanged.AddListener(OnTimeChanged); OnTimeChanged(this.plugin.animationEditContext.timeArgs); target.onAnimationKeyframesRebuilt.AddListener(OnAnimationKeyframesRebuilt); OnAnimationKeyframesRebuilt(); }
private void RebuildTransition(AtomAnimationClip clip) { bool realign = false; var previous = clips.FirstOrDefault(c => c.nextAnimationName == clip.animationName); if (previous != null && (previous.IsDirty() || clip.IsDirty())) { clip.Paste(0f, previous.Copy(previous.animationLength, previous.GetAllCurveTargets().Cast <IAtomAnimationTarget>()), false); realign = true; } var next = GetClip(clip.nextAnimationName); if (next != null && (next.IsDirty() || clip.IsDirty())) { clip.Paste(clip.animationLength, next.Copy(0f, next.GetAllCurveTargets().Cast <IAtomAnimationTarget>()), false); realign = true; } if (realign) { foreach (var target in clip.targetControllers) { if (clip.ensureQuaternionContinuity) { UnitySpecific.EnsureQuaternionContinuityAndRecalculateSlope( target.rotX, target.rotY, target.rotZ, target.rotW); } } } }
public AtomAnimationClip DeserializeClip(JSONClass clipJSON) { var animationName = clipJSON["AnimationName"].Value; var animationLayer = DeserializeString(clipJSON["AnimationLayer"], AtomAnimationClip.DefaultAnimationLayer); bool?legacyTransition = null; if (clipJSON.HasKey("Transition")) { legacyTransition = DeserializeBool(clipJSON["Transition"], false); } var clip = new AtomAnimationClip(animationName, animationLayer) { blendInDuration = DeserializeFloat(clipJSON["BlendDuration"], AtomAnimationClip.DefaultBlendDuration), loop = DeserializeBool(clipJSON["Loop"], true), autoTransitionPrevious = legacyTransition ?? DeserializeBool(clipJSON["AutoTransitionPrevious"], false), autoTransitionNext = legacyTransition ?? DeserializeBool(clipJSON["AutoTransitionNext"], false), preserveLoops = DeserializeBool(clipJSON["SyncTransitionTime"], false), ensureQuaternionContinuity = DeserializeBool(clipJSON["EnsureQuaternionContinuity"], true), nextAnimationName = clipJSON["NextAnimationName"]?.Value, nextAnimationTime = DeserializeFloat(clipJSON["NextAnimationTime"]), nextAnimationTimeRandomize = DeserializeFloat(clipJSON["NextAnimationTimeRandomize"]), autoPlay = DeserializeBool(clipJSON["AutoPlay"], false), speed = DeserializeFloat(clipJSON["Speed"], 1), weight = DeserializeFloat(clipJSON["Weight"], 1), uninterruptible = DeserializeBool(clipJSON["Uninterruptible"], false), animationLength = DeserializeFloat(clipJSON["AnimationLength"]).Snap() }; DeserializeClip(clip, clipJSON); return(clip); }
private IEnumerator UpdateAnimButton(UIDynamicButton btn, AtomAnimationClip clip) { yield return(0); var playLabel = $" \u25B6 {clip.animationName}"; while (!_disposing) { if (!clip.playbackEnabled && !clip.playbackMainInLayer) { if (btn.label != playLabel) { btn.label = playLabel; } } else { btn.label = $" \u25A0 [time: {clip.clipTime:00.000}, weight: {Mathf.Round(clip.playbackBlendWeight * 100f):000}%]"; } for (var i = 0; i < 4; i++) { yield return(0); } } }
public void SelectAnimation(AtomAnimationClip clip) { var previous = current; current = clip; if (previous != null) { previous.Leave(); } if (previous.animationLayer != current.animationLayer) { onClipsListChanged.Invoke(); } onCurrentAnimationChanged.Invoke(new CurrentAnimationChangedEventArgs { before = previous, after = current }); if (isPlaying) { var previousMain = clips.FirstOrDefault(c => c.playbackMainInLayer && c.animationLayer == current.animationLayer); if (previousMain != null) { TransitionAnimation(previousMain, current); } } else { clipTime = 0f; Sample(); } }
public void Bind(IAtomPlugin plugin, AtomAnimationClip clip, T target) { this.plugin = plugin; this.clip = clip; this.target = target; CreateToggle(plugin); toggle.label = target.name; CreateCustom(); valueText = CreateValueText(); _expandButton = CreateExpandButton(); var expandListener = _expandButton.AddComponent <Clickable>(); expandListener.onClick.AddListener(pointerEvent => ToggleExpanded()); this.plugin.animation.TimeChanged.AddListener(this.OnTimeChanged); OnTimeChanged(this.plugin.animation.Time); target.onAnimationKeyframesModified.AddListener(OnAnimationKeyframesModified); OnAnimationKeyframesModified(); }
public void RemoveClip(AtomAnimationClip clip) { clips.Remove(clip); clip.Dispose(); onClipsListChanged.Invoke(); OnAnimationKeyframesDirty(); }
public void RemoveClip(AtomAnimationClip clip) { Clips.Remove(clip); clip.Dispose(); ClipsListChanged.Invoke(); OnAnimationModified(); }
private string GetNewAnimationName(AtomAnimationClip source) { var match = _lastDigitsRegex.Match(source.animationName); string animationNameBeforeInt; int animationNameInt; if (!match.Success) { animationNameBeforeInt = $"{source.animationName.TrimEnd()} "; animationNameInt = 1; } else { animationNameBeforeInt = match.Groups["name"].Value; animationNameInt = int.Parse(match.Groups["index"].Value); } for (var i = animationNameInt + 1; i < 999; i++) { var animationName = animationNameBeforeInt + i; if (clips.All(c => c.animationName != animationName)) { return(animationName); } } return(Guid.NewGuid().ToString()); }
private void SerializeClip(AtomAnimationClip clip, JSONClass clipJSON) { if (clip.animationPattern != null) { clipJSON.Add("AnimationPattern", clip.animationPattern.containingAtom.uid); } var controllersJSON = new JSONArray(); clipJSON.Add("Controllers", controllersJSON); foreach (var controller in clip.targetControllers) { var controllerJSON = new JSONClass { { "Controller", controller.controller.name }, { "X", SerializeCurve(controller.x, controller.settings) }, { "Y", SerializeCurve(controller.y, controller.settings) }, { "Z", SerializeCurve(controller.z, controller.settings) }, { "RotX", SerializeCurve(controller.rotX, controller.settings) }, { "RotY", SerializeCurve(controller.rotY, controller.settings) }, { "RotZ", SerializeCurve(controller.rotZ, controller.settings) }, { "RotW", SerializeCurve(controller.rotW, controller.settings) } }; controllersJSON.Add(controllerJSON); } var paramsJSON = new JSONArray(); clipJSON.Add("FloatParams", paramsJSON); foreach (var target in clip.targetFloatParams) { var paramJSON = new JSONClass { { "Storable", target.storableId }, { "Name", target.floatParamName }, { "Value", SerializeCurve(target.value, target.settings) }, }; paramsJSON.Add(paramJSON); } var triggersJSON = new JSONArray(); clipJSON.Add("", triggersJSON); clipJSON.Add("Triggers", triggersJSON); foreach (var target in clip.targetTriggers) { var triggerJSON = new JSONClass() { { "Name", target.name } }; var entriesJSON = new JSONArray(); foreach (var x in target.triggersMap.OrderBy(kvp => kvp.Key)) { entriesJSON.Add(x.Value.GetJSON()); } triggerJSON["Triggers"] = entriesJSON; triggersJSON.Add(triggerJSON); } }
private static void RebuildClip(AtomAnimationClip clip, AtomAnimationClip previous) { foreach (var target in clip.targetControllers) { if (!target.dirty) { continue; } if (clip.loop) { target.SetCurveSnapshot(clip.animationLength, target.GetCurveSnapshot(0f), false); } target.ComputeCurves(); if (clip.ensureQuaternionContinuity) { var lastMatching = previous?.targetControllers.FirstOrDefault(t => t.TargetsSameAs(target)); var q = lastMatching?.GetRotationAtKeyframe(lastMatching.rotX.length - 1) ?? target.GetRotationAtKeyframe(target.rotX.length - 1); UnitySpecific.EnsureQuaternionContinuityAndRecalculateSlope( target.rotX, target.rotY, target.rotZ, target.rotW, q); } foreach (var curve in target.GetCurves()) { curve.ComputeCurves(); } } foreach (var target in clip.targetFloatParams) { if (!target.dirty) { continue; } if (clip.loop) { target.SetCurveSnapshot(clip.animationLength, target.GetCurveSnapshot(0), false); } target.value.ComputeCurves(); } foreach (var target in clip.targetTriggers) { if (!target.dirty) { continue; } target.RebuildKeyframes(clip.animationLength); } }
public AtomAnimationClip AddAnimation() { string animationName = GetNewAnimationName(); var clip = new AtomAnimationClip(animationName); AddClip(clip); return(clip); }
public virtual void Init(IAtomPlugin plugin) { this.plugin = plugin; prefabFactory = gameObject.AddComponent <VamPrefabFactory>(); prefabFactory.plugin = plugin; plugin.animation.onCurrentAnimationChanged.AddListener(OnCurrentAnimationChanged); current = plugin.animation?.current; }
private AtomAnimationClip WithStorable(TestContext context, AtomAnimationClip clip, string name) { var storable = context.gameObject.GetComponent <JSONStorable>() ?? context.gameObject.AddComponent <JSONStorable>(); var target = clip.Add(new FloatParamAnimationTarget(storable, new JSONStorableFloat(name, 0, 0, 1))); target.AddEdgeFramesIfMissing(clip.animationLength); return(clip); }
private void ReapplyClipCurve(AtomAnimationClip clip) { clip.Clip.ClearCurves(); foreach (var target in clip.TargetControllers) { target.ReapplyCurvesToClip(clip.Clip); } }
private void Blend(AtomAnimationClip clip, float weight, float duration) { if (!clip.playbackEnabled) { clip.playbackWeight = 0; } clip.playbackEnabled = true; clip.playbackBlendRate = (weight - clip.playbackWeight) / duration; }
public void PlayClip(AtomAnimationClip clip, bool sequencing, bool allowPreserveLoops = true) { paused = false; if (clip.playbackEnabled && clip.playbackMainInLayer) { return; } if (!isPlaying) { isPlaying = true; this.sequencing = this.sequencing || sequencing; #if (PLAYBACK_HEALTH_CHECK) PlaybackHealthCheck(clip); #endif } if (sequencing && !clip.playbackEnabled) { clip.clipTime = 0; } var previousMain = index.ByLayer(clip.animationLayer).FirstOrDefault(c => c.playbackMainInLayer); if (previousMain != null && previousMain != clip) { if (previousMain.uninterruptible) { return; } if (clip.loop && clip.preserveLoops && previousMain.loop && allowPreserveLoops) { previousMain.SetNext(clip.animationName, Mathf.Max(previousMain.animationLength - previousMain.clipTime, 0f)); } else { TransitionAnimation(previousMain, clip); } } else { if (clip.clipTime == clip.animationLength) { clip.clipTime = 0f; } Blend(clip, 1f, clip.blendInDuration); clip.playbackMainInLayer = true; } if (clip.animationPattern) { clip.animationPattern.SetBoolParamValue("loopOnce", false); clip.animationPattern.ResetAndPlay(); } if (sequencing && clip.nextAnimationName != null) { AssignNextAnimation(clip); } onIsPlayingChanged.Invoke(clip); }
public void AddClip(AtomAnimationClip clip) { clip.AnimationSettingsModified.AddListener(OnAnimationSettingsModified); clip.onAnimationKeyframesModified.AddListener(OnAnimationModified); clip.onTargetsListChanged.AddListener(OnAnimationModified); clip.onTargetsSelectionChanged.AddListener(OnTargetsSelectionChanged); Clips.Add(clip); ClipsListChanged.Invoke(); }
public void ChangeAnimation(string animationName) { var clip = Clips.FirstOrDefault(c => c.AnimationName == animationName); if (clip == null) { throw new NullReferenceException($"Could not find animation '{animationName}'. Found animations: '{string.Join("', '", Clips.Select(c => c.AnimationName).ToArray())}'."); } var targetAnim = _unityAnimation[animationName]; var time = Time; if (_isPlaying) { if (HasAnimatableControllers()) { targetAnim.time = 0f; targetAnim.enabled = true; targetAnim.weight = 0f; _unityAnimation.Blend(Current.AnimationName, 0f, Current.BlendDuration); _unityAnimation.Blend(animationName, 1f, Current.BlendDuration); } if (Current.AnimationPattern != null) { // Let the loop finish during the transition Current.AnimationPattern.SetBoolParamValue("loopOnce", true); } _previousClip = Current; _blendingTimeLeft = _blendingDuration = Current.BlendDuration; } var previous = Current; Current = clip; _animState = targetAnim; if (_isPlaying) { DetermineNextAnimation(_playTime); if (Current.AnimationPattern != null) { Current.AnimationPattern.SetBoolParamValue("loopOnce", false); Current.AnimationPattern.ResetAndPlay(); } } else { Time = 0f; Sample(); CurrentAnimationChanged.Invoke(new CurrentAnimationChangedEventArgs { Before = previous, After = Current }); } }
private static FreeControllerAnimationTarget CopyTarget(AtomAnimationClip clip, FreeControllerAnimationTarget origTarget) { var newTarget = clip.Add(origTarget.controller); newTarget.SetParent(origTarget.parentAtomId, origTarget.parentRigidbodyId); newTarget.weight = origTarget.weight; newTarget.controlPosition = origTarget.controlPosition; newTarget.controlRotation = origTarget.controlPosition; return(newTarget); }
private void RebuildClip(AtomAnimationClip clip) { foreach (var target in clip.targetControllers) { if (!target.dirty) { continue; } if (clip.loop) { target.SetCurveSnapshot(clip.animationLength, target.GetCurveSnapshot(0f), false); } target.ReapplyCurveTypes(clip.loop); if (clip.ensureQuaternionContinuity) { UnitySpecific.EnsureQuaternionContinuityAndRecalculateSlope( target.rotX, target.rotY, target.rotZ, target.rotW); } RebuildClipLoop(clip, target); } foreach (var target in clip.targetFloatParams) { if (!target.dirty) { continue; } if (clip.loop) { target.value.SetKeyframe(clip.animationLength, target.value[0].value); } target.ReapplyCurveTypes(clip.loop); RebuildClipLoop(clip, target); } foreach (var target in clip.targetTriggers) { if (!target.dirty) { continue; } target.RebuildKeyframes(clip.animationLength); } }
public AtomAnimationClip CreateClip(string animationLayer, string animationName) { if (clips.Any(c => c.animationName == animationName)) { throw new InvalidOperationException($"Animation '{animationName}' already exists"); } var clip = new AtomAnimationClip(animationName, animationLayer); AddClip(clip); return(clip); }
public void SendCurrentAnimation(AtomAnimationClip clip) { if (syncing) { return; } SendTimelineEvent(new object[] { nameof(SendCurrentAnimation), clip.animationName }); }
public IEnumerable OverwriteEmptyClip(TestContext context) { var existing = context.animation.clips.Single(); var clip = new AtomAnimationClip(existing.animationName, "New Layer"); new ImportOperations(context.animation, true).ImportClips(new[] { clip }); context.Assert(context.animation.clips.Count, 1, "When the animation is empty, replace it"); context.Assert(clip.animationLayer, "Main Layer", "The existing animation layer is used"); yield break; }
public void DeserializeAnimation(AtomAnimation animation, JSONClass animationJSON) { if (animation == null) { throw new ArgumentNullException(nameof(animation)); } animation.speed = DeserializeFloat(animationJSON["Speed"], 1f); JSONArray clipsJSON = animationJSON["Clips"].AsArray; if (clipsJSON == null || clipsJSON.Count == 0) { throw new NullReferenceException("Saved state does not have clips"); } foreach (JSONClass clipJSON in clipsJSON) { var animationName = clipJSON["AnimationName"].Value; var animationLayer = DeserializeString(clipJSON["AnimationLayer"], AtomAnimationClip.DefaultAnimationLayer); var existingClip = animation.GetClip(animationName); if (existingClip != null) { if (existingClip.IsEmpty()) { var clipToRemove = animation.GetClip(animationName); animation.clips.Remove(clipToRemove); clipToRemove.Dispose(); } else { var newAnimationName = GenerateUniqueAnimationName(animation, animationName); SuperController.LogError($"VamTimeline: Imported clip '{animationName}' already exists and will be imported with the name {newAnimationName}"); animationName = newAnimationName; } } var clip = new AtomAnimationClip(animationName, animationLayer) { blendDuration = DeserializeFloat(clipJSON["BlendDuration"], AtomAnimationClip.DefaultBlendDuration), loop = DeserializeBool(clipJSON["Loop"], true), transition = DeserializeBool(clipJSON["Transition"], false), ensureQuaternionContinuity = DeserializeBool(clipJSON["EnsureQuaternionContinuity"], true), nextAnimationName = clipJSON["NextAnimationName"]?.Value, nextAnimationTime = DeserializeFloat(clipJSON["NextAnimationTime"], 0), autoPlay = DeserializeBool(clipJSON["AutoPlay"], false), speed = DeserializeFloat(clipJSON["Speed"], 1), weight = DeserializeFloat(clipJSON["Weight"], 1), }; clip.animationLength = DeserializeFloat(clipJSON["AnimationLength"]).Snap(); DeserializeClip(clip, clipJSON); animation.AddClip(clip); } animation.Initialize(); animation.RebuildAnimationNow(); }
public void SendTime(AtomAnimationClip clip) { if (syncing) { return; } SendTimelineEvent(new object[] { nameof(SendTime), // 0 clip.animationName, // 1 clip.clipTime // 2 }); }