From 35dee4e076507afb0900a63e3b3f23973df6ad68 Mon Sep 17 00:00:00 2001
From: RisaDev <151885272+RisaDev@users.noreply.github.com>
Date: Thu, 21 Nov 2024 05:44:28 +0300
Subject: [PATCH] Experimental: set position in render hook so root translation
applies without movement
---
.../Armatures/Services/ArmatureManager.cs | 45 +++++++++++++++++--
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/CustomizePlus/Armatures/Services/ArmatureManager.cs b/CustomizePlus/Armatures/Services/ArmatureManager.cs
index 19ecd9b..b12fe41 100644
--- a/CustomizePlus/Armatures/Services/ArmatureManager.cs
+++ b/CustomizePlus/Armatures/Services/ArmatureManager.cs
@@ -14,6 +14,7 @@
using CustomizePlus.Profiles.Events;
using CustomizePlus.Templates.Events;
using Dalamud.Plugin.Services;
+using Lumina.Excel.Sheets;
using OtterGui.Classes;
using OtterGui.Log;
using Penumbra.GameData.Actors;
@@ -36,6 +37,12 @@ public unsafe sealed class ArmatureManager : IDisposable
private readonly ActorManager _actorManager;
private readonly ArmatureChanged _event;
+ ///
+ /// This is a movement flag for every object. Used to prevent calls to ApplyRootTranslation from both movement and render hooks.
+ /// I know there are less than 1000 objects in object table but I want to be semi-protected from object table getting bigger in the future.
+ ///
+ private readonly bool[] _objectMovementFlagsArr = new bool[1000];
+
public Dictionary Armatures { get; private set; } = new();
public ArmatureManager(
@@ -96,7 +103,10 @@ public void OnGameObjectMove(Actor actor)
return;
if (Armatures.TryGetValue(identifier, out var armature) && armature.IsBuilt && armature.IsVisible)
+ {
+ _objectMovementFlagsArr[actor.AsObject->ObjectIndex] = true;
ApplyRootTranslation(armature, actor);
+ }
}
///
@@ -122,12 +132,16 @@ private void RefreshArmatures()
var armature = kvPair.Value;
//Only remove armatures which haven't been seen for a while
//But remove armatures of special actors (like examine screen) right away
- if (!_objectManager.Identifiers.ContainsKey(kvPair.Value.ActorIdentifier) &&
+ if (!_objectManager.Identifiers.TryGetValue(kvPair.Value.ActorIdentifier, out var actorData) &&
(armature.LastSeen <= armatureExpirationDateTime || armature.ActorIdentifier.Type == IdentifierType.Special))
{
_logger.Debug($"Removing armature {armature} because {kvPair.Key.IncognitoDebug()} is gone");
RemoveArmature(armature, ArmatureChanged.DeletionReason.Gone);
+ //Reset root translation
+ foreach (var obj in actorData.Objects)
+ ApplyRootTranslation(null, obj);
+
continue;
}
@@ -170,6 +184,11 @@ private void RefreshArmatures()
{
_logger.Debug($"Removing armature {armature} because it doesn't have any active profiles");
RemoveArmature(armature, ArmatureChanged.DeletionReason.NoActiveProfiles);
+
+ //Reset root translation
+ foreach (var actor in obj.Value.Objects)
+ ApplyRootTranslation(null, actor);
+
continue;
}
@@ -198,7 +217,14 @@ private unsafe void ApplyArmatureTransforms()
if (armature.IsBuilt && armature.IsVisible && _objectManager.TryGetValue(armature.ActorIdentifier, out var actorData))
{
foreach (var actor in actorData.Objects)
+ {
ApplyPiecewiseTransformation(armature, actor, armature.ActorIdentifier);
+
+ if (!_objectMovementFlagsArr[actor.AsObject->ObjectIndex])
+ ApplyRootTranslation(armature, actor);
+
+ _objectMovementFlagsArr[actor.AsObject->ObjectIndex] = false;
+ }
}
}
}
@@ -298,14 +324,25 @@ private void ApplyPiecewiseTransformation(Armature armature, Actor actor, ActorI
}
}
- private void ApplyRootTranslation(Armature arm, Actor actor)
+ ///
+ /// Apply root bone translation. If null armature is passed then position is reset.
+ ///
+ private void ApplyRootTranslation(Armature? arm, Actor actor)
{
//I'm honestly not sure if we should or even can check if cBase->DrawObject or cBase->DrawObject.Object is a valid object
//So for now let's assume we don't need to check for that
+ //2024/11/21: we no longer check cBase->DrawObject.IsVisible here so we can set object position in render hook.
+
var cBase = actor.Model.AsCharacterBase;
if (cBase != null)
{
+ if(arm == null)
+ {
+ cBase->DrawObject.Object.Position = actor.AsObject->Position;
+ return;
+ }
+
var rootBoneTransform = arm.GetAppliedBoneTransform("n_root");
if (rootBoneTransform == null)
return;
@@ -315,8 +352,8 @@ private void ApplyRootTranslation(Armature arm, Actor actor)
rootBoneTransform.Translation.Z == 0)
return;
- if (!cBase->DrawObject.IsVisible)
- return;
+ //Reset position so we don't fly away
+ cBase->DrawObject.Object.Position = actor.AsObject->Position;
var newPosition = new FFXIVClientStructs.FFXIV.Common.Math.Vector3
{