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 {