Code refactor

This commit is contained in:
AbstractConcept 2022-10-28 00:28:51 -05:00
parent 757badf4f6
commit a55ba7b95b
232 changed files with 1282 additions and 936 deletions

View file

@ -36,60 +36,60 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public List<string> DefNames
{
get { return defNames.NullOrEmpty() ? defNames = new List<string>() : defNames; }
set { defNames = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
set { defNames = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<string> BodyDefTypes
{
get { return bodyDefTypes.NullOrEmpty() ? bodyDefTypes = new List<string>() : bodyDefTypes; }
set { bodyDefTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
set { bodyDefTypes = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<string> RequiredGenitals
{
get { return requiredGenitals.NullOrEmpty() ? requiredGenitals = new List<string>() : requiredGenitals; }
set { requiredGenitals = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
set { requiredGenitals = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<PawnRaceOffset> RaceOffsets {
get { return raceOffsets.NullOrEmpty() ? raceOffsets = new List<PawnRaceOffset>() : raceOffsets; }
set { raceOffsets = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
set { raceOffsets = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
set { tags = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public BodyTypeOffset BodyTypeOffset
{
get { return bodyTypeOffset == null ? bodyTypeOffset = new BodyTypeOffset() : bodyTypeOffset; }
set { bodyTypeOffset = value; EventsManager.OnActorChanged(this); }
set { bodyTypeOffset = value; }
}
[XmlIgnore] public bool Initiator
{
get { return initiator == true; }
set { if (value) { initiator = true; } else initiator = null; EventsManager.OnActorChanged(this); }
set { if (value) { initiator = true; } else initiator = null; }
}
[XmlIgnore] public bool ControlGenitalAngle
{
get { return controlGenitalAngle == true; }
set { if (value) { controlGenitalAngle = true; } else controlGenitalAngle = null; EventsManager.OnActorChanged(this); }
set { if (value) { controlGenitalAngle = true; } else controlGenitalAngle = null; }
}
[XmlIgnore] public bool IsFucking
{
get { return isFucking == true; }
set { if (value) { isFucking = true; } else isFucking = null; EventsManager.OnActorChanged(this); }
set { if (value) { isFucking = true; } else isFucking = null; }
}
[XmlIgnore] public bool IsFucked
{
get { return isFucked == true; }
set { if (value) { isFucked = true; } else isFucked = null; EventsManager.OnActorChanged(this); }
set { if (value) { isFucked = true; } else isFucked = null; }
}
// Local data
@ -163,6 +163,16 @@ namespace RimWorldAnimationStudio
return Workspace.animationDef.Actors.IndexOf(this);
}
public ActorPosition GetCurrentPosition()
{
return GetPositionAtTick(Workspace.StageTick);
}
public ActorPosition GetPositionAtTick(int atTick)
{
return new ActorPosition(GetActorID(), atTick);
}
// Pre-save / post-load
public void OnPreSave()
{

View file

@ -28,31 +28,31 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public int AnchoringActor
{
get { return anchoringActor.HasValue ? anchoringActor.Value : 0; }
set { anchoringActor = value; EventsManager.OnActorAddonChanged(this); }
set { anchoringActor = value; }
}
[XmlIgnore] public string AnchorName
{
get { return anchorName; }
set { anchorName = value; EventsManager.OnActorAddonChanged(this); }
set { anchorName = value; }
}
[XmlIgnore] public string Layer
{
get { return layer; }
set { layer = value; EventsManager.OnActorAddonChanged(this); }
set { layer = value; }
}
[XmlIgnore] public float Scale
{
get { return scale.HasValue ? scale.Value : 0f; }
set { scale = value; EventsManager.OnActorAddonChanged(this); }
set { scale = value; }
}
[XmlIgnore] public bool Render
{
get { return render == true; }
set { render = value; EventsManager.OnActorAddonChanged(this); }
set { render = value; }
}
// Simple curves

View file

@ -23,19 +23,19 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public float PosX
{
get { return posX.HasValue ? posX.Value : 0f; }
set { posX = value; EventsManager.OnAddonKeyframeChanged(this); }
set { posX = value; }
}
[XmlIgnore] public float PosZ
{
get { return posZ.HasValue ? posZ.Value : 0f; }
set { posZ = value; EventsManager.OnAddonKeyframeChanged(this); }
set { posZ = value; }
}
[XmlIgnore] public float Rotation
{
get { return rotation.HasValue ? rotation.Value : 0f; }
set { rotation = value; EventsManager.OnAddonKeyframeChanged(this); }
set { rotation = value; }
}
// Constructors

View file

@ -28,37 +28,37 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public string DefName
{
get { return defName != null && defName != "" ? defName : "newAnimation"; }
set { defName = value; EventsManager.OnAnimationDefChanged(); }
set { defName = value; }
}
[XmlIgnore] public string Label
{
get { return label != null && label != "" ? label : "newAnimation"; }
set { label = value; EventsManager.OnAnimationDefChanged(); }
set { label = value; }
}
[XmlIgnore] public List<string> SexTypes
{
get { return sexTypes.NullOrEmpty() ? sexTypes = new List<string>() : sexTypes; }
set { sexTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
set { sexTypes = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<string> InteractionDefTypes
{
get { return interactionDefTypes.NullOrEmpty() ? interactionDefTypes = new List<string>() : interactionDefTypes; }
set { interactionDefTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
set { interactionDefTypes = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<Actor> Actors
{
get { return actors.NullOrEmpty() ? actors = new List<Actor>() : actors; }
set { actors = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
set { actors = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<AnimationStage> AnimationStages
{
get { if (animationStages.NullOrEmpty()){ animationStages = new List<AnimationStage>(); } return animationStages; }
set { animationStages = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
set { animationStages = value.NotNullOrEmpty() ? value : null; }
}
// Local data
@ -83,12 +83,13 @@ namespace RimWorldAnimationStudio
Actor actor = new Actor();
Actors.Add(actor);
foreach (AnimationStage stage in Workspace.animationDef.AnimationStages)
{ stage.AddAnimationClip(Workspace.animationDef.Actors.Count - 1); }
Initialize();
Workspace.ActorID = Workspace.animationDef.Actors.Count - 1;
foreach (AnimationStage stage in Workspace.animationDef.AnimationStages)
{ stage.AddAnimationClip(Workspace.ActorID); }
EventsManager.OnActorCountChanged();
EventsManager.OnActorCountChanged();
Workspace.RecordEvent("Actor addition");
}
@ -119,7 +120,9 @@ namespace RimWorldAnimationStudio
{ stage.AddAnimationClip(actor.GetActorID()); }
Initialize();
Workspace.StageID = Workspace.animationDef.AnimationStages.Count - 1;
EventsManager.OnStageCountChanged();
Workspace.RecordEvent("Stage addition");
}
@ -127,9 +130,11 @@ namespace RimWorldAnimationStudio
{
AnimationStage stage = Workspace.GetCurrentAnimationStage().Copy();
stage.StageName += " (Clone)";
stage.Initialize();
Workspace.animationDef.AnimationStages.Insert(Workspace.StageID + 1, stage);
Initialize();
EventsManager.OnStageCountChanged();
Workspace.RecordEvent("Stage clone");
}
@ -154,8 +159,9 @@ namespace RimWorldAnimationStudio
}
AnimationStages.RemoveAt(Workspace.StageID);
Workspace.StageID--;
EventsManager.OnStageCountChanged();
Workspace.RecordEvent("Stage deletion");
}

View file

@ -10,11 +10,11 @@ namespace RimWorldAnimationStudio
public class AnimationStage
{
// Data to/from animationDef
[SerializeField] private string stageName;
[SerializeField] private int? playTimeTicks;
[SerializeField] private int? playTimeTicksQuick;
[SerializeField] private bool? isLooping;
[SerializeField, XmlArray("animationClips"), XmlArrayItem("li")] public List<PawnAnimationClip> animationClips;
public string stageName;
public int? playTimeTicks;
public int? playTimeTicksQuick;
public bool? isLooping;
[XmlArray("animationClips"), XmlArrayItem("li")] public List<PawnAnimationClip> animationClips;
// Data serialization control
public bool ShouldSerializeanimationClips() { return animationClips.NotNullOrEmpty(); }
@ -22,32 +22,44 @@ namespace RimWorldAnimationStudio
// Data helper functions
[XmlIgnore] public string StageName
{
get { return stageName != null && stageName != "" ? stageName : "NewStage"; }
set { stageName = value; EventsManager.OnAnimationStageChanged(this); }
get { return string.IsNullOrEmpty(stageName) ? stageName = "NewStage" : stageName; }
set { stageName = value; }
}
[XmlIgnore] public int PlayTimeTicks
{
get { return playTimeTicks.HasValue ? playTimeTicks.Value : 0; }
set { playTimeTicks = value; EventsManager.OnAnimationStageChanged(this); }
set { playTimeTicks = value; }
}
[XmlIgnore] public int PlayTimeTicksQuick
{
get { return playTimeTicksQuick.HasValue ? playTimeTicksQuick.Value : 0; }
set { playTimeTicksQuick = value; EventsManager.OnAnimationStageChanged(this); }
set { playTimeTicksQuick = value; }
}
[XmlIgnore] public bool IsLooping
{
get { return isLooping == true; }
set { isLooping = value; EventsManager.OnAnimationStageChanged(this); }
set { isLooping = value; }
}
[XmlIgnore] public List<PawnAnimationClip> AnimationClips
{
get { return animationClips.NullOrEmpty() ? animationClips = new List<PawnAnimationClip>() : animationClips; }
set { animationClips = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationStageChanged(this); }
set { animationClips = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public int StageLoopsNormal
{
get { return Mathf.CeilToInt(PlayTimeTicks / Workspace.StageWindowSize); }
set { value = Math.Max(1, value); PlayTimeTicks = value * Workspace.StageWindowSize; IsLooping = value > 1; }
}
[XmlIgnore] public int StageLoopsQuick
{
get { return Mathf.CeilToInt(PlayTimeTicksQuick / Workspace.StageWindowSize); }
set { value = Math.Max(0, Math.Min(value, StageLoopsNormal)); PlayTimeTicksQuick = value * Workspace.StageWindowSize; IsLooping = value > 1; }
}
// Local data
@ -63,8 +75,6 @@ namespace RimWorldAnimationStudio
if (clip.duration > PlayTimeTicks)
{ PlayTimeTicks = clip.duration; }
}
PlayTimeTicksQuick = PlayTimeTicks;
}
public int GetStageID()
@ -78,7 +88,7 @@ namespace RimWorldAnimationStudio
{
float scale = (float)newStageWindowSize / Workspace.StageWindowSize;
foreach (PawnAnimationClip clip in Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips)
foreach (PawnAnimationClip clip in AnimationClips)
{
foreach (PawnKeyframe keyframe in clip.Keyframes)
{
@ -93,8 +103,8 @@ namespace RimWorldAnimationStudio
public void ResizeStageWindow(int newStageWindowSize)
{
Workspace.GetCurrentAnimationStage().stageWindowSize = newStageWindowSize;
Workspace.GetCurrentAnimationStage().PlayTimeTicks = newStageWindowSize * Workspace.stageLoopsNormal;
Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick = newStageWindowSize * Workspace.stageLoopsQuick;
Workspace.GetCurrentAnimationStage().PlayTimeTicks = newStageWindowSize * StageLoopsNormal;
Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick = newStageWindowSize * StageLoopsQuick;
}
public void AddAnimationClip(int actorID = -1)
@ -134,7 +144,7 @@ namespace RimWorldAnimationStudio
clip.Keyframes.Add(keyframeB);
}
clip.BuildSimpleCurves();
animationClips.Add(clip);
}
// Pre-save / post-load

View file

@ -31,20 +31,20 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public List<ActorAddon> Addons
{
get { return addons.NullOrEmpty() ? addons = new List<ActorAddon>() : addons; }
set { addons = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnAnimationClipChanged(this); }
set { addons = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<PawnKeyframe> Keyframes
{
get { return keyframes.NullOrEmpty() ? keyframes = new List<PawnKeyframe>() : keyframes; }
set { keyframes = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnAnimationClipChanged(this); }
set { keyframes = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore]
public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnAnimationClipChanged(this); }
set { tags = value.NotNullOrEmpty() ? value : null; }
}
// Local data
@ -235,7 +235,7 @@ namespace RimWorldAnimationStudio
foreach (PawnKeyframe keyframe in keyframesToClone)
{
PawnAnimationClip clip = Workspace.GetAnimationClipThatOwnsKeyframe(keyframe.keyframeID, out int clipID);
PawnAnimationClip clip = Workspace.GetAnimationClipThatOwnsKeyframe(keyframe.keyframeID);
if (clip == null)
{ Debug.LogWarning("Cannot clone pawn keyframe - no clip owns this keyframe"); continue; }
@ -244,7 +244,7 @@ namespace RimWorldAnimationStudio
{ Debug.LogWarning("Cannot clone pawn keyframe - a keyframe already exists at this tick"); return; }
PawnKeyframe cloneFrame = keyframe.Copy();
cloneFrame.GenerateKeyframeID(clipID);
cloneFrame.GenerateKeyframeID(clip.GetOwningActorID());
cloneFrame.atTick = Workspace.StageTick;
PawnKeyframe nextKeyframe = clip.Keyframes.FirstOrDefault(x => x.atTick > Workspace.StageTick);
@ -301,8 +301,8 @@ namespace RimWorldAnimationStudio
if (Workspace.DoesPawnKeyframeExistAtTick(Workspace.StageID, targetActorID, tickToPasteAt))
{
PawnKeyframe oldKeyframe = Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips[targetActorID].Keyframes.First(x => x.atTick == tickToPasteAt);
RemovePawnKeyframe(targetActorID, oldKeyframe.keyframeID, true);
PawnKeyframe oldKeyframe = Workspace.GetPawnAnimationClip(targetActorID).Keyframes.First(x => x.atTick == tickToPasteAt);
Workspace.GetAnimationClipThatOwnsKeyframe(oldKeyframe.keyframeID).RemovePawnKeyframe(oldKeyframe.keyframeID, true);
}
PawnKeyframe clonedKeyframe = copiedKeyframe.Copy();
@ -330,34 +330,28 @@ namespace RimWorldAnimationStudio
Workspace.RecordEvent("Keyframe pasted");
}
public void RemovePawnKeyframe()
public void RemovePawnKeyframe(int keyframeID, bool force = false)
{
foreach (int keyframeID in Workspace.keyframeID)
{
if (Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID, out int clipID) != null)
{ RemovePawnKeyframe(clipID, keyframeID); }
}
}
public void RemovePawnKeyframe(int actorID, int keyframeID, bool force = false)
{
PawnKeyframe keyframe = Workspace.GetPawnKeyframe(actorID, keyframeID);
PawnAnimationClip clip = Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips[actorID];
if (keyframe == null || clip == null) return;
PawnKeyframe keyframe = Workspace.GetPawnKeyframe(keyframeID);
if (keyframe == null || IsOwnerOfKeyframe(keyframeID) == false) return;
if (keyframe.atTick == Constants.minTick && force == false)
{ Debug.LogWarning("Cannot delete key frame - the first key frame of an animation clip cannot be deleted"); return; }
if (clip.Keyframes.Count <= 2 && force == false)
if (Keyframes.Count <= 2 && force == false)
{ Debug.LogWarning("Cannot delete key frame - an animation clip must have two or more keyframes"); return; }
clip.Keyframes.Remove(keyframe);
clip.BuildSimpleCurves();
Keyframes.Remove(keyframe);
BuildSimpleCurves();
Workspace.RecordEvent("Keyframe deletion");
}
public bool IsOwnerOfKeyframe(int keyframeID)
{
return Keyframes.Any(x => x.keyframeID == keyframeID);
}
public float GetStageTickPercentage()
{
return (float)(Workspace.StageTick % duration) / duration;

View file

@ -40,79 +40,79 @@ namespace RimWorldAnimationStudio
[XmlIgnore] public float BodyAngle
{
get { return bodyAngle.HasValue ? bodyAngle.Value : 0f; }
set { bodyAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
set { bodyAngle = value; }
}
[XmlIgnore] public float HeadAngle
{
get { return headAngle.HasValue ? headAngle.Value : (float)(headAngle = 0f); }
set { headAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
set { headAngle = value; }
}
[XmlIgnore] public float HeadBob
{
get { return headBob.HasValue ? headBob.Value : (float)(headBob = 0f); }
set { headBob = value; EventsManager.OnPawnKeyframeChanged(this); }
set { headBob = value; }
}
[XmlIgnore] public float BodyOffsetX
{
get { return bodyOffsetX.HasValue ? bodyOffsetX.Value : (float)(bodyOffsetX = 0f); }
set { bodyOffsetX = value; EventsManager.OnPawnKeyframeChanged(this); }
set { bodyOffsetX = value; }
}
[XmlIgnore] public float BodyOffsetZ
{
get { return bodyOffsetZ.HasValue ? bodyOffsetZ.Value : (float)(bodyOffsetZ = 0f); }
set { bodyOffsetZ = value; EventsManager.OnPawnKeyframeChanged(this); }
set { bodyOffsetZ = value; }
}
[XmlIgnore] public int HeadFacing
{
get { return headFacing.HasValue ? headFacing.Value : (int)(headFacing = 2); }
set { headFacing = value; EventsManager.OnPawnKeyframeChanged(this); }
set { headFacing = value; }
}
[XmlIgnore] public int BodyFacing
{
get { return bodyFacing.HasValue ? bodyFacing.Value : (int)(bodyFacing = 2); }
set { bodyFacing = value; EventsManager.OnPawnKeyframeChanged(this); }
set { bodyFacing = value; }
}
[XmlIgnore] public float GenitalAngle
{
get { return genitalAngle.HasValue ? genitalAngle.Value : (float)(genitalAngle = 0f); }
set { genitalAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
set { genitalAngle = value; }
}
[XmlIgnore] public bool Quiver
{
get { return quiver == true; }
set { quiver = value; EventsManager.OnPawnKeyframeChanged(this); }
set { quiver = value; }
}
[XmlIgnore] public int TickDuration
{
get { return tickDuration.HasValue ? tickDuration.Value : (int)(tickDuration = 0); }
set { tickDuration = value; EventsManager.OnPawnKeyframeChanged(this); }
set { tickDuration = value; }
}
[XmlIgnore] public string SoundEffect
{
get { return soundEffect; }
set { soundEffect = value; EventsManager.OnPawnKeyframeChanged(this); }
set { soundEffect = value; }
}
[XmlIgnore] public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnKeyframeChanged(this); }
set { tags = value.NotNullOrEmpty() ? value : null; }
}
[XmlIgnore] public List<AddonKeyframe> AddonKeyframes
{
get { return addonKeyframes.NullOrEmpty() ? addonKeyframes = new List<AddonKeyframe>() : addonKeyframes; }
set { addonKeyframes = value.NotNullOrEmpty()? value : null; EventsManager.OnPawnKeyframeChanged(this); }
set { addonKeyframes = value.NotNullOrEmpty()? value : null; }
}
// Local data
@ -148,6 +148,63 @@ namespace RimWorldAnimationStudio
return AddonKeyframes.FirstOrDefault(x => x.AddonName == addonName);
}
public void AdjustActor(Vector2 deltaOffset)
{
float deltaAngle = -deltaOffset.x * 33.3333f + deltaOffset.y * 33.3333f;
int facing = deltaOffset.x < 0 ? 3 : deltaOffset.y < 0 ? 2 : deltaOffset.x > 0 ? 1 : 0;
switch (Workspace.actorManipulationMode)
{
case ActorManipulationMode.Pan: MoveActor(deltaOffset); break;
case ActorManipulationMode.Rotate: RotateActor(deltaAngle); break;
case ActorManipulationMode.Face: FaceActor(facing); break;
}
}
public void MoveActor(Vector2 deltaOffset)
{
if (Workspace.selectedBodyPart == null)
{
BodyOffsetX += deltaOffset.x;
BodyOffsetZ += deltaOffset.y;
}
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
{ HeadBob += deltaOffset.y; }
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
Workspace.RecordEvent("Actor position / orientation");
}
public void RotateActor(float deltaAngle)
{
if (Workspace.selectedBodyPart == null)
{ BodyAngle += deltaAngle; }
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
{ HeadAngle += deltaAngle; }
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "appendage")
{ GenitalAngle -= deltaAngle; }
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
Workspace.RecordEvent("Actor position / orientation");
}
public void FaceActor(int facing)
{
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
if (Workspace.selectedBodyPart == null)
{ keyframe.BodyFacing = facing; }
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
{ keyframe.HeadFacing = facing; }
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
Workspace.RecordEvent("Actor position / orientation");
}
// Pre-save / post-load
public void OnPreSave()
{
@ -157,7 +214,7 @@ namespace RimWorldAnimationStudio
foreach (AddonKeyframe addonKeyframe in AddonKeyframes)
{
ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID, out int clipID).GetActorAddon(addonKeyframe.AddonName);
ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID).GetActorAddon(addonKeyframe.AddonName);
if (addon.Render)
{ addonKeyframes.Add(addonKeyframe.Copy()); }