Experimental: set position in render hook so root translation applies without movement

This commit is contained in:
RisaDev
2024-11-21 05:44:28 +03:00
parent 05b845a50f
commit 35dee4e076

View File

@@ -14,6 +14,7 @@ using CustomizePlus.Profiles.Data;
using CustomizePlus.Profiles.Events; using CustomizePlus.Profiles.Events;
using CustomizePlus.Templates.Events; using CustomizePlus.Templates.Events;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
@@ -36,6 +37,12 @@ public unsafe sealed class ArmatureManager : IDisposable
private readonly ActorManager _actorManager; private readonly ActorManager _actorManager;
private readonly ArmatureChanged _event; private readonly ArmatureChanged _event;
/// <summary>
/// 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.
/// </summary>
private readonly bool[] _objectMovementFlagsArr = new bool[1000];
public Dictionary<ActorIdentifier, Armature> Armatures { get; private set; } = new(); public Dictionary<ActorIdentifier, Armature> Armatures { get; private set; } = new();
public ArmatureManager( public ArmatureManager(
@@ -96,7 +103,10 @@ public unsafe sealed class ArmatureManager : IDisposable
return; return;
if (Armatures.TryGetValue(identifier, out var armature) && armature.IsBuilt && armature.IsVisible) if (Armatures.TryGetValue(identifier, out var armature) && armature.IsBuilt && armature.IsVisible)
{
_objectMovementFlagsArr[actor.AsObject->ObjectIndex] = true;
ApplyRootTranslation(armature, actor); ApplyRootTranslation(armature, actor);
}
} }
/// <summary> /// <summary>
@@ -122,12 +132,16 @@ public unsafe sealed class ArmatureManager : IDisposable
var armature = kvPair.Value; var armature = kvPair.Value;
//Only remove armatures which haven't been seen for a while //Only remove armatures which haven't been seen for a while
//But remove armatures of special actors (like examine screen) right away //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)) (armature.LastSeen <= armatureExpirationDateTime || armature.ActorIdentifier.Type == IdentifierType.Special))
{ {
_logger.Debug($"Removing armature {armature} because {kvPair.Key.IncognitoDebug()} is gone"); _logger.Debug($"Removing armature {armature} because {kvPair.Key.IncognitoDebug()} is gone");
RemoveArmature(armature, ArmatureChanged.DeletionReason.Gone); RemoveArmature(armature, ArmatureChanged.DeletionReason.Gone);
//Reset root translation
foreach (var obj in actorData.Objects)
ApplyRootTranslation(null, obj);
continue; continue;
} }
@@ -170,6 +184,11 @@ public unsafe sealed class ArmatureManager : IDisposable
{ {
_logger.Debug($"Removing armature {armature} because it doesn't have any active profiles"); _logger.Debug($"Removing armature {armature} because it doesn't have any active profiles");
RemoveArmature(armature, ArmatureChanged.DeletionReason.NoActiveProfiles); RemoveArmature(armature, ArmatureChanged.DeletionReason.NoActiveProfiles);
//Reset root translation
foreach (var actor in obj.Value.Objects)
ApplyRootTranslation(null, actor);
continue; continue;
} }
@@ -198,7 +217,14 @@ public unsafe sealed class ArmatureManager : IDisposable
if (armature.IsBuilt && armature.IsVisible && _objectManager.TryGetValue(armature.ActorIdentifier, out var actorData)) if (armature.IsBuilt && armature.IsVisible && _objectManager.TryGetValue(armature.ActorIdentifier, out var actorData))
{ {
foreach (var actor in actorData.Objects) foreach (var actor in actorData.Objects)
{
ApplyPiecewiseTransformation(armature, actor, armature.ActorIdentifier); ApplyPiecewiseTransformation(armature, actor, armature.ActorIdentifier);
if (!_objectMovementFlagsArr[actor.AsObject->ObjectIndex])
ApplyRootTranslation(armature, actor);
_objectMovementFlagsArr[actor.AsObject->ObjectIndex] = false;
}
} }
} }
} }
@@ -298,14 +324,25 @@ public unsafe sealed class ArmatureManager : IDisposable
} }
} }
private void ApplyRootTranslation(Armature arm, Actor actor) /// <summary>
/// Apply root bone translation. If null armature is passed then position is reset.
/// </summary>
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 //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 //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; var cBase = actor.Model.AsCharacterBase;
if (cBase != null) if (cBase != null)
{ {
if(arm == null)
{
cBase->DrawObject.Object.Position = actor.AsObject->Position;
return;
}
var rootBoneTransform = arm.GetAppliedBoneTransform("n_root"); var rootBoneTransform = arm.GetAppliedBoneTransform("n_root");
if (rootBoneTransform == null) if (rootBoneTransform == null)
return; return;
@@ -315,8 +352,8 @@ public unsafe sealed class ArmatureManager : IDisposable
rootBoneTransform.Translation.Z == 0) rootBoneTransform.Translation.Z == 0)
return; return;
if (!cBase->DrawObject.IsVisible) //Reset position so we don't fly away
return; cBase->DrawObject.Object.Position = actor.AsObject->Position;
var newPosition = new FFXIVClientStructs.FFXIV.Common.Math.Vector3 var newPosition = new FFXIVClientStructs.FFXIV.Common.Math.Vector3
{ {