mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Things are progressing at a satisfying rate.
This commit is contained in:
parent
5e5ce4d234
commit
cb45221be2
7 changed files with 312 additions and 103 deletions
178
Glamourer/Gui/Materials/MaterialDrawer.cs
Normal file
178
Glamourer/Gui/Materials/MaterialDrawer.cs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Interop.Structs;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Files;
|
||||
|
||||
namespace Glamourer.Gui.Materials;
|
||||
|
||||
public unsafe class MaterialDrawer : IService
|
||||
{
|
||||
private static readonly IReadOnlyList<MaterialValueIndex.DrawObjectType> Types =
|
||||
[
|
||||
MaterialValueIndex.DrawObjectType.Human,
|
||||
MaterialValueIndex.DrawObjectType.Mainhand,
|
||||
MaterialValueIndex.DrawObjectType.Offhand,
|
||||
];
|
||||
|
||||
public void DrawPanel(Actor actor)
|
||||
{
|
||||
if (!actor.IsCharacter)
|
||||
return;
|
||||
|
||||
foreach (var type in Types)
|
||||
{
|
||||
var index = new MaterialValueIndex(type, 0, 0, 0, 0);
|
||||
if (index.TryGetModel(actor, out var model))
|
||||
DrawModelType(model, index);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawModelType(Model model, MaterialValueIndex sourceIndex)
|
||||
{
|
||||
using var tree = ImRaii.TreeNode(sourceIndex.DrawObject.ToString());
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
var names = model.AsCharacterBase->GetModelType() is CharacterBase.ModelType.Human
|
||||
? SlotNamesHuman
|
||||
: SlotNames;
|
||||
for (byte i = 0; i < model.AsCharacterBase->SlotCount; ++i)
|
||||
{
|
||||
var index = sourceIndex with { SlotIndex = i };
|
||||
DrawSlot(model, names, index);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSlot(Model model, IReadOnlyList<string> names, MaterialValueIndex sourceIndex)
|
||||
{
|
||||
using var tree = ImRaii.TreeNode(names[sourceIndex.SlotIndex]);
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
for (byte i = 0; i < MaterialService.MaterialsPerModel; ++i)
|
||||
{
|
||||
var index = sourceIndex with { MaterialIndex = i };
|
||||
var texture = model.AsCharacterBase->ColorTableTextures + index.SlotIndex * MaterialService.MaterialsPerModel + i;
|
||||
if (*texture == null)
|
||||
continue;
|
||||
|
||||
if (!DirectXTextureHelper.TryGetColorTable(*texture, out var table))
|
||||
continue;
|
||||
|
||||
DrawMaterial(ref table, texture, index);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMaterial(ref MtrlFile.ColorTable table, Texture** texture, MaterialValueIndex sourceIndex)
|
||||
{
|
||||
using var tree = ImRaii.TreeNode($"Material {sourceIndex.MaterialIndex + 1}");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
for (byte i = 0; i < MtrlFile.ColorTable.NumRows; ++i)
|
||||
{
|
||||
var index = sourceIndex with { RowIndex = i };
|
||||
ref var row = ref table[i];
|
||||
DrawRow(ref table, ref row, texture, index);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRow(ref MtrlFile.ColorTable table, ref MtrlFile.ColorTable.Row row, Texture** texture, MaterialValueIndex sourceIndex)
|
||||
{
|
||||
using var id = ImRaii.PushId(sourceIndex.RowIndex);
|
||||
var diffuse = row.Diffuse;
|
||||
var specular = row.Specular;
|
||||
var emissive = row.Emissive;
|
||||
var glossStrength = row.GlossStrength;
|
||||
var specularStrength = row.SpecularStrength;
|
||||
if (ImGui.ColorEdit3("Diffuse", ref diffuse, ImGuiColorEditFlags.NoInputs))
|
||||
{
|
||||
var index = sourceIndex with { DataIndex = MaterialValueIndex.ColorTableIndex.Diffuse };
|
||||
row.Diffuse = diffuse;
|
||||
MaterialService.ReplaceColorTable(texture, table);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.ColorEdit3("Specular", ref specular, ImGuiColorEditFlags.NoInputs))
|
||||
{
|
||||
var index = sourceIndex with { DataIndex = MaterialValueIndex.ColorTableIndex.Specular };
|
||||
row.Specular = specular;
|
||||
MaterialService.ReplaceColorTable(texture, table);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.ColorEdit3("Emissive", ref emissive, ImGuiColorEditFlags.NoInputs))
|
||||
{
|
||||
var index = sourceIndex with { DataIndex = MaterialValueIndex.ColorTableIndex.Emissive };
|
||||
row.Emissive = emissive;
|
||||
MaterialService.ReplaceColorTable(texture, table);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.DragFloat("Gloss", ref glossStrength, 0.1f))
|
||||
{
|
||||
var index = sourceIndex with { DataIndex = MaterialValueIndex.ColorTableIndex.GlossStrength };
|
||||
row.GlossStrength = glossStrength;
|
||||
MaterialService.ReplaceColorTable(texture, table);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.DragFloat("Specular Strength", ref specularStrength, 0.1f))
|
||||
{
|
||||
var index = sourceIndex with { DataIndex = MaterialValueIndex.ColorTableIndex.SpecularStrength };
|
||||
row.SpecularStrength = specularStrength;
|
||||
MaterialService.ReplaceColorTable(texture, table);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyList<string> SlotNames =
|
||||
[
|
||||
"Slot 1",
|
||||
"Slot 2",
|
||||
"Slot 3",
|
||||
"Slot 4",
|
||||
"Slot 5",
|
||||
"Slot 6",
|
||||
"Slot 7",
|
||||
"Slot 8",
|
||||
"Slot 9",
|
||||
"Slot 10",
|
||||
"Slot 11",
|
||||
"Slot 12",
|
||||
"Slot 13",
|
||||
"Slot 14",
|
||||
"Slot 15",
|
||||
"Slot 16",
|
||||
"Slot 17",
|
||||
"Slot 18",
|
||||
"Slot 19",
|
||||
"Slot 20",
|
||||
];
|
||||
|
||||
private static readonly IReadOnlyList<string> SlotNamesHuman =
|
||||
[
|
||||
"Head",
|
||||
"Body",
|
||||
"Hands",
|
||||
"Legs",
|
||||
"Feet",
|
||||
"Earrings",
|
||||
"Neck",
|
||||
"Wrists",
|
||||
"Right Finger",
|
||||
"Left Finger",
|
||||
"Slot 11",
|
||||
"Slot 12",
|
||||
"Slot 13",
|
||||
"Slot 14",
|
||||
"Slot 15",
|
||||
"Slot 16",
|
||||
"Slot 17",
|
||||
"Slot 18",
|
||||
"Slot 19",
|
||||
"Slot 20",
|
||||
];
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using Glamourer.Automation;
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Gui.Materials;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Interop.Structs;
|
||||
|
|
@ -34,7 +35,8 @@ public class ActorPanel(
|
|||
ImportService _importService,
|
||||
ICondition _conditions,
|
||||
DictModelChara _modelChara,
|
||||
CustomizeParameterDrawer _parameterDrawer)
|
||||
CustomizeParameterDrawer _parameterDrawer,
|
||||
MaterialDrawer _materialDrawer)
|
||||
{
|
||||
private ActorIdentifier _identifier;
|
||||
private string _actorName = string.Empty;
|
||||
|
|
@ -121,16 +123,36 @@ public class ActorPanel(
|
|||
|
||||
RevertButtons();
|
||||
|
||||
|
||||
if (ImGui.CollapsingHeader("Material Shit"))
|
||||
_materialDrawer.DrawPanel(_actor);
|
||||
ImGui.InputInt("Row", ref _rowId);
|
||||
ImGui.InputInt("Material", ref _materialId);
|
||||
ImGui.InputInt("Slot", ref _slotId);
|
||||
ImGuiUtil.GenericEnumCombo("Value", 300, _index, out _index);
|
||||
|
||||
var index = new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Human, (byte) _slotId, (byte) _materialId, (byte)_rowId, _index);
|
||||
index.TryGetValue(_actor, out _test);
|
||||
index.TryGetValue(_actor, out var current);
|
||||
_test = current;
|
||||
if (ImGui.ColorPicker3("TestPicker", ref _test) && _actor.Valid)
|
||||
MaterialService.Test(_actor, index, _test);
|
||||
_state.Materials.AddOrUpdateValue(index, new MaterialValueState(current, _test, StateSource.Manual));
|
||||
|
||||
if (ImGui.ColorPicker3("TestPicker2", ref _test) && _actor.Valid)
|
||||
_state.Materials.AddOrUpdateValue(index, new MaterialValueState(current, _test, StateSource.Fixed));
|
||||
|
||||
foreach (var value in _state.Materials.Values)
|
||||
{
|
||||
var id = MaterialValueIndex.FromKey(value.Key);
|
||||
ImGui.TextUnformatted($"{id.DrawObject} {id.SlotIndex} {id.MaterialIndex} {id.RowIndex} {id.DataIndex} ");
|
||||
ImGui.SameLine(0, 0);
|
||||
var game = ImGui.ColorConvertFloat4ToU32(new Vector4(value.Value.Game, 1));
|
||||
ImGuiUtil.DrawTextButton(" ", Vector2.Zero, game);
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X);
|
||||
var model = ImGui.ColorConvertFloat4ToU32(new Vector4(value.Value.Model, 1));
|
||||
ImGuiUtil.DrawTextButton(" ", Vector2.Zero, model);
|
||||
ImGui.SameLine(0, 0);
|
||||
ImGui.TextUnformatted($" {value.Value.Source}");
|
||||
}
|
||||
|
||||
using var disabled = ImRaii.Disabled(transformationId != 0);
|
||||
if (_state.ModelData.IsHuman)
|
||||
|
|
|
|||
|
|
@ -1,44 +1,12 @@
|
|||
using Dalamud.Interface.Utility.Raii;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using Glamourer.Interop.Structs;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Files;
|
||||
using OtterGui;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using static Penumbra.GameData.Files.MtrlFile;
|
||||
using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture;
|
||||
|
||||
namespace Glamourer.Interop.Material;
|
||||
|
||||
|
||||
public class MaterialServiceDrawer(ActorManager actors) : IService
|
||||
{
|
||||
private ActorIdentifier _openIdentifier;
|
||||
private uint _openSlotIndex;
|
||||
|
||||
public unsafe void PopupButton(Actor actor, EquipSlot slot)
|
||||
{
|
||||
var slotIndex = slot.ToIndex();
|
||||
|
||||
var identifier = actor.GetIdentifier(actors);
|
||||
var buttonActive = actor.Valid
|
||||
&& identifier.IsValid
|
||||
&& actor.Model.IsCharacterBase
|
||||
&& slotIndex < actor.Model.AsCharacterBase->SlotCount
|
||||
&& (actor.Model.AsCharacterBase->HasModelInSlotLoaded & (1 << (int)slotIndex)) != 0;
|
||||
using var id = ImRaii.PushId((int)slot);
|
||||
if (ImGuiUtil.DrawDisabledButton("Advanced", Vector2.Zero, "Open advanced window.", !buttonActive))
|
||||
{
|
||||
_openIdentifier = identifier;
|
||||
_openSlotIndex = slotIndex;
|
||||
ImGui.OpenPopup($"Popup{slot}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe class MaterialService
|
||||
{
|
||||
public const int TextureWidth = 4;
|
||||
|
|
@ -49,7 +17,7 @@ public static unsafe class MaterialService
|
|||
/// <param name="original"> The original texture that will be replaced with a new one. </param>
|
||||
/// <param name="colorTable"> The input color table. </param>
|
||||
/// <returns> Success or failure. </returns>
|
||||
public static bool GenerateColorTable(Texture** original, in ColorTable colorTable)
|
||||
public static bool ReplaceColorTable(Texture** original, in ColorTable colorTable)
|
||||
{
|
||||
if (original == null)
|
||||
return false;
|
||||
|
|
@ -63,7 +31,7 @@ public static unsafe class MaterialService
|
|||
if (texture.IsInvalid)
|
||||
return false;
|
||||
|
||||
fixed(ColorTable* ptr = &colorTable)
|
||||
fixed (ColorTable* ptr = &colorTable)
|
||||
{
|
||||
if (!texture.Texture->InitializeContents(ptr))
|
||||
return false;
|
||||
|
|
@ -73,6 +41,22 @@ public static unsafe class MaterialService
|
|||
return true;
|
||||
}
|
||||
|
||||
public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* texture)
|
||||
{
|
||||
var textureSize = stackalloc int[2];
|
||||
textureSize[0] = TextureWidth;
|
||||
textureSize[1] = TextureHeight;
|
||||
|
||||
texture = Device.Instance()->CreateTexture2D(textureSize, 1, (uint)TexFile.TextureFormat.R16G16B16A16F,
|
||||
(uint)(TexFile.Attribute.TextureType2D | TexFile.Attribute.Managed | TexFile.Attribute.Immutable), 7);
|
||||
if (texture == null)
|
||||
return false;
|
||||
|
||||
fixed (ColorTable* ptr = &colorTable)
|
||||
{
|
||||
return texture->InitializeContents(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Obtain a pointer to the models pointer to a specific color table texture. </summary>
|
||||
/// <param name="model"></param>
|
||||
|
|
@ -112,19 +96,4 @@ public static unsafe class MaterialService
|
|||
|
||||
return (ColorTable*)material->ColorTable;
|
||||
}
|
||||
|
||||
public static void Test(Actor actor, MaterialValueIndex index, Vector3 value)
|
||||
{
|
||||
if (!index.TryGetColorTable(actor, out var table))
|
||||
return;
|
||||
|
||||
ref var row = ref table[index.RowIndex];
|
||||
if (!index.DataIndex.SetValue(ref row, value))
|
||||
return;
|
||||
|
||||
var texture = GetColorTableTexture(index.TryGetModel(actor, out var model) ? model : Model.Null, index.SlotIndex,
|
||||
index.MaterialIndex);
|
||||
if (texture != null)
|
||||
GenerateColorTable(texture, table);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
namespace Glamourer.Interop.Material;
|
||||
global using StateMaterialManager = Glamourer.Interop.Material.MaterialValueManager<Glamourer.Interop.Material.MaterialValueState>;
|
||||
global using DesignMaterialManager = Glamourer.Interop.Material.MaterialValueManager<System.Numerics.Vector3>;
|
||||
using Glamourer.State;
|
||||
|
||||
public readonly struct MaterialValueManager
|
||||
|
||||
namespace Glamourer.Interop.Material;
|
||||
|
||||
public record struct MaterialValueState(Vector3 Game, Vector3 Model, StateSource Source);
|
||||
|
||||
public readonly struct MaterialValueManager<T>
|
||||
{
|
||||
private readonly List<(uint Key, Vector3 Value)> _values = [];
|
||||
private readonly List<(uint Key, T Value)> _values = [];
|
||||
|
||||
public MaterialValueManager()
|
||||
{ }
|
||||
|
||||
public bool TryGetValue(MaterialValueIndex index, out Vector3 value)
|
||||
public void Clear()
|
||||
=> _values.Clear();
|
||||
|
||||
public bool TryGetValue(MaterialValueIndex index, out T value)
|
||||
{
|
||||
if (_values.Count == 0)
|
||||
{
|
||||
value = Vector3.Zero;
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -22,11 +32,11 @@ public readonly struct MaterialValueManager
|
|||
return true;
|
||||
}
|
||||
|
||||
value = Vector3.Zero;
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryAddValue(MaterialValueIndex index, in Vector3 value)
|
||||
public bool TryAddValue(MaterialValueIndex index, in T value)
|
||||
{
|
||||
var key = index.Key;
|
||||
var idx = Search(key);
|
||||
|
|
@ -50,7 +60,7 @@ public readonly struct MaterialValueManager
|
|||
return true;
|
||||
}
|
||||
|
||||
public void AddOrUpdateValue(MaterialValueIndex index, in Vector3 value)
|
||||
public void AddOrUpdateValue(MaterialValueIndex index, in T value)
|
||||
{
|
||||
var key = index.Key;
|
||||
var idx = Search(key);
|
||||
|
|
@ -60,11 +70,11 @@ public readonly struct MaterialValueManager
|
|||
_values[idx] = (key, value);
|
||||
}
|
||||
|
||||
public bool UpdateValue(MaterialValueIndex index, in Vector3 value, out Vector3 oldValue)
|
||||
public bool UpdateValue(MaterialValueIndex index, in T value, out T oldValue)
|
||||
{
|
||||
if (_values.Count == 0)
|
||||
{
|
||||
oldValue = Vector3.Zero;
|
||||
oldValue = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +82,7 @@ public readonly struct MaterialValueManager
|
|||
var idx = Search(key);
|
||||
if (idx < 0)
|
||||
{
|
||||
oldValue = Vector3.Zero;
|
||||
oldValue = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +91,9 @@ public readonly struct MaterialValueManager
|
|||
return true;
|
||||
}
|
||||
|
||||
public IReadOnlyList<(uint Key, T Value)> Values
|
||||
=> _values;
|
||||
|
||||
public int RemoveValues(MaterialValueIndex min, MaterialValueIndex max)
|
||||
{
|
||||
var (minIdx, maxIdx) = GetMinMax(CollectionsMarshal.AsSpan(_values), min.Key, max.Key);
|
||||
|
|
@ -92,21 +105,21 @@ public readonly struct MaterialValueManager
|
|||
return count;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<(uint key, Vector3 Value)> GetValues(MaterialValueIndex min, MaterialValueIndex max)
|
||||
public ReadOnlySpan<(uint key, T Value)> GetValues(MaterialValueIndex min, MaterialValueIndex max)
|
||||
=> Filter(CollectionsMarshal.AsSpan(_values), min, max);
|
||||
|
||||
public static ReadOnlySpan<(uint Key, Vector3 Value)> Filter(ReadOnlySpan<(uint Key, Vector3 Value)> values, MaterialValueIndex min,
|
||||
public static ReadOnlySpan<(uint Key, T Value)> Filter(ReadOnlySpan<(uint Key, T Value)> values, MaterialValueIndex min,
|
||||
MaterialValueIndex max)
|
||||
{
|
||||
var (minIdx, maxIdx) = GetMinMax(values, min.Key, max.Key);
|
||||
return minIdx < 0 ? [] : values[minIdx..(maxIdx - minIdx)];
|
||||
return minIdx < 0 ? [] : values[minIdx..(maxIdx - minIdx + 1)];
|
||||
}
|
||||
|
||||
/// <summary> Obtain the minimum index and maximum index for a minimum and maximum key. </summary>
|
||||
private static (int MinIdx, int MaxIdx) GetMinMax(ReadOnlySpan<(uint Key, Vector3 Value)> values, uint minKey, uint maxKey)
|
||||
private static (int MinIdx, int MaxIdx) GetMinMax(ReadOnlySpan<(uint Key, T Value)> values, uint minKey, uint maxKey)
|
||||
{
|
||||
// Find the minimum index by binary search.
|
||||
var idx = values.BinarySearch((minKey, Vector3.Zero), Comparer.Instance);
|
||||
var idx = values.BinarySearch((minKey, default!), Comparer.Instance);
|
||||
var minIdx = idx;
|
||||
|
||||
// If the key does not exist, check if it is an invalid range or set it correctly.
|
||||
|
|
@ -131,7 +144,7 @@ public readonly struct MaterialValueManager
|
|||
|
||||
|
||||
// Do pretty much the same but in the other direction with the maximum key.
|
||||
var maxIdx = values[idx..].BinarySearch((maxKey, Vector3.Zero), Comparer.Instance);
|
||||
var maxIdx = values[idx..].BinarySearch((maxKey, default!), Comparer.Instance);
|
||||
if (maxIdx < 0)
|
||||
{
|
||||
maxIdx = ~maxIdx;
|
||||
|
|
@ -149,14 +162,14 @@ public readonly struct MaterialValueManager
|
|||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private int Search(uint key)
|
||||
=> _values.BinarySearch((key, Vector3.Zero), Comparer.Instance);
|
||||
=> _values.BinarySearch((key, default!), Comparer.Instance);
|
||||
|
||||
private class Comparer : IComparer<(uint Key, Vector3 Value)>
|
||||
private class Comparer : IComparer<(uint Key, T Value)>
|
||||
{
|
||||
public static readonly Comparer Instance = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public int Compare((uint Key, Vector3 Value) x, (uint Key, Vector3 Value) y)
|
||||
int IComparer<(uint Key, T Value)>.Compare((uint Key, T Value) x, (uint Key, T Value) y)
|
||||
=> x.Key.CompareTo(y.Key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Glamourer.State;
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -58,28 +57,18 @@ public sealed unsafe class PrepareColorSet
|
|||
return _task.Result.Original(characterBase, material, stainId);
|
||||
}
|
||||
|
||||
public static MtrlFile.ColorTable GetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainId stainId)
|
||||
public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainId stainId, out MtrlFile.ColorTable table)
|
||||
{
|
||||
var table = new MtrlFile.ColorTable();
|
||||
characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&table));
|
||||
return table;
|
||||
}
|
||||
|
||||
public static bool TryGetColorTable(Model model, byte slotIdx, out MtrlFile.ColorTable table)
|
||||
{
|
||||
var table2 = new MtrlFile.ColorTable();
|
||||
if (!model.IsCharacterBase || slotIdx < model.AsCharacterBase->SlotCount)
|
||||
return false;
|
||||
|
||||
var resource = (MaterialResourceHandle*)model.AsCharacterBase->Materials[slotIdx];
|
||||
var stain = model.AsCharacterBase->GetModelType() switch
|
||||
if (material->ColorTable == null)
|
||||
{
|
||||
CharacterBase.ModelType.Human => model.GetArmor(EquipSlotExtensions.ToEquipSlot(slotIdx)).Stain,
|
||||
CharacterBase.ModelType.Weapon => (StainId)model.AsWeapon->ModelUnknown,
|
||||
_ => (StainId)0,
|
||||
};
|
||||
model.AsCharacterBase->ReadStainingTemplate(resource, stain.Id, (Half*)(&table2));
|
||||
table = table2;
|
||||
table = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var newTable = *(MtrlFile.ColorTable*)material->ColorTable;
|
||||
if(stainId.Id != 0)
|
||||
characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable));
|
||||
table = newTable;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -111,10 +100,6 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
var actor = _penumbra.GameObjectFromDrawObject(characterBase);
|
||||
var validType = FindType(characterBase, actor, out var type);
|
||||
var (slotId, materialId) = FindMaterial(characterBase, material);
|
||||
Glamourer.Log.Information(
|
||||
$" Triggered with 0x{(nint)characterBase:X} 0x{(nint)material:X} {stain.Id} --- Actor: 0x{actor.Address:X} Slot: {slotId} Material: {materialId} DrawObject: {type}.");
|
||||
var table = PrepareColorSet.GetColorTable(characterBase, material, stain);
|
||||
Glamourer.Log.Information($"{table[15].Diffuse}");
|
||||
|
||||
if (!validType
|
||||
|| slotId == byte.MaxValue
|
||||
|
|
@ -122,12 +107,49 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
|| !_stateManager.TryGetValue(identifier, out var state))
|
||||
return;
|
||||
|
||||
|
||||
var min = MaterialValueIndex.Min(type, slotId, materialId);
|
||||
var max = MaterialValueIndex.Max(type, slotId, materialId);
|
||||
var manager = new MaterialValueManager();
|
||||
var values = manager.GetValues(min, max);
|
||||
foreach (var (key, value) in values)
|
||||
;
|
||||
var values = state.Materials.GetValues(min, max);
|
||||
if (values.Length == 0)
|
||||
return;
|
||||
|
||||
if (!PrepareColorSet.TryGetColorTable(characterBase, material, stain, out var baseColorSet))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < values.Length; ++i)
|
||||
{
|
||||
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)
|
||||
{
|
||||
case StateSource.Manual:
|
||||
case StateSource.Pending:
|
||||
state.Materials.RemoveValue(idx);
|
||||
--i;
|
||||
break;
|
||||
case StateSource.Ipc:
|
||||
case StateSource.Fixed:
|
||||
idx.DataIndex.SetValue(ref row, model);
|
||||
state.Materials.UpdateValue(idx, new MaterialValueState(newGame, model, source), out _);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MaterialService.GenerateNewColorTable(baseColorSet, out var texture))
|
||||
ret = (nint)texture;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ public class ActorState
|
|||
/// <summary> The territory the draw object was created last. </summary>
|
||||
public ushort LastTerritory;
|
||||
|
||||
/// <summary> State for specific material values. </summary>
|
||||
public readonly StateMaterialManager Materials = new();
|
||||
|
||||
/// <summary> Whether the State is locked at all. </summary>
|
||||
public bool IsLocked
|
||||
=> Combination != 0;
|
||||
|
|
|
|||
|
|
@ -240,6 +240,8 @@ public sealed class StateManager(
|
|||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
state.Sources[flag] = StateSource.Game;
|
||||
|
||||
state.Materials.Clear();
|
||||
|
||||
var actors = ActorData.Invalid;
|
||||
if (source is StateSource.Manual or StateSource.Ipc)
|
||||
actors = Applier.ApplyAll(state, redraw, true);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue