Skip to content

Commit

Permalink
Auto Backup/Save poses (#114)
Browse files Browse the repository at this point in the history
* Add ability to automatically save poses on a set interval

This is disabled by default, but allows you set an interval and location for saving.
Some refactors were needed to make the exporting and importing features usable from multiple locations.

* Fix formatting

* Add the auto save path to the custom sidebar items if it is enabled.

This allows easier access to autosaves.

* Format the interval with '%d s' to indicate seconds

* Formatting fixes

* Set default for `ClearAutoSavesOnExit` to `false`.

* Refactor `PluginLog` => `Logger`

* Refactor `ActorsList.GetSelectorList` => `SavedObjects`

* Run `PoseAutoSave.Save` from Framework thread

* Introduce mutex lock for `PoseAutoSave.Disable`

---------

Co-authored-by: chirp <[email protected]>
  • Loading branch information
Cazzar and chirpxiv authored Oct 4, 2023
1 parent 82be2f5 commit e5dc208
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 114 deletions.
16 changes: 12 additions & 4 deletions Ktisis/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Numerics;
using System.Collections.Generic;
using System.IO;

using ImGuizmoNET;

Expand Down Expand Up @@ -65,6 +66,13 @@ public class Configuration : IPluginConfiguration {
public float SkeletonLineOpacityWhileUsing { get; set; } = 0.15F;
public float SkeletonDotRadius { get; set; } = 3.0F;

//AutoSave
public bool EnableAutoSave { get; set; } = false;
public int AutoSaveInterval { get; set; } = 60;
public int AutoSaveCount { get; set; } = 5;
public string AutoSavePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Ktisis", "PoseAutoBackup");
public bool ClearAutoSavesOnExit { get; set; } = false;

// References
// The reference Key creates a uniqueness constraint for imgui window IDs for each reference.
public Dictionary<int, ReferenceInfo> References { get; set; } = new();
Expand Down Expand Up @@ -138,15 +146,15 @@ public bool IsBoneCategoryVisible(Category category) {
public bool EnableParenting { get; set; } = true;

public bool LinkedGaze { get; set; } = true;

public bool ShowToolbar { get; set; } = false;

public Dictionary<string, string> SavedDirPaths { get; set; } = new();

// Camera

public float FreecamMoveSpeed { get; set; } = 0.1f;

public float FreecamShiftMuli { get; set; } = 2.5f;
public float FreecamCtrlMuli { get; set; } = 0.25f;
public float FreecamUpDownMuli { get; set; } = 1f;
Expand All @@ -159,7 +167,7 @@ public bool IsBoneCategoryVisible(Category category) {
public Keybind FreecamRight { get; set; } = new(VirtualKey.D);
public Keybind FreecamUp { get; set; } = new(VirtualKey.SPACE);
public Keybind FreecamDown { get; set; } = new(VirtualKey.Q);

public Keybind FreecamFast { get; set; } = new(VirtualKey.SHIFT);
public Keybind FreecamSlow { get; set; } = new(VirtualKey.CONTROL);

Expand Down
107 changes: 107 additions & 0 deletions Ktisis/Helpers/PoseHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System.IO;
using System.Collections.Generic;

using Ktisis.Data.Files;
using Ktisis.Data.Serialization;
using Ktisis.Structs.Actor;
using Ktisis.Structs.Poses;
using Ktisis.Interop.Hooks;

namespace Ktisis.Helpers {
internal class PoseHelpers {
public unsafe static void ExportPose(Actor* actor, string path, PoseMode modes) {
var model = actor->Model;
if (model == null) return;

var skeleton = model->Skeleton;
if (skeleton == null) return;

var pose = new PoseFile {
Position = model->Position,
Rotation = model->Rotation,
Scale = model->Scale,
Bones = new ()
};

pose.Bones.Store(skeleton);

if (modes.HasFlag(PoseMode.Weapons)) {
var main = actor->GetWeaponSkeleton(WeaponSlot.MainHand);
if (main != null) {
pose.MainHand = new ();
pose.MainHand.Store(main);
}

var off = actor->GetWeaponSkeleton(WeaponSlot.OffHand);
if (off != null) {
pose.OffHand = new ();
pose.OffHand.Store(off);
}

var prop = actor->GetWeaponSkeleton(WeaponSlot.Prop);
if (prop != null) {
pose.Prop = new ();
pose.Prop.Store(prop);
}
}

var json = JsonParser.Serialize(pose);

using var file = new StreamWriter(path);
file.Write(json);
}

public unsafe static void ImportPose(Actor* actor, List<string> path, PoseMode modes) {
var content = File.ReadAllText(path[0]);
var pose = JsonParser.Deserialize<PoseFile>(content);
if (pose == null) return;

if (actor->Model == null) return;

var skeleton = actor->Model->Skeleton;
if (skeleton == null) return;

pose.ConvertLegacyBones();

// Ensure posing is enabled.
if (!PoseHooks.PosingEnabled && !PoseHooks.AnamPosingEnabled)
PoseHooks.EnablePosing();

if (pose.Bones != null) {
for (var p = 0; p < skeleton->PartialSkeletonCount; p++) {
switch (p) {
case 0:
if (!modes.HasFlag(PoseMode.Body)) continue;
break;
case 1:
if (!modes.HasFlag(PoseMode.Face)) continue;
break;
}

pose.Bones.ApplyToPartial(skeleton, p, Ktisis.Configuration.PoseTransforms);
}
}

if (modes.HasFlag(PoseMode.Weapons)) {
var wepTrans = Ktisis.Configuration.PoseTransforms;
if (Ktisis.Configuration.PositionWeapons)
wepTrans |= PoseTransforms.Position;

if (pose.MainHand != null) {
var skele = actor->GetWeaponSkeleton(WeaponSlot.MainHand);
if (skele != null) pose.MainHand.Apply(skele, wepTrans);
}

if (pose.OffHand != null) {
var skele = actor->GetWeaponSkeleton(WeaponSlot.OffHand);
if (skele != null) pose.OffHand.Apply(skele, wepTrans);
}

if (pose.Prop != null) {
var skele = actor->GetWeaponSkeleton(WeaponSlot.Prop);
if (skele != null) pose.Prop.Apply(skele, wepTrans);
}
}
}
}
}
4 changes: 1 addition & 3 deletions Ktisis/Interface/Components/ActorsList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

namespace Ktisis.Interface.Components {
internal static class ActorsList {

private static List<long> SavedObjects = new();
internal static List<long> SavedObjects = new();
private static List<DalamudGameObject>? SelectorList = null;
private static string Search = "";
private static readonly HashSet<ObjectKind> WhitelistObjectKinds = new(){
Expand All @@ -29,7 +28,6 @@ internal static class ActorsList {
// TODO to clear the list on gpose leave
public static void Clear() => SavedObjects.Clear();


// Draw

public unsafe static void Draw() {
Expand Down
9 changes: 9 additions & 0 deletions Ktisis/Interface/KtisisGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ static KtisisGui() {
FontAwesomeIcon.None,
0
));

if (Ktisis.Configuration.EnableAutoSave) {
FileDialogManager.CustomSideBarItems.Add((
"AutoSave",
Ktisis.Configuration.AutoSavePath,
FontAwesomeIcon.Bookmark,
-1
));
}
}

public static void Draw() {
Expand Down
55 changes: 42 additions & 13 deletions Ktisis/Interface/Windows/ConfigGui.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Numerics;
using System.Collections.Generic;

using ImGuiNET;

Expand Down Expand Up @@ -75,6 +75,8 @@ public static void Draw() {
DrawInputTab(cfg);
if (ImGui.BeginTabItem(Locale.GetString("Camera")))
DrawCameraTab(cfg);
if (ImGui.BeginTabItem(Locale.GetString("AutoSave")))
DrawAutoSaveTab(cfg);
if (ImGui.BeginTabItem(Locale.GetString("References")))
DrawReferencesTab(cfg);
if (ImGui.BeginTabItem(Locale.GetString("Language")))
Expand Down Expand Up @@ -150,7 +152,7 @@ public static void DrawInterfaceTab(Configuration cfg) {
var displayMultiplierInputs = cfg.TransformTableDisplayMultiplierInputs;
if (ImGui.Checkbox(Locale.GetString("Show_speed_multipler_inputs"), ref displayMultiplierInputs))
cfg.TransformTableDisplayMultiplierInputs = displayMultiplierInputs;

var showToolbar = cfg.ShowToolbar;
if (ImGui.Checkbox("Show Experimental Toolbar", ref showToolbar))
cfg.ShowToolbar = showToolbar;
Expand All @@ -174,13 +176,13 @@ public static void DrawInterfaceTab(Configuration cfg) {

public static void DrawOverlayTab(Configuration cfg) {
ImGui.Spacing();

var order = cfg.OrderBoneListByDistance;
if (ImGui.Checkbox("Order bone list by distance from camera", ref order))
cfg.OrderBoneListByDistance = order;

ImGui.Spacing();

if (ImGui.CollapsingHeader(Locale.GetString("Skeleton_lines_and_dots"), ImGuiTreeNodeFlags.DefaultOpen)) {
ImGui.Separator();
var drawLines = cfg.DrawLinesOnSkeleton;
Expand All @@ -202,11 +204,11 @@ public static void DrawOverlayTab(Configuration cfg) {
var lineThickness = cfg.SkeletonLineThickness;
if (ImGui.SliderFloat(Locale.GetString("Lines_thickness"), ref lineThickness, 0.01F, 15F, "%.1f"))
cfg.SkeletonLineThickness = lineThickness;

var lineOpacity = cfg.SkeletonLineOpacity;
if (ImGui.SliderFloat(Locale.GetString("Lines_opacity"), ref lineOpacity, 0.01F, 1F, "%.2f"))
cfg.SkeletonLineOpacity = lineOpacity;

var lineOpacityWhileUsing = cfg.SkeletonLineOpacityWhileUsing;
if (ImGui.SliderFloat(Locale.GetString("Lines_opacity_while_using"), ref lineOpacityWhileUsing, 0.01F, 1F, "%.2f"))
cfg.SkeletonLineOpacityWhileUsing = lineOpacityWhileUsing;
Expand Down Expand Up @@ -270,6 +272,33 @@ public static void DrawGizmoTab(Configuration cfg) {
ImGui.EndTabItem();
}

// AutoSave
public static void DrawAutoSaveTab(Configuration cfg) {
var enableAutoSave = cfg.EnableAutoSave;
if (ImGui.Checkbox(Locale.GetString("Enable_auto_save"), ref enableAutoSave))
cfg.EnableAutoSave = enableAutoSave;

var clearOnExit = cfg.ClearAutoSavesOnExit;
if (ImGui.Checkbox(Locale.GetString("Clear_auto_saves_on_exit"), ref clearOnExit))
cfg.ClearAutoSavesOnExit = clearOnExit;

ImGui.Spacing();

var autoSaveInterval = cfg.AutoSaveInterval;
if (ImGui.SliderInt(Locale.GetString("Auto_save_interval"), ref autoSaveInterval, 10, 600, "%d s"))
cfg.AutoSaveInterval = autoSaveInterval;

var autoSaveCount = cfg.AutoSaveCount;
if (ImGui.SliderInt(Locale.GetString("Auto_save_count"), ref autoSaveCount, 1, 20))
cfg.AutoSaveCount = autoSaveCount;

var autoSavePath = cfg.AutoSavePath;
if (ImGui.InputText(Locale.GetString("Auto_save_path"), ref autoSavePath, 256))
cfg.AutoSavePath = autoSavePath;

ImGui.EndTabItem();
}

// Language

public static void DrawLanguageTab(Configuration cfg) {
Expand Down Expand Up @@ -421,11 +450,11 @@ private static void DrawCameraTab(Configuration cfg) {
var shiftMuli = cfg.FreecamShiftMuli;
if (ImGui.DragFloat("Fast speed multiplier", ref shiftMuli, 0.001f, 0, 10))
cfg.FreecamShiftMuli = shiftMuli;

var ctrlMuli = cfg.FreecamCtrlMuli;
if (ImGui.DragFloat("Slow speed multiplier", ref ctrlMuli, 0.001f, 0, 10))
cfg.FreecamCtrlMuli = ctrlMuli;

var upDownMuli = cfg.FreecamUpDownMuli;
if (ImGui.DragFloat("Up/down speed multiplier", ref upDownMuli, 0.001f, 0, 10))
cfg.FreecamUpDownMuli = upDownMuli;
Expand All @@ -437,7 +466,7 @@ private static void DrawCameraTab(Configuration cfg) {
cfg.FreecamSensitivity = camSens;

ImGui.Spacing();

ImGui.PushItemWidth(ImGui.GetFontSize() * 8);

ImGui.Text("Work camera keybinds");
Expand All @@ -450,17 +479,17 @@ private static void DrawCameraTab(Configuration cfg) {
KeybindEdit.Draw("Down##WCDown", cfg.FreecamDown);

ImGui.Spacing();

KeybindEdit.Draw("Fast speed modifier##WCUp", cfg.FreecamFast);
KeybindEdit.Draw("Slow speed modifier##WCUp", cfg.FreecamSlow);

ImGui.PopItemWidth();

ImGui.EndTabItem();
}

// Data

public static void DrawDataTab(Configuration cfg) {
ImGui.Spacing();
var validGlamPlatesFound = GlamourDresser.CountValid();
Expand Down
Loading

0 comments on commit e5dc208

Please sign in to comment.