mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-15 20:17:42 +01:00
Update Advanced Dyes.
This commit is contained in:
parent
de9fb1fd9f
commit
2f95a4ea34
10 changed files with 180 additions and 77 deletions
|
|
@ -209,25 +209,43 @@ public class DesignConverter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ComputeMaterials(DesignMaterialManager manager, in StateMaterialManager materials,
|
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)
|
foreach (var (key, value) in materials.Values)
|
||||||
{
|
{
|
||||||
var idx = MaterialValueIndex.FromKey(key);
|
var idx = MaterialValueIndex.FromKey(key);
|
||||||
if (idx.RowIndex >= ColorTable.NumUsedRows)
|
if (idx.RowIndex >= ColorTable.NumRows)
|
||||||
continue;
|
continue;
|
||||||
if (idx.MaterialIndex >= MaterialService.MaterialsPerModel)
|
if (idx.MaterialIndex >= MaterialService.MaterialsPerModel)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var slot = idx.DrawObject switch
|
switch (idx.DrawObject)
|
||||||
{
|
{
|
||||||
MaterialValueIndex.DrawObjectType.Human => idx.SlotIndex < 10 ? ((uint)idx.SlotIndex).ToEquipSlot() : EquipSlot.Unknown,
|
case MaterialValueIndex.DrawObjectType.Mainhand when idx.SlotIndex == 0:
|
||||||
MaterialValueIndex.DrawObjectType.Mainhand when idx.SlotIndex == 0 => EquipSlot.MainHand,
|
if ((equipFlags & (EquipFlag.Mainhand | EquipFlag.MainhandStain)) == 0)
|
||||||
MaterialValueIndex.DrawObjectType.Offhand when idx.SlotIndex == 0 => EquipSlot.OffHand,
|
continue;
|
||||||
_ => EquipSlot.Unknown,
|
|
||||||
};
|
break;
|
||||||
if (slot is EquipSlot.Unknown || (slot.ToBothFlags() & equipFlags) == 0)
|
case MaterialValueIndex.DrawObjectType.Offhand when idx.SlotIndex == 0:
|
||||||
continue;
|
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());
|
manager.AddOrUpdateValue(idx, value.Convert());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
|
||||||
using FFXIVClientStructs.Interop;
|
using FFXIVClientStructs.Interop;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Interop.Material;
|
using Glamourer.Interop.Material;
|
||||||
|
|
@ -10,6 +9,7 @@ using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
using OtterGui.Widgets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Files.MaterialStructs;
|
using Penumbra.GameData.Files.MaterialStructs;
|
||||||
using Penumbra.GameData.Interop;
|
using Penumbra.GameData.Interop;
|
||||||
|
|
@ -29,6 +29,9 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
private byte _selectedMaterial = byte.MaxValue;
|
private byte _selectedMaterial = byte.MaxValue;
|
||||||
private bool _anyChanged;
|
private bool _anyChanged;
|
||||||
|
|
||||||
|
private const int RowsPerPage = 16;
|
||||||
|
private int _rowOffset;
|
||||||
|
|
||||||
private bool ShouldBeDrawn()
|
private bool ShouldBeDrawn()
|
||||||
{
|
{
|
||||||
if (!config.UseAdvancedDyes)
|
if (!config.UseAdvancedDyes)
|
||||||
|
|
@ -51,8 +54,6 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
|
|
||||||
private void DrawButton(MaterialValueIndex index)
|
private void DrawButton(MaterialValueIndex index)
|
||||||
{
|
{
|
||||||
// TODO fix when working
|
|
||||||
return;
|
|
||||||
if (!config.UseAdvancedDyes)
|
if (!config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -78,8 +79,8 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
|
|
||||||
private (string Path, string GamePath) ResourceName(MaterialValueIndex index)
|
private (string Path, string GamePath) ResourceName(MaterialValueIndex index)
|
||||||
{
|
{
|
||||||
var materialHandle = (MaterialResourceHandle*)_actor.Model.AsCharacterBase->MaterialsSpan[
|
var materialHandle =
|
||||||
index.MaterialIndex + index.SlotIndex * MaterialService.MaterialsPerModel].Value;
|
_actor.Model.AsCharacterBase->Materials()[index.MaterialIndex + index.SlotIndex * MaterialService.MaterialsPerModel].Value;
|
||||||
var model = _actor.Model.AsCharacterBase->ModelsSpan[index.SlotIndex].Value;
|
var model = _actor.Model.AsCharacterBase->ModelsSpan[index.SlotIndex].Value;
|
||||||
var modelHandle = model == null ? null : model->ModelResourceHandle;
|
var modelHandle = model == null ? null : model->ModelResourceHandle;
|
||||||
var path = materialHandle == null
|
var path = materialHandle == null
|
||||||
|
|
@ -129,17 +130,30 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
if ((tab.Success || select is ImGuiTabItemFlags.SetSelected) && available)
|
if ((tab.Success || select is ImGuiTabItemFlags.SetSelected) && available)
|
||||||
{
|
{
|
||||||
_selectedMaterial = i;
|
_selectedMaterial = i;
|
||||||
|
DrawToggle();
|
||||||
DrawTable(index, table);
|
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))
|
if (ToggleButton.ButtonEx("Row 1-16 ", buttonWidth, ImGuiButtonFlags.MouseButtonLeft, ImDrawFlags.RoundCornersLeft))
|
||||||
_drawIndex = null;
|
_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<Pointer<Texture>> textures)
|
private void DrawContent(ReadOnlySpan<Pointer<Texture>> textures)
|
||||||
|
|
@ -169,7 +183,7 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
}
|
}
|
||||||
|
|
||||||
var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale,
|
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);
|
ImGui.SetNextWindowSize(size);
|
||||||
|
|
||||||
var window = ImGui.Begin("###Glamourer Advanced Dyes", flags);
|
var window = ImGui.Begin("###Glamourer Advanced Dyes", flags);
|
||||||
|
|
@ -197,12 +211,16 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
|
|
||||||
private void DrawTable(MaterialValueIndex materialIndex, in ColorTable table)
|
private void DrawTable(MaterialValueIndex materialIndex, in ColorTable table)
|
||||||
{
|
{
|
||||||
|
if (!materialIndex.Valid)
|
||||||
|
return;
|
||||||
|
|
||||||
using var disabled = ImRaii.Disabled(_state.IsLocked);
|
using var disabled = ImRaii.Disabled(_state.IsLocked);
|
||||||
_anyChanged = false;
|
_anyChanged = false;
|
||||||
for (byte i = 0; i < ColorTable.NumUsedRows; ++i)
|
for (byte i = 0; i < RowsPerPage; ++i)
|
||||||
{
|
{
|
||||||
var index = materialIndex with { RowIndex = i };
|
var actualI = (byte)(i + _rowOffset);
|
||||||
ref var row = ref table[i];
|
var index = materialIndex with { RowIndex = actualI };
|
||||||
|
ref var row = ref table[actualI];
|
||||||
DrawRow(ref row, index, table);
|
DrawRow(ref row, index, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,7 +240,7 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted("All Color Rows");
|
ImGui.TextUnformatted("All Color Rows (1-32)");
|
||||||
}
|
}
|
||||||
|
|
||||||
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||||
|
|
@ -247,7 +265,7 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
ImGui.SameLine(0, spacing);
|
ImGui.SameLine(0, spacing);
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged,
|
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged,
|
||||||
true))
|
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);
|
stateManager.ResetMaterialValue(_state, materialIndex with { RowIndex = i }, ApplySettings.Game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,9 +277,13 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
{
|
{
|
||||||
var internalRow = new ColorRow(row);
|
var internalRow = new ColorRow(row);
|
||||||
var slot = index.ToEquipSlot();
|
var slot = index.ToEquipSlot();
|
||||||
var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand
|
var weapon = slot switch
|
||||||
? _state.ModelData.Weapon(slot)
|
{
|
||||||
: _state.ModelData.Armor(slot).ToWeapon(0);
|
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);
|
value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -327,11 +349,11 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
|
|
||||||
private struct LabelStruct
|
private struct LabelStruct
|
||||||
{
|
{
|
||||||
private fixed byte _label[12];
|
private fixed byte _label[5];
|
||||||
|
|
||||||
public ImRaii.IEndObject TabItem(byte materialIndex, ImGuiTabItemFlags flags)
|
public ImRaii.IEndObject TabItem(byte materialIndex, ImGuiTabItemFlags flags)
|
||||||
{
|
{
|
||||||
_label[10] = (byte)('1' + materialIndex);
|
_label[4] = (byte)('A' + materialIndex);
|
||||||
fixed (byte* ptr = _label)
|
fixed (byte* ptr = _label)
|
||||||
{
|
{
|
||||||
return ImRaii.TabItem(ptr, flags | ImGuiTabItemFlags.NoTooltip);
|
return ImRaii.TabItem(ptr, flags | ImGuiTabItemFlags.NoTooltip);
|
||||||
|
|
@ -340,17 +362,11 @@ public sealed unsafe class AdvancedDyePopup(
|
||||||
|
|
||||||
public LabelStruct()
|
public LabelStruct()
|
||||||
{
|
{
|
||||||
_label[0] = (byte)'M';
|
_label[0] = (byte)'M';
|
||||||
_label[1] = (byte)'a';
|
_label[1] = (byte)'a';
|
||||||
_label[2] = (byte)'t';
|
_label[2] = (byte)'t';
|
||||||
_label[3] = (byte)'e';
|
_label[3] = (byte)' ';
|
||||||
_label[4] = (byte)'r';
|
_label[5] = 0;
|
||||||
_label[5] = (byte)'i';
|
|
||||||
_label[6] = (byte)'a';
|
|
||||||
_label[7] = (byte)'l';
|
|
||||||
_label[8] = (byte)' ';
|
|
||||||
_label[9] = (byte)'#';
|
|
||||||
_label[11] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,9 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
|
||||||
{
|
{
|
||||||
_newRowIdx += 1;
|
_newRowIdx += 1;
|
||||||
ImGui.SetNextItemWidth(ImGui.CalcTextSize("Row #0000").X);
|
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) };
|
_newKey = _newKey with { RowIndex = (byte)(_newRowIdx - 1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (var i = 0; i < ColorTable.NumUsedRows; ++i)
|
for (var i = 0; i < ColorTable.NumRows; ++i)
|
||||||
{
|
{
|
||||||
table[i].Diffuse = diffuse;
|
table[i].Diffuse = diffuse;
|
||||||
table[i].Emissive = emissive;
|
table[i].Emissive = emissive;
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,8 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
=> _event.Unsubscribe(OnPrepareColorSet);
|
=> _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)
|
if (!_config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -50,7 +48,6 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
||||||
var (slotId, materialId) = FindMaterial(characterBase, material);
|
var (slotId, materialId) = FindMaterial(characterBase, material);
|
||||||
|
|
||||||
if (!validType
|
if (!validType
|
||||||
|| slotId > 9
|
|
||||||
|| type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0
|
|| type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0
|
||||||
|| !actor.Identifier(_actors, out var identifier)
|
|| !actor.Identifier(_actors, out var identifier)
|
||||||
|| !_stateManager.TryGetValue(identifier, out var state))
|
|| !_stateManager.TryGetValue(identifier, out var state))
|
||||||
|
|
@ -62,7 +59,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
||||||
if (values.Length == 0)
|
if (values.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!PrepareColorSet.TryGetColorTable(characterBase, material, stain, out var baseColorSet))
|
if (!PrepareColorSet.TryGetColorTable(material, stain, out var baseColorSet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var drawData = type switch
|
var drawData = type switch
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Glamourer.Interop.Material;
|
||||||
public static unsafe class MaterialService
|
public static unsafe class MaterialService
|
||||||
{
|
{
|
||||||
public const int TextureWidth = 8;
|
public const int TextureWidth = 8;
|
||||||
public const int TextureHeight = ColorTable.NumUsedRows;
|
public const int TextureHeight = ColorTable.NumRows;
|
||||||
public const int MaterialsPerModel = 10;
|
public const int MaterialsPerModel = 10;
|
||||||
|
|
||||||
public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* texture)
|
public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* texture)
|
||||||
|
|
@ -41,10 +41,10 @@ public static unsafe class MaterialService
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var index = modelSlot * MaterialsPerModel + materialSlot;
|
var index = modelSlot * MaterialsPerModel + materialSlot;
|
||||||
if (index < 0 || index >= model.AsCharacterBase->ColorTableTexturesSpan.Length)
|
if (index < 0 || index >= model.AsCharacterBase->ColorTableTextures().Length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var texture = (Texture**)Unsafe.AsPointer(ref model.AsCharacterBase->ColorTableTexturesSpan[index]);
|
var texture = (Texture**)Unsafe.AsPointer(ref model.AsCharacterBase->ColorTableTextures()[index]);
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,10 +59,10 @@ public static unsafe class MaterialService
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var index = modelSlot * MaterialsPerModel + materialSlot;
|
var index = modelSlot * MaterialsPerModel + materialSlot;
|
||||||
if (index < 0 || index >= model.AsCharacterBase->MaterialsSpan.Length)
|
if (index < 0 || index >= model.AsCharacterBase->Materials().Length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var material = (MaterialResourceHandle*)model.AsCharacterBase->MaterialsSpan[index].Value;
|
var material = model.AsCharacterBase->Materials()[index].Value;
|
||||||
if (material == null || material->ColorTable == null)
|
if (material == null || material->ColorTable == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||||
using FFXIVClientStructs.Interop;
|
using FFXIVClientStructs.Interop;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -79,13 +81,13 @@ public readonly record struct MaterialValueIndex(
|
||||||
{
|
{
|
||||||
if (!TryGetModel(actor, out var model)
|
if (!TryGetModel(actor, out var model)
|
||||||
|| SlotIndex >= model.AsCharacterBase->SlotCount
|
|| SlotIndex >= model.AsCharacterBase->SlotCount
|
||||||
|| model.AsCharacterBase->ColorTableTexturesSpan.Length < (SlotIndex + 1) * MaterialService.MaterialsPerModel)
|
|| model.AsCharacterBase->ColorTableTextures().Length < (SlotIndex + 1) * MaterialService.MaterialsPerModel)
|
||||||
{
|
{
|
||||||
textures = [];
|
textures = [];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
textures = model.AsCharacterBase->ColorTableTexturesSpan.Slice(SlotIndex * MaterialService.MaterialsPerModel,
|
textures = model.AsCharacterBase->ColorTableTextures().Slice(SlotIndex * MaterialService.MaterialsPerModel,
|
||||||
MaterialService.MaterialsPerModel);
|
MaterialService.MaterialsPerModel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +141,7 @@ public readonly record struct MaterialValueIndex(
|
||||||
public static bool ValidateSlot(DrawObjectType type, byte slotIndex)
|
public static bool ValidateSlot(DrawObjectType type, byte slotIndex)
|
||||||
=> type switch
|
=> type switch
|
||||||
{
|
{
|
||||||
DrawObjectType.Human => slotIndex < 14,
|
DrawObjectType.Human => slotIndex < 18,
|
||||||
DrawObjectType.Mainhand => slotIndex == 0,
|
DrawObjectType.Mainhand => slotIndex == 0,
|
||||||
DrawObjectType.Offhand => slotIndex == 0,
|
DrawObjectType.Offhand => slotIndex == 0,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
@ -149,7 +151,7 @@ public readonly record struct MaterialValueIndex(
|
||||||
=> materialIndex < MaterialService.MaterialsPerModel;
|
=> materialIndex < MaterialService.MaterialsPerModel;
|
||||||
|
|
||||||
public static bool ValidateRow(byte rowIndex)
|
public static bool ValidateRow(byte rowIndex)
|
||||||
=> rowIndex < ColorTable.NumUsedRows;
|
=> rowIndex < ColorTable.NumRows;
|
||||||
|
|
||||||
private static uint ToKey(DrawObjectType type, byte slotIndex, byte materialIndex, byte rowIndex)
|
private static uint ToKey(DrawObjectType type, byte slotIndex, byte materialIndex, byte rowIndex)
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +176,8 @@ public readonly record struct MaterialValueIndex(
|
||||||
DrawObjectType.Human when SlotIndex == 11 => $"BodySlot.Face.ToString() Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
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 == 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 == 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.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}",
|
DrawObjectType.Offhand when SlotIndex == 0 => $"{EquipSlot.OffHand.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||||
_ => $"{DrawObject} Slot {SlotIndex} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
_ => $"{DrawObject} Slot {SlotIndex} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||||
|
|
@ -189,3 +193,13 @@ public readonly record struct MaterialValueIndex(
|
||||||
=> FromKey(serializer.Deserialize<uint>(reader), out var value) ? value : throw new Exception($"Invalid material key {value.Key}.");
|
=> FromKey(serializer.Deserialize<uint>(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<Pointer<MaterialResourceHandle>> Materials(this ref CharacterBase character)
|
||||||
|
=> new(character.Materials, character.SlotCount * MaterialService.MaterialsPerModel);
|
||||||
|
|
||||||
|
public static unsafe Span<Pointer<Texture>> ColorTableTextures(this ref CharacterBase character)
|
||||||
|
=> new(character.ColorTableTextures, character.SlotCount * MaterialService.MaterialsPerModel);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,22 @@ using Penumbra.GameData.Structs;
|
||||||
namespace Glamourer.Interop.Material;
|
namespace Glamourer.Interop.Material;
|
||||||
|
|
||||||
public sealed unsafe class PrepareColorSet
|
public sealed unsafe class PrepareColorSet
|
||||||
: EventWrapperPtr12Ref34<CharacterBase, MaterialResourceHandle, StainId, nint, PrepareColorSet.Priority>, IHookService
|
: EventWrapperPtr12Ref34<CharacterBase, MaterialResourceHandle, StainIds, nint, PrepareColorSet.Priority>, IHookService
|
||||||
{
|
{
|
||||||
|
private readonly UpdateColorSets _updateColorSets;
|
||||||
|
|
||||||
public enum Priority
|
public enum Priority
|
||||||
{
|
{
|
||||||
/// <seealso cref="MaterialManager.OnPrepareColorSet"/>
|
/// <seealso cref="MaterialManager.OnPrepareColorSet"/>
|
||||||
MaterialManager = 0,
|
MaterialManager = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO enable when working
|
public PrepareColorSet(HookManager hooks, UpdateColorSets updateColorSets)
|
||||||
public PrepareColorSet(HookManager hooks)
|
|
||||||
: base("Prepare Color Set ")
|
: base("Prepare Color Set ")
|
||||||
=> _task = hooks.CreateHook<Delegate>(Name, Sigs.PrepareColorSet, Detour, false);
|
{
|
||||||
|
_updateColorSets = updateColorSets;
|
||||||
|
_task = hooks.CreateHook<Delegate>(Name, Sigs.PrepareColorSet, Detour, true);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Task<Hook<Delegate>> _task;
|
private readonly Task<Hook<Delegate>> _task;
|
||||||
|
|
||||||
|
|
@ -43,20 +47,25 @@ public sealed unsafe class PrepareColorSet
|
||||||
public bool Finished
|
public bool Finished
|
||||||
=> _task.IsCompletedSuccessfully;
|
=> _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}.");
|
Glamourer.Log.Excessive($"[{Name}] Triggered with 0x{(nint)material:X} {stainId1.Id} {stainId2.Id}.");
|
||||||
var ret = nint.Zero;
|
var characterBase = _updateColorSets.Get();
|
||||||
Invoke(characterBase, material, ref stainId, ref ret);
|
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)
|
if (ret != nint.Zero)
|
||||||
return (Texture*)ret;
|
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)
|
out ColorTable table)
|
||||||
{
|
{
|
||||||
if (material->ColorTable == null)
|
if (material->ColorTable == null)
|
||||||
|
|
@ -66,9 +75,17 @@ public sealed unsafe class PrepareColorSet
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTable = *(ColorTable*)material->ColorTable;
|
var newTable = *(ColorTable*)material->ColorTable;
|
||||||
// TODO
|
if (GetDyeTable(material, out var dyeTable))
|
||||||
//if (stainIds.Stain1.Id != 0 || stainIds.Stain2.Id != 0)
|
{
|
||||||
// characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable));
|
if (stainIds.Stain1.Id != 0)
|
||||||
|
((delegate* unmanaged<MaterialResourceHandle*, ushort*, byte, Half*, uint, void>)MaterialResourceHandle.MemberFunctionPointers
|
||||||
|
.ReadStainingTemplate)(material, dyeTable, stainIds.Stain1.Id, (Half*)(&newTable), 0);
|
||||||
|
|
||||||
|
if (stainIds.Stain2.Id != 0)
|
||||||
|
((delegate* unmanaged<MaterialResourceHandle*, ushort*, byte, Half*, uint, void>)MaterialResourceHandle.MemberFunctionPointers
|
||||||
|
.ReadStainingTemplate)(material, dyeTable, stainIds.Stain2.Id, (Half*)(&newTable), 1);
|
||||||
|
}
|
||||||
|
|
||||||
table = newTable;
|
table = newTable;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +104,7 @@ public sealed unsafe class PrepareColorSet
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryGetColorTable(model.AsCharacterBase, handle, GetStains(), out table);
|
return TryGetColorTable(handle, GetStains(), out table);
|
||||||
|
|
||||||
StainIds GetStains()
|
StainIds GetStains()
|
||||||
{
|
{
|
||||||
|
|
@ -105,4 +122,24 @@ public sealed unsafe class PrepareColorSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Get the correct dye table for a material. </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
Glamourer/Interop/Material/UpdateColorSets.cs
Normal file
25
Glamourer/Interop/Material/UpdateColorSets.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
|
|
||||||
|
namespace Glamourer.Interop.Material;
|
||||||
|
|
||||||
|
public sealed class UpdateColorSets : FastHook<UpdateColorSets.Delegate>
|
||||||
|
{
|
||||||
|
public delegate void Delegate(Model model, uint unk);
|
||||||
|
|
||||||
|
private readonly ThreadLocal<Model> _updatingModel = new();
|
||||||
|
|
||||||
|
public UpdateColorSets(HookManager hooks)
|
||||||
|
=> Task = hooks.CreateHook<Delegate>("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;
|
||||||
|
}
|
||||||
|
|
@ -306,8 +306,6 @@ public class StateApplier(
|
||||||
|
|
||||||
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
|
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
|
||||||
{
|
{
|
||||||
// TODO fix when working
|
|
||||||
return;
|
|
||||||
if (!force && !_config.UseAdvancedDyes)
|
if (!force && !_config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -340,8 +338,6 @@ public class StateApplier(
|
||||||
|
|
||||||
public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials, bool force)
|
public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials, bool force)
|
||||||
{
|
{
|
||||||
// TODO: fix when working
|
|
||||||
return;
|
|
||||||
if (!force && !_config.UseAdvancedDyes)
|
if (!force && !_config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue