Skip to content

Commit

Permalink
Merge pull request #1280 from AsgardXIV/brio2
Browse files Browse the repository at this point in the history
Brio Work
  • Loading branch information
Luminiari authored Jan 20, 2023
2 parents bc0e282 + fabff7e commit e47e039
Show file tree
Hide file tree
Showing 13 changed files with 548 additions and 409 deletions.
3 changes: 3 additions & 0 deletions Anamnesis/Actor/Refresh/AnamnesisActorRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public class AnamnesisActorRefresher : IActorRefresher
{
public bool CanRefresh(ActorMemory actor)
{
if (PoseService.Instance.IsEnabled)
return false;

// Ana can't refresh gpose actors
if (actor.IsGPoseActor)
return false;
Expand Down
51 changes: 49 additions & 2 deletions Anamnesis/Actor/Refresh/BrioActorRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
namespace Anamnesis.Actor.Refresh;

using System.Threading.Tasks;
using Anamnesis.Brio;
using Anamnesis.Files;
using Anamnesis.Memory;
using Anamnesis.Services;
using static Anamnesis.Memory.ActorBasicMemory;
using XivToolsWpf;

public class BrioActorRefresher : IActorRefresher
{
Expand All @@ -21,6 +23,51 @@ public bool CanRefresh(ActorMemory actor)

public async Task RefreshActor(ActorMemory actor)
{
await Brio.Brio.Redraw(actor.ObjectIndex);
await Dispatch.MainThread();
bool isPosing = PoseService.Instance.IsEnabled;

RedrawType redrawType = RedrawType.AllowFull | RedrawType.PreservePosition | RedrawType.AllowOptimized;

if(actor.IsWeaponDirty && !isPosing)
{
redrawType |= RedrawType.RedrawWeaponsOnOptimized;
}

if (actor.IsWeaponDirty && isPosing)
{
redrawType &= ~RedrawType.AllowOptimized;
}

if (isPosing)
{
// Save the current pose
PoseFile poseFile = new PoseFile();
SkeletonVisual3d skeletonVisual3D = new SkeletonVisual3d();
await skeletonVisual3D.SetActor(actor);
poseFile.WriteToFile(actor, skeletonVisual3D, null);

// Redraw
var result = await Brio.Redraw(actor.ObjectIndex, redrawType);
if(result == "\"Full\"")
{
// TODO: It's probably best to find some way to detect when it's safe
// this is a good first attempt though.
new Task(async () =>
{
await Task.Delay(500);
await Dispatch.MainThread();

// Restore current pose
skeletonVisual3D = new SkeletonVisual3d();
await skeletonVisual3D.SetActor(actor);
await poseFile.Apply(actor, skeletonVisual3D, null, PoseFile.Mode.All);
}).Start();
}
}
else
{
// Outside of pose mode we can just refresh
await Brio.Redraw(actor.ObjectIndex, redrawType);
}
}
}
3 changes: 3 additions & 0 deletions Anamnesis/Actor/Refresh/PenumbraActorRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public bool CanRefresh(ActorMemory actor)
if (!SettingsService.Current.UseExternalRefresh)
return false;

if (PoseService.Instance.IsEnabled)
return false;

// Penumbra can't refresh world frozen actors
if (PoseService.Instance.FreezeWorldPosition)
return false;
Expand Down
33 changes: 29 additions & 4 deletions Anamnesis/Brio/Brio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,47 @@

namespace Anamnesis.Brio;

using System;
using System.Threading.Tasks;

public static class Brio
{
public static async Task Redraw(int targetIndex)
public static async Task<string> Redraw(int targetIndex, RedrawType redrawType)
{
RedrawData data = new();
data.ObjectIndex = targetIndex;
data.RedrawType = redrawType;

await BrioApi.Post("/redraw", data);
var result = await BrioApi.Post("/redraw", data);

await Task.Delay(500);

return result;
}

public class RedrawData
public static async Task<int> Spawn()
{
public int ObjectIndex { get; set; } = -1;
var resultRaw = await BrioApi.Post("/spawn", 0);
var resultId = int.Parse(resultRaw);
await Task.Delay(500);
return resultId;
}
}

[Flags]
public enum RedrawType
{
None = 0,
AllowOptimized = 1,
AllowFull = 2,
RedrawWeaponsOnOptimized = 4,
PreservePosition = 8,

All = AllowOptimized | AllowFull | RedrawWeaponsOnOptimized | PreservePosition,
}

public class RedrawData
{
public int ObjectIndex { get; set; } = -1;
public RedrawType? RedrawType { get; set; } = Anamnesis.Brio.RedrawType.All;
}
23 changes: 8 additions & 15 deletions Anamnesis/Brio/BrioApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,13 @@ internal static class BrioApi
private const string Url = "http://localhost:42428/brio";
private const int TimeoutMs = 500;

public static async Task Post(string route, object content)
public static async Task<string> Post(string route, object content)
{
await PostRequest(route, content);
var response = await PostRequest(route, content);
return response;
}

public static async Task<T> Post<T>(string route, object content)
where T : notnull
{
HttpResponseMessage response = await PostRequest(route, content);

using StreamReader? sr = new StreamReader(await response.Content.ReadAsStreamAsync());
string json = sr.ReadToEnd();

return SerializerService.Deserialize<T>(json);
}

private static async Task<HttpResponseMessage> PostRequest(string route, object content)
private static async Task<string> PostRequest(string route, object content)
{
if (!route.StartsWith('/'))
route = '/' + route;
Expand All @@ -49,8 +39,11 @@ private static async Task<HttpResponseMessage> PostRequest(string route, object
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

using var response = await client.PostAsync(Url + route, byteContent);
using var responseContent = await response.Content.ReadAsStreamAsync();
using StreamReader? sr = new StreamReader(responseContent);
string body = sr.ReadToEnd();

return response;
return body;
}
catch (Exception ex)
{
Expand Down
3 changes: 2 additions & 1 deletion Anamnesis/Languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"Target_Other": "Others",
"Target_Hidden": "Show Hidden/Unloaded",
"Target_Gpose": "(GPose)",
"Target_SpawnActor": "Spawn",

"SubActor_Title": "Sub Actors",
"SubActor_Mount": "Mount",
Expand Down Expand Up @@ -677,7 +678,7 @@
"Settings_DefaultAuthor": "Default Author Name",
"Settings_ExternalHeader": "External Services",
"Settings_UseExternalRefresh": "Use Penumbra Redraw",
"Settings_UseExternalRefreshBrio": "Use Brio Redraw",
"Settings_UseExternalRefreshBrio": "Enable Brio Integration",
"Settings_UseExternalRefreshWarning": "Requires the HTTP API to be enabled in Penumbra/Brio. This option is found under the Advanced/Integrations section of the Settings tab of the plugins.",
"Settings_EnableNpcHack": "Enable NPC Faces on PC",
"Settings_EnableNpcHackWarning": "This will break Penumbra Collections.",
Expand Down
5 changes: 5 additions & 0 deletions Anamnesis/Memory/ActorMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public enum CharacterFlagDefs : byte

public bool AutomaticRefreshEnabled { get; set; } = true;
public bool IsRefreshing { get; set; } = false;
public bool IsWeaponDirty { get; set; } = false;
public bool PendingRefresh => this.refreshQueue.Pending;

[DependsOn(nameof(IsValid), nameof(IsOverworldActor), nameof(Name), nameof(RenderMode))]
Expand Down Expand Up @@ -181,6 +182,7 @@ public async Task RefreshAsync()
finally
{
this.IsRefreshing = false;
this.IsWeaponDirty = false;
this.WriteDelayedBinds();
}

Expand Down Expand Up @@ -223,6 +225,9 @@ protected override void HandlePropertyChanged(PropertyChange change)

if (change.OriginBind.Flags.HasFlag(BindFlags.ActorRefresh) && change.Origin != PropertyChange.Origins.Game)
{
if (change.OriginBind.Flags.HasFlag(BindFlags.WeaponRefresh))
this.IsWeaponDirty = true;

this.Refresh();
}
}
Expand Down
1 change: 1 addition & 0 deletions Anamnesis/Memory/Binds/BindFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public enum BindFlags
DontCacheOffsets = 4,
OnlyInGPose = 8,
DontRecordHistory = 16,
WeaponRefresh = 32,
}
8 changes: 4 additions & 4 deletions Anamnesis/Memory/WeaponMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ public enum WeaponFlagDefs : byte
WeaponHidden = 1 << 1,
}

[Bind(0x000, BindFlags.ActorRefresh)] public ushort Set { get; set; }
[Bind(0x002, BindFlags.ActorRefresh)] public ushort Base { get; set; }
[Bind(0x004, BindFlags.ActorRefresh)] public ushort Variant { get; set; }
[Bind(0x006, BindFlags.ActorRefresh)] public byte Dye { get; set; }
[Bind(0x000, BindFlags.ActorRefresh | BindFlags.WeaponRefresh)] public ushort Set { get; set; }
[Bind(0x002, BindFlags.ActorRefresh | BindFlags.WeaponRefresh)] public ushort Base { get; set; }
[Bind(0x004, BindFlags.ActorRefresh | BindFlags.WeaponRefresh)] public ushort Variant { get; set; }
[Bind(0x006, BindFlags.ActorRefresh | BindFlags.WeaponRefresh)] public byte Dye { get; set; }
[Bind(0x008, BindFlags.Pointer)] public WeaponModelMemory? Model { get; set; }
[Bind(0x040)] public bool IsSheathed { get; set; }
[Bind(0x05C)] public WeaponFlagDefs WeaponFlags { get; set; }
Expand Down
3 changes: 0 additions & 3 deletions Anamnesis/Services/ActorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ public class ActorService : ServiceBase<ActorService>

public bool CanRefreshActor(ActorMemory actor)
{
if (PoseService.Instance.IsEnabled)
return false;

if (!actor.IsValid)
return false;

Expand Down
Loading

0 comments on commit e47e039

Please sign in to comment.