Experimental: set position in render hook so root translation applies without movement
This commit is contained in:
@@ -14,6 +14,7 @@ using CustomizePlus.Profiles.Data;
|
||||
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;
|
||||
|
||||
/// <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 ArmatureManager(
|
||||
@@ -96,7 +103,10 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
return;
|
||||
|
||||
if (Armatures.TryGetValue(identifier, out var armature) && armature.IsBuilt && armature.IsVisible)
|
||||
{
|
||||
_objectMovementFlagsArr[actor.AsObject->ObjectIndex] = true;
|
||||
ApplyRootTranslation(armature, actor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,12 +132,16 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
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 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
{
|
||||
_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 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
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 @@ 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
|
||||
//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 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user