Change mousewheel to ctrl, current material state.

This commit is contained in:
Ottermandias 2024-02-06 16:42:43 +01:00
parent 42ac507b86
commit b5b9289dc2
20 changed files with 537 additions and 296 deletions

View file

@ -6,6 +6,8 @@ using Glamourer.Interop.Structs;
using Glamourer.State;
using OtterGui.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
using Penumbra.GameData.Structs;
namespace Glamourer.Interop.Material;
@ -19,6 +21,8 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
private int _lastSlot;
private readonly ThreadLocal<List<MaterialValueIndex>> _deleteList = new(() => []);
public MaterialManager(PrepareColorSet prepareColorSet, StateManager stateManager, ActorManager actors, PenumbraService penumbra)
{
_stateManager = stateManager;
@ -39,7 +43,8 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
var (slotId, materialId) = FindMaterial(characterBase, material);
if (!validType
|| slotId == byte.MaxValue
|| slotId > 9
|| type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0
|| !actor.Identifier(_actors, out var identifier)
|| !_stateManager.TryGetValue(identifier, out var state))
return;
@ -53,38 +58,60 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
if (!PrepareColorSet.TryGetColorTable(characterBase, material, stain, out var baseColorSet))
return;
for (var i = 0; i < values.Length; ++i)
var drawData = type switch
{
var idx = MaterialValueIndex.FromKey(values[i].key);
var (oldGame, model, source) = values[i].Value;
ref var row = ref baseColorSet[idx.RowIndex];
if (!idx.DataIndex.TryGetValue(row, out var newGame))
continue;
if (newGame == oldGame)
{
idx.DataIndex.SetValue(ref row, model);
}
else
{
switch (source.Base())
{
case StateSource.Manual:
_stateManager.ChangeMaterialValue(state, idx, Vector3.Zero, Vector3.Zero, ApplySettings.Game);
--i;
break;
case StateSource.Fixed:
idx.DataIndex.SetValue(ref row, model);
state.Materials.UpdateValue(idx, new MaterialValueState(newGame, model, source), out _);
break;
}
}
}
MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, slotId),
_ => GetTempSlot((Weapon*)characterBase),
};
UpdateMaterialValues(state, values, drawData, ref baseColorSet);
if (MaterialService.GenerateNewColorTable(baseColorSet, out var texture))
ret = (nint)texture;
}
/// <summary> Update and apply the glamourer state of an actor according to the application sources when updated by the game. </summary>
private void UpdateMaterialValues(ActorState state, ReadOnlySpan<(uint Key, MaterialValueState Value)> values, CharacterWeapon drawData,
ref MtrlFile.ColorTable colorTable)
{
var deleteList = _deleteList.Value!;
deleteList.Clear();
for (var i = 0; i < values.Length; ++i)
{
var idx = MaterialValueIndex.FromKey(values[i].Key);
var materialValue = values[i].Value;
ref var row = ref colorTable[idx.RowIndex];
var newGame = new ColorRow(row);
if (materialValue.EqualGame(newGame, drawData))
materialValue.Model.Apply(ref row);
else
switch (materialValue.Source)
{
case StateSource.Pending:
materialValue.Model.Apply(ref row);
state.Materials.UpdateValue(idx, new MaterialValueState(newGame, materialValue.Model, drawData, StateSource.Manual),
out _);
break;
case StateSource.IpcManual:
case StateSource.Manual:
deleteList.Add(idx);
break;
case StateSource.Fixed:
case StateSource.IpcFixed:
materialValue.Model.Apply(ref row);
state.Materials.UpdateValue(idx, new MaterialValueState(newGame, materialValue.Model, drawData, materialValue.Source),
out _);
break;
}
}
foreach (var idx in deleteList)
_stateManager.ChangeMaterialValue(state, idx, default, ApplySettings.Game);
}
/// <summary>
/// Find the index of a material by searching through a draw objects pointers.
/// Tries to take shortcuts for consecutive searches like when a character is newly created.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private (byte SlotId, byte MaterialId) FindMaterial(CharacterBase* characterBase, MaterialResourceHandle* material)
{
@ -119,6 +146,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
return (byte.MaxValue, byte.MaxValue);
}
/// <summary> Find the type of the given draw object by checking the actors pointers. </summary>
private static bool FindType(CharacterBase* characterBase, Actor actor, out MaterialValueIndex.DrawObjectType type)
{
type = MaterialValueIndex.DrawObjectType.Human;
@ -145,4 +173,26 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
return false;
}
/// <summary> We need to get the temporary set, variant and stain that is currently being set if it is available. </summary>
private CharacterWeapon GetTempSlot(Human* human, byte slotId)
{
if (human->ChangedEquipData == null)
return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0);
return ((CharacterArmor*)human->ChangedEquipData + slotId * 3)->ToWeapon(0);
}
/// <summary>
/// We need to get the temporary set, variant and stain that is currently being set if it is available.
/// Weapons do not change in skeleton id without being reconstructed, so this is not changeable data.
/// </summary>
private CharacterWeapon GetTempSlot(Weapon* weapon)
{
var changedData = *(void**)((byte*)weapon + 0x918);
if (changedData == null)
return new CharacterWeapon(weapon->ModelSetId, weapon->SecondaryId, (Variant)weapon->Variant, (StainId)weapon->ModelUnknown);
return new CharacterWeapon(weapon->ModelSetId, *(SecondaryId*)changedData, ((Variant*)changedData)[2], ((StainId*)changedData)[3]);
}
}