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.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
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user