From 2f95a4ea34ea5bae25af3068f48d17ea07e57ff5 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 19 Jul 2024 17:27:30 +0200 Subject: [PATCH] Update Advanced Dyes. --- Glamourer/Designs/DesignConverter.cs | 38 ++++++--- Glamourer/Gui/Materials/AdvancedDyePopup.cs | 78 +++++++++++-------- Glamourer/Gui/Materials/MaterialDrawer.cs | 4 +- .../Material/LiveColorTablePreviewer.cs | 2 +- Glamourer/Interop/Material/MaterialManager.cs | 7 +- Glamourer/Interop/Material/MaterialService.cs | 10 +-- .../Interop/Material/MaterialValueIndex.cs | 22 +++++- Glamourer/Interop/Material/PrepareColorSet.cs | 67 ++++++++++++---- Glamourer/Interop/Material/UpdateColorSets.cs | 25 ++++++ Glamourer/State/StateApplier.cs | 4 - 10 files changed, 180 insertions(+), 77 deletions(-) create mode 100644 Glamourer/Interop/Material/UpdateColorSets.cs diff --git a/Glamourer/Designs/DesignConverter.cs b/Glamourer/Designs/DesignConverter.cs index c3235ab..2ebcea0 100644 --- a/Glamourer/Designs/DesignConverter.cs +++ b/Glamourer/Designs/DesignConverter.cs @@ -209,25 +209,43 @@ public static string ToBase64(JToken jObject) } private static void ComputeMaterials(DesignMaterialManager manager, in StateMaterialManager materials, - EquipFlag equipFlags = EquipFlagExtensions.All) + EquipFlag equipFlags = EquipFlagExtensions.All, BonusItemFlag bonusFlags = BonusExtensions.All) { foreach (var (key, value) in materials.Values) { var idx = MaterialValueIndex.FromKey(key); - if (idx.RowIndex >= ColorTable.NumUsedRows) + if (idx.RowIndex >= ColorTable.NumRows) continue; if (idx.MaterialIndex >= MaterialService.MaterialsPerModel) continue; - var slot = idx.DrawObject switch + switch (idx.DrawObject) { - MaterialValueIndex.DrawObjectType.Human => idx.SlotIndex < 10 ? ((uint)idx.SlotIndex).ToEquipSlot() : EquipSlot.Unknown, - MaterialValueIndex.DrawObjectType.Mainhand when idx.SlotIndex == 0 => EquipSlot.MainHand, - MaterialValueIndex.DrawObjectType.Offhand when idx.SlotIndex == 0 => EquipSlot.OffHand, - _ => EquipSlot.Unknown, - }; - if (slot is EquipSlot.Unknown || (slot.ToBothFlags() & equipFlags) == 0) - continue; + case MaterialValueIndex.DrawObjectType.Mainhand when idx.SlotIndex == 0: + if ((equipFlags & (EquipFlag.Mainhand | EquipFlag.MainhandStain)) == 0) + continue; + + break; + case MaterialValueIndex.DrawObjectType.Offhand when idx.SlotIndex == 0: + if ((equipFlags & (EquipFlag.Offhand | EquipFlag.OffhandStain)) == 0) + continue; + + break; + case MaterialValueIndex.DrawObjectType.Human: + if (idx.SlotIndex < 10) + { + if ((((uint)idx.SlotIndex).ToEquipSlot().ToBothFlags() & equipFlags) == 0) + continue; + } + else if (idx.SlotIndex >= 16) + { + if (((idx.SlotIndex - 16u).ToBonusSlot() & bonusFlags) == 0) + continue; + } + + break; + default: continue; + } manager.AddOrUpdateValue(idx, value.Convert()); } diff --git a/Glamourer/Gui/Materials/AdvancedDyePopup.cs b/Glamourer/Gui/Materials/AdvancedDyePopup.cs index 173109b..56f4d24 100644 --- a/Glamourer/Gui/Materials/AdvancedDyePopup.cs +++ b/Glamourer/Gui/Materials/AdvancedDyePopup.cs @@ -1,7 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; -using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.Interop; using Glamourer.Designs; using Glamourer.Interop.Material; @@ -10,6 +9,7 @@ using OtterGui; using OtterGui.Raii; using OtterGui.Services; +using OtterGui.Widgets; using Penumbra.GameData.Enums; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Interop; @@ -29,6 +29,9 @@ public sealed unsafe class AdvancedDyePopup( private byte _selectedMaterial = byte.MaxValue; private bool _anyChanged; + private const int RowsPerPage = 16; + private int _rowOffset; + private bool ShouldBeDrawn() { if (!config.UseAdvancedDyes) @@ -51,8 +54,6 @@ public void DrawButton(BonusItemFlag slot) private void DrawButton(MaterialValueIndex index) { - // TODO fix when working - return; if (!config.UseAdvancedDyes) return; @@ -78,8 +79,8 @@ private void DrawButton(MaterialValueIndex index) private (string Path, string GamePath) ResourceName(MaterialValueIndex index) { - var materialHandle = (MaterialResourceHandle*)_actor.Model.AsCharacterBase->MaterialsSpan[ - index.MaterialIndex + index.SlotIndex * MaterialService.MaterialsPerModel].Value; + var materialHandle = + _actor.Model.AsCharacterBase->Materials()[index.MaterialIndex + index.SlotIndex * MaterialService.MaterialsPerModel].Value; var model = _actor.Model.AsCharacterBase->ModelsSpan[index.SlotIndex].Value; var modelHandle = model == null ? null : model->ModelResourceHandle; var path = materialHandle == null @@ -129,17 +130,30 @@ private void DrawTabBar(ReadOnlySpan> textures, ref bool firstA if ((tab.Success || select is ImGuiTabItemFlags.SetSelected) && available) { _selectedMaterial = i; + DrawToggle(); DrawTable(index, table); } } + } - using (ImRaii.PushFont(UiBuilder.IconFont)) + private void DrawToggle() + { + var buttonWidth = new Vector2(ImGui.GetContentRegionAvail().X / 2, 0); + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + using (ImRaii.Disabled(_rowOffset == 0)) { - if (ImGui.TabItemButton($"{FontAwesomeIcon.Times.ToIconString()} ", ImGuiTabItemFlags.NoTooltip)) - _drawIndex = null; + if (ToggleButton.ButtonEx("Row 1-16 ", buttonWidth, ImGuiButtonFlags.MouseButtonLeft, ImDrawFlags.RoundCornersLeft)) + _rowOffset = 0; } - ImGuiUtil.HoverTooltip("Close the advanced dye window."); + ImGui.SameLine(0, 0); + + + using (ImRaii.Disabled(_rowOffset == RowsPerPage)) + { + if (ToggleButton.ButtonEx("Row 17-32", buttonWidth, ImGuiButtonFlags.MouseButtonLeft, ImDrawFlags.RoundCornersRight)) + _rowOffset = RowsPerPage; + } } private void DrawContent(ReadOnlySpan> textures) @@ -169,7 +183,7 @@ private void DrawWindow(ReadOnlySpan> textures) } var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale, - 18 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + 2 * ImGui.GetStyle().ItemSpacing.Y); + 19 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + 3 * ImGui.GetStyle().ItemSpacing.Y); ImGui.SetNextWindowSize(size); var window = ImGui.Begin("###Glamourer Advanced Dyes", flags); @@ -197,12 +211,16 @@ public void Draw(Actor actor, ActorState state) private void DrawTable(MaterialValueIndex materialIndex, in ColorTable table) { + if (!materialIndex.Valid) + return; + using var disabled = ImRaii.Disabled(_state.IsLocked); _anyChanged = false; - for (byte i = 0; i < ColorTable.NumUsedRows; ++i) + for (byte i = 0; i < RowsPerPage; ++i) { - var index = materialIndex with { RowIndex = i }; - ref var row = ref table[i]; + var actualI = (byte)(i + _rowOffset); + var index = materialIndex with { RowIndex = actualI }; + ref var row = ref table[actualI]; DrawRow(ref row, index, table); } @@ -222,7 +240,7 @@ private void DrawAllRow(MaterialValueIndex materialIndex, in ColorTable table) ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.MonoFont)) { - ImGui.TextUnformatted("All Color Rows"); + ImGui.TextUnformatted("All Color Rows (1-32)"); } var spacing = ImGui.GetStyle().ItemInnerSpacing.X; @@ -247,7 +265,7 @@ private void DrawAllRow(MaterialValueIndex materialIndex, in ColorTable table) ImGui.SameLine(0, spacing); if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged, true)) - for (byte i = 0; i < ColorTable.NumUsedRows; ++i) + for (byte i = 0; i < ColorTable.NumRows; ++i) stateManager.ResetMaterialValue(_state, materialIndex with { RowIndex = i }, ApplySettings.Game); } @@ -259,9 +277,13 @@ private void DrawRow(ref ColorTable.Row row, MaterialValueIndex index, in ColorT { var internalRow = new ColorRow(row); var slot = index.ToEquipSlot(); - var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand - ? _state.ModelData.Weapon(slot) - : _state.ModelData.Armor(slot).ToWeapon(0); + var weapon = slot switch + { + EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand), + EquipSlot.OffHand => _state.ModelData.Weapon(EquipSlot.OffHand), + EquipSlot.Unknown => _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).ToArmor().ToWeapon(0), + _ => _state.ModelData.Armor(slot).ToWeapon(0), + }; value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual); } else @@ -327,11 +349,11 @@ private void DrawRow(ref ColorTable.Row row, MaterialValueIndex index, in ColorT private struct LabelStruct { - private fixed byte _label[12]; + private fixed byte _label[5]; public ImRaii.IEndObject TabItem(byte materialIndex, ImGuiTabItemFlags flags) { - _label[10] = (byte)('1' + materialIndex); + _label[4] = (byte)('A' + materialIndex); fixed (byte* ptr = _label) { return ImRaii.TabItem(ptr, flags | ImGuiTabItemFlags.NoTooltip); @@ -340,17 +362,11 @@ public ImRaii.IEndObject TabItem(byte materialIndex, ImGuiTabItemFlags flags) public LabelStruct() { - _label[0] = (byte)'M'; - _label[1] = (byte)'a'; - _label[2] = (byte)'t'; - _label[3] = (byte)'e'; - _label[4] = (byte)'r'; - _label[5] = (byte)'i'; - _label[6] = (byte)'a'; - _label[7] = (byte)'l'; - _label[8] = (byte)' '; - _label[9] = (byte)'#'; - _label[11] = 0; + _label[0] = (byte)'M'; + _label[1] = (byte)'a'; + _label[2] = (byte)'t'; + _label[3] = (byte)' '; + _label[5] = 0; } } } diff --git a/Glamourer/Gui/Materials/MaterialDrawer.cs b/Glamourer/Gui/Materials/MaterialDrawer.cs index e7a061f..445184d 100644 --- a/Glamourer/Gui/Materials/MaterialDrawer.cs +++ b/Glamourer/Gui/Materials/MaterialDrawer.cs @@ -175,9 +175,9 @@ private void DrawRowIdxDrag() { _newRowIdx += 1; ImGui.SetNextItemWidth(ImGui.CalcTextSize("Row #0000").X); - if (ImGui.DragInt("##Row", ref _newRowIdx, 0.01f, 1, ColorTable.NumUsedRows, "Row #%i")) + if (ImGui.DragInt("##Row", ref _newRowIdx, 0.01f, 1, ColorTable.NumRows, "Row #%i")) { - _newRowIdx = Math.Clamp(_newRowIdx, 1, ColorTable.NumUsedRows); + _newRowIdx = Math.Clamp(_newRowIdx, 1, ColorTable.NumRows); _newKey = _newKey with { RowIndex = (byte)(_newRowIdx - 1) }; } diff --git a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs index a9f3a74..1df85f6 100644 --- a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs +++ b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs @@ -78,7 +78,7 @@ private void OnFramework(IFramework _) } else { - for (var i = 0; i < ColorTable.NumUsedRows; ++i) + for (var i = 0; i < ColorTable.NumRows; ++i) { table[i].Diffuse = diffuse; table[i].Emissive = emissive; diff --git a/Glamourer/Interop/Material/MaterialManager.cs b/Glamourer/Interop/Material/MaterialManager.cs index 5f2b553..fe786d7 100644 --- a/Glamourer/Interop/Material/MaterialManager.cs +++ b/Glamourer/Interop/Material/MaterialManager.cs @@ -38,10 +38,8 @@ public MaterialManager(PrepareColorSet prepareColorSet, StateManager stateManage public void Dispose() => _event.Unsubscribe(OnPrepareColorSet); - private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainId stain, ref nint ret) + private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainIds stain, ref nint ret) { - // TODO fix when working - return; if (!_config.UseAdvancedDyes) return; @@ -50,7 +48,6 @@ private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHan var (slotId, materialId) = FindMaterial(characterBase, material); if (!validType - || slotId > 9 || type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0 || !actor.Identifier(_actors, out var identifier) || !_stateManager.TryGetValue(identifier, out var state)) @@ -62,7 +59,7 @@ private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHan if (values.Length == 0) return; - if (!PrepareColorSet.TryGetColorTable(characterBase, material, stain, out var baseColorSet)) + if (!PrepareColorSet.TryGetColorTable(material, stain, out var baseColorSet)) return; var drawData = type switch diff --git a/Glamourer/Interop/Material/MaterialService.cs b/Glamourer/Interop/Material/MaterialService.cs index b2626be..3e972a8 100644 --- a/Glamourer/Interop/Material/MaterialService.cs +++ b/Glamourer/Interop/Material/MaterialService.cs @@ -10,7 +10,7 @@ namespace Glamourer.Interop.Material; public static unsafe class MaterialService { public const int TextureWidth = 8; - public const int TextureHeight = ColorTable.NumUsedRows; + public const int TextureHeight = ColorTable.NumRows; public const int MaterialsPerModel = 10; public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* texture) @@ -41,10 +41,10 @@ public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* return null; var index = modelSlot * MaterialsPerModel + materialSlot; - if (index < 0 || index >= model.AsCharacterBase->ColorTableTexturesSpan.Length) + if (index < 0 || index >= model.AsCharacterBase->ColorTableTextures().Length) return null; - var texture = (Texture**)Unsafe.AsPointer(ref model.AsCharacterBase->ColorTableTexturesSpan[index]); + var texture = (Texture**)Unsafe.AsPointer(ref model.AsCharacterBase->ColorTableTextures()[index]); return texture; } @@ -59,10 +59,10 @@ public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* return null; var index = modelSlot * MaterialsPerModel + materialSlot; - if (index < 0 || index >= model.AsCharacterBase->MaterialsSpan.Length) + if (index < 0 || index >= model.AsCharacterBase->Materials().Length) return null; - var material = (MaterialResourceHandle*)model.AsCharacterBase->MaterialsSpan[index].Value; + var material = model.AsCharacterBase->Materials()[index].Value; if (material == null || material->ColorTable == null) return null; diff --git a/Glamourer/Interop/Material/MaterialValueIndex.cs b/Glamourer/Interop/Material/MaterialValueIndex.cs index 8101c05..5c44e21 100644 --- a/Glamourer/Interop/Material/MaterialValueIndex.cs +++ b/Glamourer/Interop/Material/MaterialValueIndex.cs @@ -1,4 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.Interop; using Newtonsoft.Json; using Penumbra.GameData.Enums; @@ -79,13 +81,13 @@ public unsafe bool TryGetTextures(Actor actor, out ReadOnlySpan { if (!TryGetModel(actor, out var model) || SlotIndex >= model.AsCharacterBase->SlotCount - || model.AsCharacterBase->ColorTableTexturesSpan.Length < (SlotIndex + 1) * MaterialService.MaterialsPerModel) + || model.AsCharacterBase->ColorTableTextures().Length < (SlotIndex + 1) * MaterialService.MaterialsPerModel) { textures = []; return false; } - textures = model.AsCharacterBase->ColorTableTexturesSpan.Slice(SlotIndex * MaterialService.MaterialsPerModel, + textures = model.AsCharacterBase->ColorTableTextures().Slice(SlotIndex * MaterialService.MaterialsPerModel, MaterialService.MaterialsPerModel); return true; } @@ -139,7 +141,7 @@ public static bool Validate(DrawObjectType type) public static bool ValidateSlot(DrawObjectType type, byte slotIndex) => type switch { - DrawObjectType.Human => slotIndex < 14, + DrawObjectType.Human => slotIndex < 18, DrawObjectType.Mainhand => slotIndex == 0, DrawObjectType.Offhand => slotIndex == 0, _ => false, @@ -149,7 +151,7 @@ public static bool ValidateMaterial(byte materialIndex) => materialIndex < MaterialService.MaterialsPerModel; public static bool ValidateRow(byte rowIndex) - => rowIndex < ColorTable.NumUsedRows; + => rowIndex < ColorTable.NumRows; private static uint ToKey(DrawObjectType type, byte slotIndex, byte materialIndex, byte rowIndex) { @@ -174,6 +176,8 @@ public override string ToString() DrawObjectType.Human when SlotIndex == 11 => $"BodySlot.Face.ToString() Material #{MaterialIndex + 1} Row #{RowIndex + 1}", DrawObjectType.Human when SlotIndex == 12 => $"{BodySlot.Tail} / {BodySlot.Ear} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", DrawObjectType.Human when SlotIndex == 13 => $"Connectors Material #{MaterialIndex + 1} Row #{RowIndex + 1}", + DrawObjectType.Human when SlotIndex == 16 => $"{BonusItemFlag.Glasses.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", + DrawObjectType.Human when SlotIndex == 17 => $"{BonusItemFlag.UnkSlot.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", DrawObjectType.Mainhand when SlotIndex == 0 => $"{EquipSlot.MainHand.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", DrawObjectType.Offhand when SlotIndex == 0 => $"{EquipSlot.OffHand.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", _ => $"{DrawObject} Slot {SlotIndex} Material #{MaterialIndex + 1} Row #{RowIndex + 1}", @@ -189,3 +193,13 @@ public override MaterialValueIndex ReadJson(JsonReader reader, Type objectType, => FromKey(serializer.Deserialize(reader), out var value) ? value : throw new Exception($"Invalid material key {value.Key}."); } } + +// TODO Remove when fixed in CS. +public static class ColorTableExtension +{ + public static unsafe Span> Materials(this ref CharacterBase character) + => new(character.Materials, character.SlotCount * MaterialService.MaterialsPerModel); + + public static unsafe Span> ColorTableTextures(this ref CharacterBase character) + => new(character.ColorTableTextures, character.SlotCount * MaterialService.MaterialsPerModel); +} diff --git a/Glamourer/Interop/Material/PrepareColorSet.cs b/Glamourer/Interop/Material/PrepareColorSet.cs index 6595c8f..b8e31c2 100644 --- a/Glamourer/Interop/Material/PrepareColorSet.cs +++ b/Glamourer/Interop/Material/PrepareColorSet.cs @@ -13,18 +13,22 @@ namespace Glamourer.Interop.Material; public sealed unsafe class PrepareColorSet - : EventWrapperPtr12Ref34, IHookService + : EventWrapperPtr12Ref34, IHookService { + private readonly UpdateColorSets _updateColorSets; + public enum Priority { /// MaterialManager = 0, } - // TODO enable when working - public PrepareColorSet(HookManager hooks) + public PrepareColorSet(HookManager hooks, UpdateColorSets updateColorSets) : base("Prepare Color Set ") - => _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, false); + { + _updateColorSets = updateColorSets; + _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, true); + } private readonly Task> _task; @@ -43,20 +47,25 @@ public Task Awaiter public bool Finished => _task.IsCompletedSuccessfully; - private delegate Texture* Delegate(CharacterBase* characterBase, MaterialResourceHandle* material, StainId stainId); + private delegate Texture* Delegate(MaterialResourceHandle* material, StainId stainId1, StainId stainId2); - private Texture* Detour(CharacterBase* characterBase, MaterialResourceHandle* material, StainId stainId) + private Texture* Detour(MaterialResourceHandle* material, StainId stainId1, StainId stainId2) { - Glamourer.Log.Excessive($"[{Name}] Triggered with 0x{(nint)characterBase:X} 0x{(nint)material:X} {stainId.Id}."); - var ret = nint.Zero; - Invoke(characterBase, material, ref stainId, ref ret); + Glamourer.Log.Excessive($"[{Name}] Triggered with 0x{(nint)material:X} {stainId1.Id} {stainId2.Id}."); + var characterBase = _updateColorSets.Get(); + if (!characterBase.IsCharacterBase) + return _task.Result.Original(material, stainId1, stainId2); + + var ret = nint.Zero; + var stainIds = new StainIds(stainId1, stainId2); + Invoke(characterBase.AsCharacterBase, material, ref stainIds, ref ret); if (ret != nint.Zero) return (Texture*)ret; - return _task.Result.Original(characterBase, material, stainId); + return _task.Result.Original(material, stainIds.Stain1, stainIds.Stain2); } - public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainIds stainIds, + public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds, out ColorTable table) { if (material->ColorTable == null) @@ -66,9 +75,17 @@ public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResour } var newTable = *(ColorTable*)material->ColorTable; - // TODO - //if (stainIds.Stain1.Id != 0 || stainIds.Stain2.Id != 0) - // characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable)); + if (GetDyeTable(material, out var dyeTable)) + { + if (stainIds.Stain1.Id != 0) + ((delegate* unmanaged)MaterialResourceHandle.MemberFunctionPointers + .ReadStainingTemplate)(material, dyeTable, stainIds.Stain1.Id, (Half*)(&newTable), 0); + + if (stainIds.Stain2.Id != 0) + ((delegate* unmanaged)MaterialResourceHandle.MemberFunctionPointers + .ReadStainingTemplate)(material, dyeTable, stainIds.Stain2.Id, (Half*)(&newTable), 1); + } + table = newTable; return true; } @@ -87,7 +104,7 @@ public static bool TryGetColorTable(Actor actor, MaterialValueIndex index, out C return false; } - return TryGetColorTable(model.AsCharacterBase, handle, GetStains(), out table); + return TryGetColorTable(handle, GetStains(), out table); StainIds GetStains() { @@ -105,4 +122,24 @@ StainIds GetStains() } } } + + /// Get the correct dye table for a material. + private static bool GetDyeTable(MaterialResourceHandle* material, out ushort* ptr) + { + ptr = null; + if (material->AdditionalDataSize is 0 || material->AdditionalData is null) + return false; + + var flags1 = material->AdditionalData[0]; + if ((flags1 & 0xF0) is 0) + { + ptr = (ushort*)material + 0x100; + return true; + } + + var flags2 = material->AdditionalData[1]; + var offset = 4 * (1 << (flags1 >> 4)) * (1 << (flags2 & 0x0F)); + ptr = (ushort*)material->DataSet + offset; + return true; + } } diff --git a/Glamourer/Interop/Material/UpdateColorSets.cs b/Glamourer/Interop/Material/UpdateColorSets.cs new file mode 100644 index 0000000..a96c60f --- /dev/null +++ b/Glamourer/Interop/Material/UpdateColorSets.cs @@ -0,0 +1,25 @@ +using OtterGui.Services; +using Penumbra.GameData; +using Penumbra.GameData.Interop; + +namespace Glamourer.Interop.Material; + +public sealed class UpdateColorSets : FastHook +{ + public delegate void Delegate(Model model, uint unk); + + private readonly ThreadLocal _updatingModel = new(); + + public UpdateColorSets(HookManager hooks) + => Task = hooks.CreateHook("Update Color Sets", Sigs.UpdateColorSets, Detour, true); + + private void Detour(Model model, uint unk) + { + _updatingModel.Value = model; + Task.Result.Original(model, unk); + _updatingModel.Value = nint.Zero; + } + + public Model Get() + => _updatingModel.Value; +} diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index 2af6437..66b83fb 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -306,8 +306,6 @@ public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force) { - // TODO fix when working - return; if (!force && !_config.UseAdvancedDyes) return; @@ -340,8 +338,6 @@ public ActorData ChangeMaterialValue(ActorState state, MaterialValueIndex index, public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials, bool force) { - // TODO: fix when working - return; if (!force && !_config.UseAdvancedDyes) return;