From f24be919f22e008cfcaa5fece576f25f4e575536 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Thu, 17 Oct 2024 01:23:58 +1100 Subject: [PATCH 1/6] Blender addon related changes - Use proper default from shpk for materials - Fix tattoo shader using hair params - Allow viewing file info in layout tab - Logging changes --- .../Models/Composer/DataProvider.cs | 22 +- .../Models/Composer/MaterialSet.cs | 102 ++++---- .../Composer/Materials/BgMaterialBuilder.cs | 170 -------------- .../Materials/CharacterMaterialBuilder.cs | 6 +- .../CharacterTattooMaterialBuilder.cs | 12 +- .../Materials/GenericMaterialBuilder.cs | 22 +- .../Composer/Materials/HairMaterialBuilder.cs | 2 +- .../Materials/MeddleMaterialBuilder.cs | 1 + .../Composer/Materials/SkinMaterialBuilder.cs | 2 +- .../Structs/SharedGroupLayoutExtension.cs | 21 ++ .../Meddle.Plugin/Services/HousingService.cs | 218 ------------------ .../Meddle.Plugin/Services/LayoutService.cs | 52 +++-- Meddle/Meddle.Plugin/UI/DebugTab.cs | 29 ++- Meddle/Meddle.Plugin/UI/Layout/Instance.cs | 194 ++++++++++++++++ .../Meddle.Plugin/UI/Layout/LayoutWindow.cs | 4 + Meddle/Meddle.Plugin/UI/Layout/Overlay.cs | 3 + Meddle/Meddle.Utils/Constants/Material.cs | 9 +- Meddle/Meddle.Utils/Export/ShaderPackage.cs | 23 ++ 18 files changed, 396 insertions(+), 496 deletions(-) delete mode 100644 Meddle/Meddle.Plugin/Models/Composer/Materials/BgMaterialBuilder.cs create mode 100644 Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs delete mode 100644 Meddle/Meddle.Plugin/Services/HousingService.cs diff --git a/Meddle/Meddle.Plugin/Models/Composer/DataProvider.cs b/Meddle/Meddle.Plugin/Models/Composer/DataProvider.cs index 767d840..21bc3a9 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/DataProvider.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/DataProvider.cs @@ -37,8 +37,16 @@ public MaterialBuilder GetMaterialBuilder(MaterialSet material, string path, str cancellationToken.ThrowIfCancellationRequested(); return new Lazy(() => { - logger.LogInformation("[{shpkName}] Composing material {path}", shpkName, path); - return material.Compose(this); + try + { + logger.LogInformation("[{shpkName}] Composing material {path}", shpkName, path); + return material.Compose(this); + } + catch (Exception e) + { + logger.LogError(e, "[{shpkName}] Failed to compose material {path}", shpkName, path); + return new MaterialBuilder(path); + } }, LazyThreadSafetyMode.ExecutionAndPublication); }).Value; } @@ -137,10 +145,8 @@ public ShpkFile GetShpkFile(string fullPath) return outPath; } - - - public ImageBuilder CacheTexture(SKTexture texture, string texName) + public static string FilterTexName(string texName) { texName = texName.TrimHandlePath(); if (Path.IsPathRooted(texName)) @@ -148,6 +154,12 @@ public ImageBuilder CacheTexture(SKTexture texture, string texName) texName = Path.GetFileName(texName); } + return texName; + } + + public ImageBuilder CacheTexture(SKTexture texture, string texName) + { + texName = FilterTexName(texName); var outPath = Path.Combine(cacheDir, $"{texName}.png"); SaveTextureToDisk(texture, outPath); diff --git a/Meddle/Meddle.Plugin/Models/Composer/MaterialSet.cs b/Meddle/Meddle.Plugin/Models/Composer/MaterialSet.cs index d707ee6..3d5f6e0 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/MaterialSet.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/MaterialSet.cs @@ -47,9 +47,9 @@ public int Uid() hash.Add(key); hash.Add(value); } - foreach (var key in ShaderKeys) + foreach (var key in ShaderKeyDict) { - hash.Add(key.Category); + hash.Add(key.Key); hash.Add(key.Value); } return hash.ToHashCode(); @@ -86,8 +86,28 @@ public string HashStr() //private readonly ShaderPackage package; public readonly string MtrlPath; public readonly string ShpkName; - public readonly ShaderKey[] ShaderKeys; - public readonly Dictionary Constants; + public Dictionary Constants => AllConstants(); + public readonly Dictionary ShpkConstants; + public readonly Dictionary MtrlConstants; + public readonly Dictionary ShaderKeyDict; + private Dictionary AdditionalProperties = new(); + + public void AddProperty(string key, object value) + { + AdditionalProperties[key] = value; + } + + private Dictionary AllConstants() + { + var all = new Dictionary(ShpkConstants); + foreach (var (key, value) in MtrlConstants) + { + all[key] = value; + } + + return all; + } + public readonly Dictionary TextureUsageDict; private readonly Func? textureLoader; @@ -312,31 +332,29 @@ public Vector4 GetConstantOrDefault(MaterialConstant id, Vector4 @default) : @default; } - public TValue GetShaderKeyOrDefault(TCategory category, TValue @default) where TCategory : Enum where TValue : Enum + public uint GetShaderKeyOrDefault(uint category) { - var cat = Convert.ToUInt32(category); - var value = GetShaderKeyOrDefault(cat, Convert.ToUInt32(@default)); - return (TValue)Enum.ToObject(typeof(TValue), value); + return GetShaderKeyOrDefault((ShaderCategory)category); } - public TValue GetShaderKeyOrDefault(TCategory category, uint @default) where TCategory : Enum where TValue : Enum + public uint GetShaderKeyOrDefault(ShaderCategory category) { - var cat = Convert.ToUInt32(category); - var value = GetShaderKeyOrDefault(cat, @default); - return (TValue)Enum.ToObject(typeof(TValue), value); + if (ShaderKeyDict.TryGetValue(category, out var value)) + { + return value; + } + + throw new InvalidOperationException($"Shader key {category} not found"); } - public uint GetShaderKeyOrDefault(uint category, uint @default) + public TValue GetShaderKeyOrDefault(ShaderCategory category) where TValue : Enum { - foreach (var key in ShaderKeys) + if (ShaderKeyDict.TryGetValue(category, out var value)) { - if (key.Category == category) - { - return key.Value; - } + return (TValue)(object)value; } - - return @default; + + throw new InvalidOperationException($"Shader key {category} not found"); } public MaterialSet(MtrlFile file, string mtrlPath, ShpkFile shpk, string shpkName, HandleString[]? texturePathOverride, Func? textureLoader) @@ -347,8 +365,18 @@ public MaterialSet(MtrlFile file, string mtrlPath, ShpkFile shpk, string shpkNam shaderFlagData = file.ShaderHeader.Flags; var package = new ShaderPackage(shpk, shpkName); colorTable = file.GetColorTable(); - ShaderKeys = file.ShaderKeys; - Constants = package.MaterialConstants; + ShpkConstants = package.MaterialConstants; + MtrlConstants = new Dictionary(); + ShaderKeyDict = new Dictionary(); + foreach (var (key, value) in package.DefaultKeyValues) + { + ShaderKeyDict[(ShaderCategory)key] = value; + } + + foreach (var key in file.ShaderKeys) + { + ShaderKeyDict[(ShaderCategory)key.Category] = key.Value; + } // override with material constants foreach (var constant in file.Constants) @@ -371,7 +399,7 @@ public MaterialSet(MtrlFile file, string mtrlPath, ShpkFile shpk, string shpkNam } // even if duplicate, last probably takes precedence - Constants[id] = MemoryMarshal.Cast(buf.ToArray()).ToArray(); + MtrlConstants[id] = MemoryMarshal.Cast(buf.ToArray()).ToArray(); if (logOutOfBounds) { Plugin.Logger?.LogWarning("Material constant {id} out of bounds for {mtrlPath}, {indexAndCount} greater than {shaderValuesLength}, [{values}]", @@ -410,16 +438,9 @@ public MaterialSet(MtrlFile file, string mtrlPath, ShpkFile shpk, string shpkNam private MeddleMaterialBuilder GetMaterialBuilder(DataProvider dataProvider) { - var mtrlName = $"{Path.GetFileNameWithoutExtension(MtrlPath)}_{Path.GetFileNameWithoutExtension(ShpkName)}_{Uid()}"; + var mtrlName = $"{Path.GetFileNameWithoutExtension(MtrlPath)}_{Path.GetFileNameWithoutExtension(ShpkName)}_{HashStr()}"; switch (ShpkName) { - case "bg.shpk": - case "bguvscroll.shpk": - return new BgMaterialBuilder(mtrlName, new BgParams(), this, dataProvider); - case "bgcolorchange.shpk": - return new BgMaterialBuilder(mtrlName, new BgColorChangeParams(stainColor), this, dataProvider); - case "lightshaft.shpk": - return new LightshaftMaterialBuilder(mtrlName, this, dataProvider); case "character.shpk": case "characterlegacy.shpk": return new CharacterMaterialBuilder(mtrlName, this, dataProvider, colorTable, textureMode); @@ -476,6 +497,11 @@ public Dictionary ComposeExtras() AddCustomizeData(); AddStainColor(); AddColorTable(); + + foreach (var (key, value) in AdditionalProperties) + { + extrasDict[key] = value; + } return extrasDict; @@ -532,14 +558,13 @@ string IsDefinedOrHex(TEnum value) where TEnum : Enum void AddShaderKeys() { - foreach (var key in ShaderKeys) + foreach (var key in ShaderKeyDict) { - var category = key.Category; + uint category = (uint)key.Key; var value = key.Value; if (Enum.IsDefined(typeof(ShaderCategory), category)) { - var keyCat = (ShaderCategory)category; - var valStr = keyCat switch + var valStr = key.Key switch { ShaderCategory.CategoryHairType => IsDefinedOrHex((HairType)value), ShaderCategory.CategorySkinType => IsDefinedOrHex((SkinType)value), @@ -551,7 +576,7 @@ void AddShaderKeys() _ => $"0x{value:X8}" }; - extrasDict[keyCat.ToString()] = valStr; + extrasDict[key.Key.ToString()] = valStr; } else { @@ -567,10 +592,7 @@ void AddSamplers() { var usageStr = usage.ToString(); extrasDict[usageStr] = path.GamePath; - if (path.FullPath != path.GamePath) - { - extrasDict[$"{usageStr}_FullPath"] = path.FullPath; - } + extrasDict[$"{usageStr}_FullPath"] = path.FullPath; } } diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/BgMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/BgMaterialBuilder.cs deleted file mode 100644 index e18b35e..0000000 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/BgMaterialBuilder.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Numerics; -using Meddle.Plugin.Utils; -using Meddle.Utils; -using Meddle.Utils.Constants; -using Meddle.Utils.Export; -using Meddle.Utils.Files; -using Meddle.Utils.Helpers; -using Meddle.Utils.Materials; -using SharpGLTF.Materials; - -namespace Meddle.Plugin.Models.Composer.Materials; - - -public interface IBgMaterialBuilderParams; - -public record BgColorChangeParams : IBgMaterialBuilderParams -{ - public Vector4? StainColor { get; init; } - - public BgColorChangeParams(Vector4? stainColor) - { - StainColor = stainColor; - } -} - -public record BgParams : IBgMaterialBuilderParams; - - -public class BgMaterialBuilder : MeddleMaterialBuilder -{ - private readonly IBgMaterialBuilderParams bgParams; - private readonly MaterialSet set; - private readonly DataProvider dataProvider; - public BgMaterialBuilder(string name, IBgMaterialBuilderParams bgParams, MaterialSet set, DataProvider dataProvider) : base(name) - { - this.bgParams = bgParams; - this.set = set; - this.dataProvider = dataProvider; - } - - private record TextureSet(SKTexture Color0, SKTexture Specular0, SKTexture Normal0, SKTexture? Color1, SKTexture? Specular1, SKTexture? Normal1); - - // DetailID = - // bgcommon/nature/detail/texture/detail_d_array.tex - // bgcommon/nature/detail/texture/detail_n_array.tex - private static TexFile? DetailDArray; - private static TexFile? DetailNArray; - private static readonly object DetailLock = new(); - private record DetailSet(SKTexture Diffuse, SKTexture Normal, Vector3 DetailColor, float DetailNormalScale, Vector4 DetailColorUvScale, Vector4 DetailNormalUvScale); - - private DetailSet GetDetail(int detailId, Vector2 size) - { - lock (DetailLock) - { - const int maxDetailId = 32; - if (detailId < 0 || detailId >= maxDetailId) - { - throw new ArgumentOutOfRangeException(nameof(detailId), - $"Detail ID must be between 0 and {maxDetailId - 1}"); - } - - if (DetailDArray == null) - { - var detailDArray = dataProvider.LookupData("bgcommon/nature/detail/texture/detail_d_array.tex"); - if (detailDArray == null) - { - throw new Exception("Detail D array texture not found"); - } - - DetailDArray = new TexFile(detailDArray); - } - - if (DetailNArray == null) - { - var detailNArray = dataProvider.LookupData("bgcommon/nature/detail/texture/detail_n_array.tex"); - if (detailNArray == null) - { - throw new Exception("Detail N array texture not found"); - } - - DetailNArray = new TexFile(detailNArray); - } - - var detailD = ImageUtils.GetTexData(DetailDArray, detailId, 0, 0).ToTexture(size); - var detailN = ImageUtils.GetTexData(DetailNArray, detailId, 0, 0).ToTexture(size); - - if (!set.TryGetConstant(MaterialConstant.g_DetailColor, out Vector3 detailColor)) - { - detailColor = Vector3.One; - } - - if (!set.TryGetConstant(MaterialConstant.g_DetailNormalScale, out float detailNormalScale)) - { - detailNormalScale = 1; - } - - if (!set.TryGetConstant(MaterialConstant.g_DetailColorUvScale, out Vector4 detailColorUvScale)) - { - detailColorUvScale = new Vector4(4); - } - - if (!set.TryGetConstant(MaterialConstant.g_DetailNormalUvScale, out Vector4 detailNormalUvScale)) - { - detailNormalUvScale = new Vector4(4); - } - - return new DetailSet(detailD, detailN, detailColor, detailNormalScale, detailColorUvScale, detailNormalUvScale); - } - } - - - private bool GetDiffuseColor(out Vector4 diffuseColor) - { - if (!set.TryGetConstant(MaterialConstant.g_DiffuseColor, out Vector3 diffuseColor3)) - { - diffuseColor = Vector4.One; - return false; - } - - diffuseColor = new Vector4(diffuseColor3, 1); - return true; - } - - public override MeddleMaterialBuilder Apply() - { - var extras = new List<(string, object)>(); - - var colorMap0Texture = set.GetImageBuilderStrict(dataProvider, TextureUsage.g_SamplerColorMap0); - var specularMap0Texture = set.GetImageBuilderStrict(dataProvider, TextureUsage.g_SamplerSpecularMap0); - var normalMap0Texture = set.GetImageBuilderStrict(dataProvider, TextureUsage.g_SamplerNormalMap0); - - set.TryGetImageBuilder(dataProvider, TextureUsage.g_SamplerColorMap1, out var colorMap1Texture); - set.TryGetImageBuilder(dataProvider, TextureUsage.g_SamplerSpecularMap1, out var specularMap1Texture); - set.TryGetImageBuilder(dataProvider, TextureUsage.g_SamplerNormalMap1, out var normalMap1Texture); - - - var alphaType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryDiffuseAlpha, DiffuseAlpha.Default); - if (alphaType == DiffuseAlpha.UseDiffuseAlphaAsOpacity) - { - var alphaThreshold = set.GetConstantOrThrow(MaterialConstant.g_AlphaThreshold); - WithAlpha(AlphaMode.MASK, alphaThreshold); // TODO: which mode? - } - - if (bgParams is BgColorChangeParams bgColorChangeParams && GetDiffuseColor(out var bgColorChangeDiffuseColor)) - { - Vector4 diffuseColor; - if (bgColorChangeParams.StainColor != null && bgColorChangeParams.StainColor != Vector4.Zero) - { - diffuseColor = bgColorChangeParams.StainColor.Value with { W = 1.0f }; - } - else - { - diffuseColor = bgColorChangeDiffuseColor; - } - - extras.Add(("DiffuseColor", diffuseColor.AsFloatArray())); - } - - var vertexPaintValue = set.GetShaderKeyOrDefault(ShaderCategory.CategoryBgVertexPaint, BgVertexPaint.Off); - VertexPaint = vertexPaintValue == BgVertexPaint.On; - - WithNormal(normalMap0Texture); - WithBaseColor(colorMap0Texture); - // only the green/y channel is used here for roughness - WithMetallicRoughness(specularMap0Texture, 0.0f, 1.0f); - - Extras = set.ComposeExtrasNode(extras.ToArray()); - return this; - } -} diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs index b5c4ad2..2b888b1 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs @@ -27,9 +27,9 @@ public CharacterMaterialBuilder(string name, MaterialSet set, DataProvider dataP private void ApplyComputed() { - var textureMode = set.GetShaderKeyOrDefault(ShaderCategory.GetValuesTextureType, Meddle.Utils.Constants.TextureMode.Default); - var specularMode = set.GetShaderKeyOrDefault(ShaderCategory.CategorySpecularType, SpecularMode.Default); // TODO: is default actually default - var flowType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryFlowMapType, FlowType.Standard); + var textureMode = set.GetShaderKeyOrDefault(ShaderCategory.GetValuesTextureType); + var specularMode = set.GetShaderKeyOrDefault(ShaderCategory.CategorySpecularType); + var flowType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryFlowMapType); if (!set.TryGetTextureStrict(dataProvider, TextureUsage.g_SamplerNormal, out var normalRes)) throw new InvalidOperationException("Missing normal texture"); diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterTattooMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterTattooMaterialBuilder.cs index ea4d687..0a93052 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterTattooMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterTattooMaterialBuilder.cs @@ -26,15 +26,7 @@ public CharacterTattooMaterialBuilder( public override MeddleMaterialBuilder Apply() { - var hairType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryHairType, (HairType)0); - - Vector3 color = hairType switch - { - HairType.Face => customizeParameter.OptionColor, - HairType.Hair => customizeParameter.MeshColor, - _ => Vector3.Zero - }; - + var influenceColor = customizeParameter.OptionColor; if (!set.TryGetTextureStrict(dataProvider, TextureUsage.g_SamplerNormal, out var normalRes)) throw new InvalidOperationException("Missing normal texture"); @@ -47,7 +39,7 @@ public override MeddleMaterialBuilder Apply() if (influence > 0) { - diffuseTexture[x, y] = new Vector4(color, normal.W).ToSkColor(); + diffuseTexture[x, y] = new Vector4(influenceColor, normal.W).ToSkColor(); } else { diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/GenericMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/GenericMaterialBuilder.cs index 40cd34f..6e0a166 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/GenericMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/GenericMaterialBuilder.cs @@ -20,27 +20,7 @@ public GenericMaterialBuilder(string name, MaterialSet set, DataProvider dataPro public override MeddleMaterialBuilder Apply() { - var alphaThreshold = set.GetConstantOrDefault(MaterialConstant.g_AlphaThreshold, 0.0f); - if (alphaThreshold > 0) - WithAlpha(AlphaMode.MASK, alphaThreshold); - - var setTypes = new HashSet(); - foreach (var textureUsage in set.TextureUsageDict) - { - var texData = dataProvider.LookupData(textureUsage.Value.FullPath); - if (texData == null) continue; - // caching the texture regardless of usage, but only applying it to the material if it's a known channel - var texture = new TexFile(texData).ToResource().ToTexture(); - var tex = dataProvider.CacheTexture(texture, textureUsage.Value.FullPath); - - var channel = MapTextureUsageToChannel(textureUsage.Key); - if (channel != null && setTypes.Add(textureUsage.Key)) - { - WithChannelImage(channel.Value, tex); - } - } - - IndexOfRefraction = set.GetConstantOrDefault(MaterialConstant.g_GlassIOR, 1.0f); + ApplyRaw(set, dataProvider); Extras = set.ComposeExtrasNode(); return this; } diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/HairMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/HairMaterialBuilder.cs index a22b931..c46ce97 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/HairMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/HairMaterialBuilder.cs @@ -28,7 +28,7 @@ public HairMaterialBuilder( private void ApplyComputed() { - var hairType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryHairType, HairType.Hair); + var hairType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryHairType); if (!set.TryGetTextureStrict(dataProvider, TextureUsage.g_SamplerNormal, out var normalRes)) throw new InvalidOperationException("Missing normal texture"); diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/MeddleMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/MeddleMaterialBuilder.cs index afd10b0..8c2cca2 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/MeddleMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/MeddleMaterialBuilder.cs @@ -25,6 +25,7 @@ internal void ApplyRaw(MaterialSet set, DataProvider dataProvider) { var texName = $"{Path.GetFileNameWithoutExtension(set.MtrlPath)}_{hashStr}/{texture.Value.GamePath}"; var builder = dataProvider.CacheTexture(tex.ToTexture(), texName); + set.AddProperty($"{texture.Key}_PngCachePath", $"{DataProvider.FilterTexName(texName)}.png"); var mapped = GenericMaterialBuilder.MapTextureUsageToChannel(texture.Key); if (mapped != null) { diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/SkinMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/SkinMaterialBuilder.cs index 1d725b5..bcbc079 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/SkinMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/SkinMaterialBuilder.cs @@ -30,7 +30,7 @@ public SkinMaterialBuilder( private void ApplyComputed() { - var skinType = set.GetShaderKeyOrDefault(ShaderCategory.CategorySkinType, SkinType.Face); + var skinType = set.GetShaderKeyOrDefault(ShaderCategory.CategorySkinType); // var normalTexture = set.GetTexture(dataProvider, TextureUsage.g_SamplerNormal).ToResource().ToTexture(); // var maskTexture = set.GetTexture(dataProvider, TextureUsage.g_SamplerMask).ToResource().ToTexture(normalTexture.Size); diff --git a/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs b/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs new file mode 100644 index 0000000..ba68987 --- /dev/null +++ b/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; +using FFXIVClientStructs.FFXIV.Client.Game.Object; +using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Group; + +namespace Meddle.Plugin.Models.Structs; + +[StructLayout(LayoutKind.Explicit, Size = 0x1D0)] +public struct MeddleHousingObject { + [FieldOffset(0x00)] public HousingObject HousingObject; + [FieldOffset(0x1B0)] public byte ColorId; +} + +[StructLayout(LayoutKind.Explicit, Size = 0x1E0)] +public unsafe struct MeddleSharedGroupLayoutInstance +{ + [FieldOffset(0x00)] public SharedGroupLayoutInstance SharedGroupLayoutInstance; + [FieldOffset(0x1C)] public byte ColorId; + [FieldOffset(0x1F)] public byte HasHousingObject; + [FieldOffset(0x20)] public byte ColorId0; + [FieldOffset(0x21)] public byte ColorId1; +} diff --git a/Meddle/Meddle.Plugin/Services/HousingService.cs b/Meddle/Meddle.Plugin/Services/HousingService.cs deleted file mode 100644 index 647b8f1..0000000 --- a/Meddle/Meddle.Plugin/Services/HousingService.cs +++ /dev/null @@ -1,218 +0,0 @@ -using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.Game.Object; -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using FFXIVClientStructs.FFXIV.Client.LayoutEngine; -using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Group; -using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Layer; -using FFXIVClientStructs.Interop; -using Lumina.Excel.GeneratedSheets; -using Meddle.Plugin.Models.Structs; -using Microsoft.Extensions.Logging; -using HousingFurniture = FFXIVClientStructs.FFXIV.Client.Game.HousingFurniture; - -namespace Meddle.Plugin.Services; - -public class HousingService : IService -{ - private readonly SigUtil sigUtil; - private readonly ILogger logger; - private readonly Dictionary stainDict; - private readonly Dictionary itemDict; - - public HousingService(SigUtil sigUtil, ILogger logger, IDataManager dataManager) - { - this.sigUtil = sigUtil; - this.logger = logger; - this.stainDict = dataManager.GetExcelSheet()!.ToDictionary(row => row.RowId, row => row); - this.itemDict = dataManager.GetExcelSheet()! - .Where(item => item.AdditionalData != 0 && item.ItemSearchCategory.Row is 65 or 66) - .ToDictionary(row => row.AdditionalData, row => row); - } - - public unsafe Dictionary GetHousingItems() - { - var housingManager = sigUtil.GetHousingManager(); - if (housingManager == null) - return []; - - if (housingManager->CurrentTerritory == null) - return []; - - var territoryType = housingManager->CurrentTerritory->GetTerritoryType(); - switch (territoryType) - { - case HousingTerritoryType.Indoor: - { - var indoorTerritory = housingManager->IndoorTerritory; - if (indoorTerritory == null) - { - logger.LogWarning("Indoor territory is null"); - return []; - } - - var items = new Dictionary(); - for (int i = 0; i < indoorTerritory->Furniture.Length; i++) - { - var furniture = indoorTerritory->Furniture[i]; - var index = furniture.Index; - if (furniture.Index == -1) continue; - try - { - var objectPtr = indoorTerritory->HousingObjectManager.Objects[index]; - if (objectPtr == null || objectPtr.Value == null) - { - continue; - } - - if (objectPtr.Value->LayoutInstance == null) - { - logger.LogWarning("LayoutInstance is null for object at index {Index}", index); - continue; - } - - var layoutInstance = objectPtr.Value->LayoutInstance; - var bgParts = ParseBgObjectsFromInstance(layoutInstance); - - var item = new HousingItem - { - Furniture = furniture, - Object = objectPtr, - Stain = stainDict.GetValueOrDefault(furniture.Stain), - Item = itemDict.GetValueOrDefault(furniture.Id), - BgParts = bgParts - }; - items[(nint)objectPtr.Value] = item; - } - catch (Exception e) - { - logger.LogError(e, "Error getting object at index {Index}", index); - } - } - - return items; - } - case HousingTerritoryType.Outdoor: - { - var outdoorTerritory = housingManager->OutdoorTerritory; - if (outdoorTerritory == null) - return []; - - var items = new Dictionary(); - for (int i = 0; i < outdoorTerritory->Furniture.Length; i++) - { - var furniture = outdoorTerritory->Furniture[i]; - var index = furniture.Index; - if (furniture.Index == -1) continue; - try - { - var objectPtr = outdoorTerritory->HousingObjectManager.Objects[index]; - if (objectPtr == null || objectPtr.Value == null) - { - continue; - } - - if (objectPtr.Value->LayoutInstance == null) - { - logger.LogWarning("LayoutInstance is null for object at index {Index}", index); - continue; - } - - var layoutInstance = objectPtr.Value->LayoutInstance; - var bgParts = ParseBgObjectsFromInstance(layoutInstance); - - var item = new HousingItem - { - Furniture = furniture, - Object = objectPtr, - Stain = stainDict.GetValueOrDefault(furniture.Stain), - Item = itemDict.GetValueOrDefault(furniture.Id), - BgParts = bgParts - }; - items[(nint)objectPtr.Value] = item; - } - catch (Exception e) - { - logger.LogError(e, "Error getting object at index {Index}", index); - } - } - return items; - } - case HousingTerritoryType.Workshop: - // nothing to do here really, no placeable items - return []; - default: - logger.LogWarning("Unknown territory type {TerritoryType}", territoryType); - return []; - } - } - - private unsafe List> ParseBgObjectsFromInstance(ILayoutInstance* instance) - { - var bgParts = ParseBgPartsFromInstance(instance); - var bgObjects = new List>(); - foreach (var bgPart in bgParts) - { - if (bgPart == null || bgPart.Value == null) - { - continue; - } - - if (bgPart.Value->GraphicsObject == null) - { - continue; - } - - var bgObject = (BgObject*)bgPart.Value->GraphicsObject; - if (bgObject == null) - { - continue; - } - - bgObjects.Add(bgObject); - } - - return bgObjects; - } - - private unsafe List> ParseBgPartsFromInstance(ILayoutInstance* instance) - { - var bgParts = new List>(); - if (instance->Id.Type == InstanceType.BgPart) - { - bgParts.Add((BgPartsLayoutInstance*)instance); - } - else if (instance->Id.Type == InstanceType.SharedGroup) - { - var sharedGroup = (SharedGroupLayoutInstance*)instance; - foreach (var instanceDataPtr in sharedGroup->Instances.Instances) - { - if (instanceDataPtr == null) - { - continue; - } - - var instanceData = instanceDataPtr.Value; - if (instanceData->Instance->Id.Type == InstanceType.BgPart) - { - bgParts.Add((BgPartsLayoutInstance*)instanceData->Instance); - } - else if (instanceData->Instance->Id.Type == InstanceType.SharedGroup) - { - var nestedBgParts = ParseBgPartsFromInstance(instanceData->Instance); - bgParts.AddRange(nestedBgParts); - } - } - } - return bgParts; - } - - public struct HousingItem - { - public HousingFurniture Furniture; - public Stain? Stain; - public Item? Item; - public unsafe GameObject* Object; - public List> BgParts; - } -} diff --git a/Meddle/Meddle.Plugin/Services/LayoutService.cs b/Meddle/Meddle.Plugin/Services/LayoutService.cs index 1ea5609..4d2473c 100644 --- a/Meddle/Meddle.Plugin/Services/LayoutService.cs +++ b/Meddle/Meddle.Plugin/Services/LayoutService.cs @@ -23,10 +23,10 @@ namespace Meddle.Plugin.Services; public class LayoutService : IService, IDisposable { private readonly Dictionary itemDict; + private readonly Dictionary stainDict; private readonly ILogger logger; private readonly IFramework framework; private readonly SigUtil sigUtil; - private readonly Dictionary stainDict; public LayoutService( SigUtil sigUtil, @@ -259,20 +259,16 @@ private unsafe ParsedInstanceSet[] ParseLayout(LayoutManager* activeLayout, Pars var furnitureMatch = context.HousingItems.FirstOrDefault(item => item.LayoutInstance == sharedGroupPtr); if (furnitureMatch is not null) { - // TODO: Kinda messy - var stain = stainDict.GetValueOrDefault(furnitureMatch.HousingFurniture.Stain); - var item = itemDict.GetValueOrDefault(furnitureMatch.HousingFurniture.Id); - var housing = new ParsedHousingInstance((nint)sharedGroup, new Transform(*sharedGroup->GetTransformImpl()), path, - furnitureMatch.GameObject->NameString, - furnitureMatch.GameObject->ObjectKind, - stain, - item, children); + furnitureMatch.GameObject->HousingObject.NameString, + furnitureMatch.GameObject->HousingObject.ObjectKind, + furnitureMatch.Stain, + furnitureMatch.Item, children); foreach (var child in housing.Flatten()) { if (child is ParsedBgPartsInstance parsedBgPartsInstance) { - parsedBgPartsInstance.StainColor = stain?.Color != null ? ImGui.ColorConvertU32ToFloat4(stain.Color) : null; + parsedBgPartsInstance.StainColor = furnitureMatch.Stain != null ? ImGui.ColorConvertU32ToFloat4(furnitureMatch.Stain.Color) : null; } } @@ -362,19 +358,41 @@ private unsafe Furniture[] ParseTerritoryFurniture(HousingTerritory* territory) var index = item.Index; if (item.Index == -1) continue; var objectPtr = objectManager->Objects[index]; - if (objectPtr == null || objectPtr.Value == null || objectPtr.Value->LayoutInstance == null) + if (objectPtr == null || objectPtr.Value == null || objectPtr.Value->SharedGroupLayoutInstance == null) { continue; } - var layoutInstance = objectPtr.Value->LayoutInstance; + if (objectPtr.Value->ObjectKind != ObjectKind.HousingEventObject) + { + logger.LogWarning("ObjectKind is not HousingEventObject"); + continue; + } + + var housingObjectPtr = (MeddleHousingObject*)objectPtr.Value; + var layoutInstance = housingObjectPtr->HousingObject.SharedGroupLayoutInstance; + var lookupItem = itemDict.GetValueOrDefault(item.Id); + Stain? stain = null; + if (housingObjectPtr->ColorId != 0) + { + stain = stainDict.GetValueOrDefault(housingObjectPtr->ColorId); + } + + var sLayoutInstance = (MeddleSharedGroupLayoutInstance*)layoutInstance; + if (sLayoutInstance->ColorId != 0) + { + stain = stainDict.GetValueOrDefault(sLayoutInstance->ColorId); + } + + + items.Add(new Furniture { - GameObject = objectPtr, + GameObject = housingObjectPtr, LayoutInstance = layoutInstance, HousingFurniture = item, - Stain = stainDict.GetValueOrDefault(item.Stain), - Item = itemDict.GetValueOrDefault(item.Id) + Stain = stain, + Item = lookupItem }); } @@ -393,10 +411,10 @@ public ParseContext(Furniture[] housingItems) public unsafe class Furniture { - public GameObject* GameObject; + public MeddleHousingObject* GameObject; public HousingFurniture HousingFurniture; public Item? Item; - public ILayoutInstance* LayoutInstance; + public SharedGroupLayoutInstance* LayoutInstance; public Stain? Stain; } diff --git a/Meddle/Meddle.Plugin/UI/DebugTab.cs b/Meddle/Meddle.Plugin/UI/DebugTab.cs index 6e4cfb3..4958927 100644 --- a/Meddle/Meddle.Plugin/UI/DebugTab.cs +++ b/Meddle/Meddle.Plugin/UI/DebugTab.cs @@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.Havok.Animation.Rig; using ImGuiNET; +using Lumina.Excel.GeneratedSheets2; using Meddle.Plugin.Models; using Meddle.Plugin.Services; using Meddle.Plugin.Services.UI; @@ -26,6 +27,7 @@ public class DebugTab : ITab private readonly LayoutService layoutService; private readonly ParseService parseService; private readonly PbdHooks pbdHooks; + private readonly IDataManager dataManager; private string boneSearch = ""; private ICharacter? selectedCharacter; @@ -42,7 +44,8 @@ private enum BoneMode public DebugTab(Configuration config, SigUtil sigUtil, CommonUi commonUi, IGameGui gui, IClientState clientState, LayoutService layoutService, - ParseService parseService, PbdHooks pbdHooks) + ParseService parseService, PbdHooks pbdHooks, + IDataManager dataManager) { this.config = config; this.sigUtil = sigUtil; @@ -52,6 +55,8 @@ public DebugTab(Configuration config, SigUtil sigUtil, CommonUi commonUi, this.layoutService = layoutService; this.parseService = parseService; this.pbdHooks = pbdHooks; + this.dataManager = dataManager; + stainDict = dataManager.GetExcelSheet()!.ToDictionary(row => row.RowId, row => row); } public void Dispose() @@ -67,7 +72,9 @@ public void Dispose() WriteIndented = true, IncludeFields = true }; - + + private readonly Dictionary stainDict; + public void Draw() { if (ImGui.CollapsingHeader("View Skeleton")) @@ -96,6 +103,11 @@ public void Draw() DrawCacheInfo(); } + if (ImGui.CollapsingHeader("Stain Info")) + { + DrawStainInfo(); + } + if (ImGui.CollapsingHeader("PBD Info")) { using var table = ImRaii.Table("##PbdInfo", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); @@ -125,6 +137,19 @@ public void Draw() } } + private void DrawStainInfo() + { + foreach (var (key, stain) in stainDict) + { + using var id = ImRaii.PushId(key.ToString()); + ImGui.Text($"Stain: {key}, {stain.Name}"); + var color = ImGui.ColorConvertU32ToFloat4(stain.Color); + ImGui.SameLine(); + ImGui.ColorButton("Color", color); + + } + } + private unsafe void DrawObjectTable() { using var indent = ImRaii.PushIndent(); diff --git a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs index 0dd83b5..277338f 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs @@ -8,12 +8,16 @@ using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Layer; using FFXIVClientStructs.Interop; using ImGuiNET; +using Meddle.Plugin.Models.Composer; using Meddle.Plugin.Models.Layout; using Meddle.Plugin.Models.Structs; using Meddle.Plugin.Utils; using Meddle.Utils; +using Meddle.Utils.Constants; using Meddle.Utils.Export; +using Meddle.Utils.Files; using Meddle.Utils.Helpers; +using Microsoft.Extensions.Logging; using Transform = FFXIVClientStructs.FFXIV.Client.LayoutEngine.Transform; namespace Meddle.Plugin.UI.Layout; @@ -111,6 +115,11 @@ private void DrawInstance(ParsedInstance instance, Stack stack, ImGui.Text("No Stain"); } } + + if (instance is ParsedBgPartsInstance bgPart) + { + DrawCache(bgPart); + } if (instance is ParsedCharacterInstance character) { @@ -188,6 +197,191 @@ private void DrawTerrain(ParsedTerrainInstance terrain) } } + private unsafe void DrawCache(ParsedInstance instance) + { + if (instance is not ParsedBgPartsInstance bgPartInstance) return; + var mdlPath = bgPartInstance.Path.FullPath; + + if (!mdlCache.TryGetValue(mdlPath, out var cachedMdl)) + { + try + { + var mdlFile = dataManager.GetFileOrReadFromDisk(mdlPath); + if (mdlFile == null) + { + mdlCache[mdlPath] = null; + } + else + { + mdlCache[mdlPath] = new MdlFile(mdlFile); + } + } + catch (Exception e) + { + log.LogError(e, "Failed to load mdl file"); + mdlCache[mdlPath] = null; + } + + cachedMdl = mdlCache[mdlPath]; + } + + if (cachedMdl == null) + { + ImGui.Text("No cached mdl data"); + return; + } + + using var treeNode = ImRaii.TreeNode("Mdl Data"); + if (!treeNode.Success) return; + + foreach (var mtrlName in cachedMdl.GetMaterialNames().Select(x => x.Value)) + { + using var materialNode = ImRaii.TreeNode(mtrlName); + if (!materialNode.Success) continue; + + if (!mtrlCache.TryGetValue(mtrlName, out var cachedMtrl)) + { + try + { + var mtrlFile = dataManager.GetFileOrReadFromDisk(mtrlName); + if (mtrlFile == null) + { + mtrlCache[mtrlName] = null; + } + else + { + mtrlCache[mtrlName] = new MtrlFile(mtrlFile); + } + } + catch (Exception e) + { + log.LogError(e, "Failed to load mtrl file"); + mtrlCache[mtrlName] = null; + } + + cachedMtrl = mtrlCache[mtrlName]; + } + + if (cachedMtrl == null) + { + ImGui.Text("No cached mtrl data"); + continue; + } + + ImGui.Text($"Shader: {cachedMtrl.GetShaderPackageName()}"); + if (!shpkCache.TryGetValue(cachedMtrl.GetShaderPackageName(), out var cachedShpk)) + { + try + { + var shpkFile = dataManager.GetFileOrReadFromDisk($"shader/sm5/shpk/{cachedMtrl.GetShaderPackageName()}"); + if (shpkFile == null) + { + shpkCache[cachedMtrl.GetShaderPackageName()] = null; + } + else + { + shpkCache[cachedMtrl.GetShaderPackageName()] = new ShpkFile(shpkFile); + } + } + catch (Exception e) + { + log.LogError(e, "Failed to load shpk file"); + shpkCache[cachedMtrl.GetShaderPackageName()] = null; + } + + cachedShpk = shpkCache[cachedMtrl.GetShaderPackageName()]; + } + + if (cachedShpk == null) + { + ImGui.Text("No cached shpk data"); + continue; + } + + var materialSet = new MaterialSet(cachedMtrl, mtrlName, cachedShpk, cachedMtrl.GetShaderPackageName(), null, null); + using (var shpkContentNode = ImRaii.TreeNode("Shpk Constants")) + { + if (shpkContentNode.Success) + { + foreach (var constant in materialSet.ShpkConstants) + { + ImGui.Text(Enum.IsDefined(constant.Key) + ? $"{constant.Key}: {string.Join(", ", constant.Value)}" + : $"{(uint)constant.Key:X8}: {string.Join(", ", constant.Value)}"); + } + } + } + + using (var mtrlContentNode = ImRaii.TreeNode("Mtrl Constants")) + { + if (mtrlContentNode.Success) + { + foreach (var constant in materialSet.MtrlConstants) + { + ImGui.Text(Enum.IsDefined(constant.Key) + ? $"{constant.Key}: {string.Join(", ", constant.Value)}" + : $"{(uint)constant.Key:X8}: {string.Join(", ", constant.Value)}"); + } + } + } + + TreeNode("Shader Keys", () => + { + foreach (var key in cachedMtrl.ShaderKeys) + { + ImGui.Text($"{(ShaderCategory)key.Category}: {key.Value:X8}"); + } + }); + + TreeNode("Color Sets", () => + { + foreach (var key in cachedMtrl.GetColorSetStrings()) + { + ImGui.Text(key.Value); + } + }); + + TreeNode("UVColor Sets", () => + { + foreach (var key in cachedMtrl.GetUvColorSetStrings()) + { + ImGui.Text(key.Value); + } + }); + + TreeNode("Textures", () => + { + foreach (var key in cachedMtrl.GetTexturePaths()) + { + ImGui.Text(key.Value); + } + }); + + TreeNode("Samplers", () => + { + foreach (var key in cachedMtrl.Samplers) + { + ImGui.Text($"[{key.TextureIndex}]{(TextureUsage)key.SamplerId}: {key.Flags}"); + } + }); + + TreeNode("ColorTable", () => + { + ImGui.Text($"Has Color Table: {cachedMtrl.HasTable}"); + ImGui.Text($"Has Dye Table: {cachedMtrl.HasDyeTable}"); + }); + } + } + + private void TreeNode(string name, Action action) + { + using var node = ImRaii.TreeNode(name); + if (node.Success) + { + action(); + } + } + private void DrawCharacter(ParsedCharacterInstance character) { ImGui.Text($"Kind: {character.Kind}"); diff --git a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs index 2a1a2cc..98f9a76 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs @@ -9,6 +9,7 @@ using Meddle.Plugin.Models.Composer; using Meddle.Plugin.Models.Layout; using Meddle.Plugin.Services; +using Meddle.Utils.Files; using Meddle.Utils.Files.SqPack; using Microsoft.Extensions.Logging; using SharpGLTF.Scenes; @@ -38,6 +39,9 @@ public partial class LayoutWindow : ITab private CancellationTokenSource cancelToken = new(); private ParsedInstance[] currentLayout = []; private readonly Dictionary selectedInstances = new(); + private readonly Dictionary mdlCache = new(); + private readonly Dictionary mtrlCache = new(); + private readonly Dictionary shpkCache = new(); private Vector3 currentPos; private string? lastError; diff --git a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs index 4cc8468..2221978 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs @@ -103,6 +103,9 @@ private void DrawTooltip(ParsedInstance instance, bool extras = true) : ImGui.ColorConvertU32ToFloat4(housingInstance.Stain.Color); if (color != null) { + var colorSq = color.Value * color.Value; + ImGui.ColorButton("Stain", colorSq); + ImGui.SameLine(); ImGui.ColorButton("Stain", color.Value); } else diff --git a/Meddle/Meddle.Utils/Constants/Material.cs b/Meddle/Meddle.Utils/Constants/Material.cs index 38a4507..ddff491 100644 --- a/Meddle/Meddle.Utils/Constants/Material.cs +++ b/Meddle/Meddle.Utils/Constants/Material.cs @@ -11,13 +11,6 @@ public enum ShaderCategory : uint CategoryFlowMapType = 0x40D1481E, // STANDARD, FLOW CategoryDiffuseAlpha = 0xA9A3EE25, // Alpha channel on diffuse texture is used CategoryBgVertexPaint = 0x4F4F0636, // Enable vertex paint - CategoryBgTextureMode = 0x36F72D5F, // Number of textures in BG shader -} - -public enum BgTextureMode : uint -{ - Map0 = 0x1E314009, - Map1 = 0x9807BAC4 } public enum BgVertexPaint : uint @@ -59,7 +52,7 @@ public enum TextureMode : uint Simple = 0x22A4AABF, // meh // BG.shpk - BG_UNK0 = 0x669A451B, + BG_UNK0 = 0x669A451B, // no map1 usage BG_UNK1 = 0x1DF2985C, BG_UNK2 = 0x941820BE, // Mix both textures? } diff --git a/Meddle/Meddle.Utils/Export/ShaderPackage.cs b/Meddle/Meddle.Utils/Export/ShaderPackage.cs index 5d54146..a0085c7 100644 --- a/Meddle/Meddle.Utils/Export/ShaderPackage.cs +++ b/Meddle/Meddle.Utils/Export/ShaderPackage.cs @@ -10,6 +10,7 @@ public unsafe class ShaderPackage public Dictionary TextureLookup { get; } public Dictionary MaterialConstants { get; } public Dictionary? ResourceKeys { get; } + public Dictionary DefaultKeyValues { get; } public ShaderPackage(ShpkFile file, string name) { @@ -17,6 +18,7 @@ public ShaderPackage(ShpkFile file, string name) var textureUsages = new Dictionary(); var resourceKeys = new Dictionary(); + var defaultKeyValues = new Dictionary(); var stringReader = new SpanBinaryReader(file.RawData[(int)file.FileHeader.StringsOffset..]); foreach (var sampler in file.Samplers) { @@ -72,7 +74,28 @@ public ShaderPackage(ShpkFile file, string name) Array.Copy(defaults, defaultCopy, defaults.Length); materialConstantDict[(MaterialConstant)materialParam.Id] = defaultCopy; } + + foreach (var materialKey in file.MaterialKeys) + { + defaultKeyValues[materialKey.Id] = materialKey.DefaultValue; + } + + foreach (var systemKey in file.SystemKeys) + { + defaultKeyValues[systemKey.Id] = systemKey.DefaultValue; + } + + foreach (var sceneKey in file.SceneKeys) + { + defaultKeyValues[sceneKey.Id] = sceneKey.DefaultValue; + } + + foreach (var subViewKey in file.SubViewKeys) + { + defaultKeyValues[subViewKey.Id] = subViewKey.DefaultValue; + } + DefaultKeyValues = defaultKeyValues; MaterialConstants = materialConstantDict; TextureLookup = textureUsages; ResourceKeys = resourceKeys; From b7ead012fcd028f07c04974c3bd73e4f5d49d5ee Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:36:51 +1100 Subject: [PATCH 2/6] Remove unused lookups on character shaders - fixes issues with legacy --- .../Models/Composer/Materials/CharacterMaterialBuilder.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs index 2b888b1..fc11517 100644 --- a/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs +++ b/Meddle/Meddle.Plugin/Models/Composer/Materials/CharacterMaterialBuilder.cs @@ -28,8 +28,6 @@ public CharacterMaterialBuilder(string name, MaterialSet set, DataProvider dataP private void ApplyComputed() { var textureMode = set.GetShaderKeyOrDefault(ShaderCategory.GetValuesTextureType); - var specularMode = set.GetShaderKeyOrDefault(ShaderCategory.CategorySpecularType); - var flowType = set.GetShaderKeyOrDefault(ShaderCategory.CategoryFlowMapType); if (!set.TryGetTextureStrict(dataProvider, TextureUsage.g_SamplerNormal, out var normalRes)) throw new InvalidOperationException("Missing normal texture"); @@ -43,12 +41,6 @@ private void ApplyComputed() Meddle.Utils.Constants.TextureMode.Compatibility => set.TryGetTexture(dataProvider, TextureUsage.g_SamplerDiffuse, out var tex) ? tex.ToTexture(normalRes.Size) : throw new InvalidOperationException("Missing diffuse texture"), _ => new SKTexture(normalRes.Width, normalRes.Height) }; - - var flowTexture = flowType switch - { - FlowType.Flow => set.TryGetTexture(dataProvider, TextureUsage.g_SamplerFlow, out var tex) ? tex.ToTexture(normalRes.Size) : throw new InvalidOperationException("Missing flow texture"), - _ => null - }; var normalTexture = normalRes.ToTexture(); var maskTexture = maskRes.ToTexture(normalRes.Size); From b0e9ccceda4c9c8bc4fd3d26a869bdcffb3ee5f3 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:31:17 +1100 Subject: [PATCH 3/6] Allow changing default export directory --- Meddle/Meddle.Plugin/Plugin.cs | 2 ++ Meddle/Meddle.Plugin/UI/AnimationTab.cs | 7 +++++-- .../Meddle.Plugin/UI/Layout/LayoutWindow.cs | 2 +- Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs | 14 +++++++------- Meddle/Meddle.Plugin/UI/OptionsTab.cs | 19 ++++++++++++++++++- Meddle/Meddle.Plugin/Utils/UIUtil.cs | 13 ++++++++----- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Meddle/Meddle.Plugin/Plugin.cs b/Meddle/Meddle.Plugin/Plugin.cs index 2be3059..bf9d60a 100644 --- a/Meddle/Meddle.Plugin/Plugin.cs +++ b/Meddle/Meddle.Plugin/Plugin.cs @@ -117,6 +117,8 @@ public void Migrate() public float WorldCutoffDistance { get; set; } = 100; public Vector4 WorldDotColor { get; set; } = new(1f, 1f, 1f, 0.5f); + public string ExportDirectory { get; set; } = Plugin.TempDirectory; + /// /// Used to hide names in the UI /// diff --git a/Meddle/Meddle.Plugin/UI/AnimationTab.cs b/Meddle/Meddle.Plugin/UI/AnimationTab.cs index d866633..974a56c 100644 --- a/Meddle/Meddle.Plugin/UI/AnimationTab.cs +++ b/Meddle/Meddle.Plugin/UI/AnimationTab.cs @@ -18,6 +18,7 @@ public class AnimationTab : ITab { private readonly AnimationExportService animationExportService; private readonly CommonUi commonUi; + private readonly Configuration config; private readonly List<(DateTime Time, AttachSet[])> frames = []; private readonly IFramework framework; private readonly ILogger logger; @@ -33,12 +34,14 @@ public class AnimationTab : ITab public AnimationTab( IFramework framework, ILogger logger, AnimationExportService animationExportService, - CommonUi commonUi) + CommonUi commonUi, + Configuration config) { this.framework = framework; this.logger = logger; this.animationExportService = animationExportService; this.commonUi = commonUi; + this.config = config; this.framework.Update += OnFrameworkUpdate; } @@ -84,7 +87,7 @@ public void Draw() { if (!result) return; animationExportService.ExportAnimation(frames, includePositionalData, path); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } diff --git a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs index 98f9a76..db4f317 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs @@ -348,7 +348,7 @@ private void InstanceExport(ParsedInstance[] instances) Process.Start("explorer.exe", path); }, cancelToken.Token); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } public void Dispose() diff --git a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs index 547bcff..ea07cbe 100644 --- a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs +++ b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs @@ -319,7 +319,7 @@ private void DrawDrawObject(DrawObject* drawObject, CustomizeData? customizeData gltf.SaveAsType(exportType, path, "models"); Process.Start("explorer.exe", path); }, exportCancelTokenSource.Token); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } } @@ -377,7 +377,7 @@ private void ExportAllModelsWithAttaches(CSCharacter* character, CustomizeParame gltf.SaveAsType(exportType, path, "character"); Process.Start("explorer.exe", path); }, exportCancelTokenSource.Token); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } private void ExportAllModels(CSCharacterBase* cBase, CustomizeParameter? customizeParams, CustomizeData? customizeData) @@ -431,7 +431,7 @@ private void ExportAllModels(CSCharacterBase* cBase, CustomizeParameter? customi } Process.Start("explorer.exe", path); }, exportCancelTokenSource.Token); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } private void DrawModel(Pointer cPtr, Pointer mPtr, CustomizeParameter? customizeParams, @@ -568,7 +568,7 @@ private void DrawModel(Pointer cPtr, Pointer mPtr, Custo } Process.Start("explorer.exe", path); }, exportCancelTokenSource.Token); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } } @@ -839,7 +839,7 @@ private void DrawMaterial( DataProvider.SaveTextureToDisk(texture, filePath); } Process.Start("explorer.exe", path); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } @@ -917,7 +917,7 @@ private void DrawTexture(CSMaterial* material, CSMaterial.TextureEntry textureEn { if (!result) return; DataProvider.SaveTextureToDisk(textureData, path); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } if (ImGui.MenuItem("Export as tex")) @@ -937,7 +937,7 @@ private void DrawTexture(CSMaterial* material, CSMaterial.TextureEntry textureEn } File.WriteAllBytes(path, data); - }, Plugin.TempDirectory); + }, config.ExportDirectory); } ImGui.EndPopup(); diff --git a/Meddle/Meddle.Plugin/UI/OptionsTab.cs b/Meddle/Meddle.Plugin/UI/OptionsTab.cs index 8d57607..857fcdb 100644 --- a/Meddle/Meddle.Plugin/UI/OptionsTab.cs +++ b/Meddle/Meddle.Plugin/UI/OptionsTab.cs @@ -25,9 +25,26 @@ public void Dispose() { } public void Draw() { + var exportDirectory = config.ExportDirectory; + if (ImGui.InputText("Default Export Directory", ref exportDirectory, 256)) + { + config.ExportDirectory = exportDirectory; + config.Save(); + } + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button(FontAwesomeIcon.Redo.ToIconString())) + { + config.ExportDirectory = Plugin.TempDirectory; + config.Save(); + } + } + if (ImGui.Button("Open output folder")) { - Process.Start("explorer.exe", Plugin.TempDirectory); + Process.Start("explorer.exe", config.ExportDirectory); } var openOnLoad = config.OpenOnLoad; diff --git a/Meddle/Meddle.Plugin/Utils/UIUtil.cs b/Meddle/Meddle.Plugin/Utils/UIUtil.cs index 02b3615..bc37e50 100644 --- a/Meddle/Meddle.Plugin/Utils/UIUtil.cs +++ b/Meddle/Meddle.Plugin/Utils/UIUtil.cs @@ -8,6 +8,7 @@ using Meddle.Plugin.Models; using Meddle.Plugin.Models.Skeletons; using Meddle.Utils.Files.Structs.Material; +using Microsoft.Extensions.Logging; using SharpGLTF.Transforms; using CustomizeData = Meddle.Utils.Export.CustomizeData; using CustomizeParameter = Meddle.Utils.Export.CustomizeParameter; @@ -107,16 +108,18 @@ public static void DrawColorTable(ReadOnlySpan tableRows, ColorDy private static void DrawRow(int i, ColorTableRow row, ColorDyeTable? dyeTable) { + using var rowId = ImRaii.PushId(i); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text($"{i}"); ImGui.TableSetColumnIndex(1); ImGui.ColorButton("##rowdiff", new Vector4(row.Diffuse, 1f), ImGuiColorEditFlags.NoAlpha); + if (dyeTable != null) { ImGui.SameLine(); var diff = dyeTable.Value.Rows[i].Diffuse; - ImGui.Checkbox("##rowdiff", ref diff); + ImGui.Checkbox("##rowdiffcheck", ref diff); } ImGui.TableSetColumnIndex(2); @@ -125,7 +128,7 @@ private static void DrawRow(int i, ColorTableRow row, ColorDyeTable? dyeTable) { ImGui.SameLine(); var spec = dyeTable.Value.Rows[i].Specular; - ImGui.Checkbox("##rowspec", ref spec); + ImGui.Checkbox("##rowspeccheck", ref spec); } ImGui.TableSetColumnIndex(3); @@ -134,7 +137,7 @@ private static void DrawRow(int i, ColorTableRow row, ColorDyeTable? dyeTable) { ImGui.SameLine(); var emm = dyeTable.Value.Rows[i].Emissive; - ImGui.Checkbox("##rowemm", ref emm); + ImGui.Checkbox("##rowemmcheck", ref emm); } ImGui.TableSetColumnIndex(4); @@ -145,7 +148,7 @@ private static void DrawRow(int i, ColorTableRow row, ColorDyeTable? dyeTable) if (dyeTable != null) { var specStrength = dyeTable.Value.Rows[i].SpecularStrength; - ImGui.Checkbox("##rowspecstr", ref specStrength); + ImGui.Checkbox("##rowspecstrcheck", ref specStrength); ImGui.SameLine(); } @@ -154,7 +157,7 @@ private static void DrawRow(int i, ColorTableRow row, ColorDyeTable? dyeTable) if (dyeTable != null) { var gloss = dyeTable.Value.Rows[i].Gloss; - ImGui.Checkbox("##rowgloss", ref gloss); + ImGui.Checkbox("##rowglosscheck", ref gloss); ImGui.SameLine(); } From 6fa17ef813379c43bbb69618747a4621e78e0bcc Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:57:26 +1100 Subject: [PATCH 4/6] Update hdr mapping --- Meddle/Meddle.Plugin/Models/Structs/LightInstance.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Meddle/Meddle.Plugin/Models/Structs/LightInstance.cs b/Meddle/Meddle.Plugin/Models/Structs/LightInstance.cs index 1c27069..a391275 100644 --- a/Meddle/Meddle.Plugin/Models/Structs/LightInstance.cs +++ b/Meddle/Meddle.Plugin/Models/Structs/LightInstance.cs @@ -96,8 +96,13 @@ public struct ColorHdr { public float HdrIntensity => Intensity * _vec3.Length(); - private static Vector3 HdrToRgb(Vector3 hdr) { - var len = hdr.Length(); - return hdr / (1.0f + len); + private static Vector3 HdrToRgb(Vector3 hdr) + { + return Vector3.Clamp(new Vector3( + Reinhard(hdr.X), + Reinhard(hdr.Y), + Reinhard(hdr.Z) + ), Vector3.Zero, Vector3.One); + float Reinhard(float x) => x / (1.0f + x); } } From d974a34d1afa33895f0ce34f4f6634900f3eb070 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:23:28 +1100 Subject: [PATCH 5/6] Pull default stain id from Sgb handle Thanks @chirpxiv for the sgb HousingSettings structure --- .../Models/Layout/ParsedInstance.cs | 4 +- .../Structs/SharedGroupLayoutExtension.cs | 21 ------- .../Structs/SharedGroupResourceHandle.cs | 61 +++++++++++++++++++ .../Meddle.Plugin/Services/LayoutService.cs | 40 ++++++------ Meddle/Meddle.Plugin/UI/Layout/Instance.cs | 3 + Meddle/Meddle.Plugin/UI/Layout/Overlay.cs | 16 ++--- Meddle/Meddle.Plugin/UI/Layout/Utils.cs | 2 +- Meddle/Meddle.Plugin/Utils/UIUtil.cs | 9 +++ 8 files changed, 107 insertions(+), 49 deletions(-) delete mode 100644 Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs create mode 100644 Meddle/Meddle.Plugin/Models/Structs/SharedGroupResourceHandle.cs diff --git a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs index 2d7bdfd..b5aea46 100644 --- a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs +++ b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs @@ -168,15 +168,17 @@ private bool SearchInternal(string query, HashSet visited) public class ParsedHousingInstance : ParsedSharedInstance, ISearchableInstance { public ParsedHousingInstance(nint id, Transform transform, string path, string name, - ObjectKind kind, Stain? stain, Item? item, IReadOnlyList children) : base(id, ParsedInstanceType.Housing, transform, path, children) + ObjectKind kind, Stain? stain, Stain defaultStain, Item? item, IReadOnlyList children) : base(id, ParsedInstanceType.Housing, transform, path, children) { Name = name; Kind = kind; Stain = stain; + DefaultStain = defaultStain; Item = item; } public Stain? Stain { get; } + public Stain DefaultStain { get; } public Item? Item { get; } public string Name { get; } public ObjectKind Kind { get; } diff --git a/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs b/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs deleted file mode 100644 index ba68987..0000000 --- a/Meddle/Meddle.Plugin/Models/Structs/SharedGroupLayoutExtension.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.InteropServices; -using FFXIVClientStructs.FFXIV.Client.Game.Object; -using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Group; - -namespace Meddle.Plugin.Models.Structs; - -[StructLayout(LayoutKind.Explicit, Size = 0x1D0)] -public struct MeddleHousingObject { - [FieldOffset(0x00)] public HousingObject HousingObject; - [FieldOffset(0x1B0)] public byte ColorId; -} - -[StructLayout(LayoutKind.Explicit, Size = 0x1E0)] -public unsafe struct MeddleSharedGroupLayoutInstance -{ - [FieldOffset(0x00)] public SharedGroupLayoutInstance SharedGroupLayoutInstance; - [FieldOffset(0x1C)] public byte ColorId; - [FieldOffset(0x1F)] public byte HasHousingObject; - [FieldOffset(0x20)] public byte ColorId0; - [FieldOffset(0x21)] public byte ColorId1; -} diff --git a/Meddle/Meddle.Plugin/Models/Structs/SharedGroupResourceHandle.cs b/Meddle/Meddle.Plugin/Models/Structs/SharedGroupResourceHandle.cs new file mode 100644 index 0000000..ee8ae90 --- /dev/null +++ b/Meddle/Meddle.Plugin/Models/Structs/SharedGroupResourceHandle.cs @@ -0,0 +1,61 @@ +using System.Runtime.InteropServices; +using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; +using FFXIVClientStructs.Interop; + +namespace Meddle.Plugin.Models.Structs; + +[StructLayout(LayoutKind.Explicit, Size = 0xB8)] +public unsafe struct SharedGroupResourceHandle +{ + [FieldOffset(0x00)] public ResourceHandle* ResourceHandle; + [FieldOffset(0xB0)] public SgbFile* SceneChunk; +} + +[StructLayout(LayoutKind.Explicit, Size = 0x1E0)] +public unsafe struct SgbFile +{ + [FieldOffset(0x10)] public SgbData* SgbData; + + public Pointer GetHousingSettings() + { + var chunk = SgbData->SceneChunkHeader; + var housingOffset = chunk.HousingOffset; + + if (housingOffset == 0) return null; + const int layerGroupOffset = 0x08; + const int sceneChunkHeader = 0x0C; + + var ptr = (byte*)SgbData + + sceneChunkHeader + + layerGroupOffset; + var housingSettings = (HousingSettings*)(ptr + housingOffset); + return housingSettings; + } +} + +[StructLayout(LayoutKind.Explicit, Size = 0x10)] +public unsafe struct SgbData +{ + [FieldOffset(0x00)] public fixed byte FileId[4]; + [FieldOffset(0x04)] public uint FileSize; + [FieldOffset(0x08)] public uint TotalChunkCount; + [FieldOffset(0x0C)] public SceneChunkHeader SceneChunkHeader; + // ... rest of the layer data +} + +[StructLayout(LayoutKind.Explicit, Size = 0x48)] +public unsafe struct SceneChunkHeader +{ + [FieldOffset(0x00)] public fixed byte ChunkId[4]; + [FieldOffset(0x04)] public uint ChunkSize; + [FieldOffset(0x08)] public uint LayerGroupOffset; + [FieldOffset(0x0C)] public uint LayerGroupCount; + + [FieldOffset(0x34)] public uint HousingOffset; +} + +[StructLayout(LayoutKind.Explicit, Size = 0x42)] +public unsafe struct HousingSettings +{ + [FieldOffset(0x00)] public ushort DefaultColorId; +} diff --git a/Meddle/Meddle.Plugin/Services/LayoutService.cs b/Meddle/Meddle.Plugin/Services/LayoutService.cs index 4d2473c..9e00d3f 100644 --- a/Meddle/Meddle.Plugin/Services/LayoutService.cs +++ b/Meddle/Meddle.Plugin/Services/LayoutService.cs @@ -260,15 +260,18 @@ private unsafe ParsedInstanceSet[] ParseLayout(LayoutManager* activeLayout, Pars if (furnitureMatch is not null) { var housing = new ParsedHousingInstance((nint)sharedGroup, new Transform(*sharedGroup->GetTransformImpl()), path, - furnitureMatch.GameObject->HousingObject.NameString, - furnitureMatch.GameObject->HousingObject.ObjectKind, + furnitureMatch.GameObject->NameString, + furnitureMatch.GameObject->ObjectKind, furnitureMatch.Stain, + furnitureMatch.DefaultStain, furnitureMatch.Item, children); foreach (var child in housing.Flatten()) { if (child is ParsedBgPartsInstance parsedBgPartsInstance) { - parsedBgPartsInstance.StainColor = furnitureMatch.Stain != null ? ImGui.ColorConvertU32ToFloat4(furnitureMatch.Stain.Color) : null; + parsedBgPartsInstance.StainColor = furnitureMatch.Stain != null ? + UiUtil.ConvertU32ColorToVector4(furnitureMatch.Stain.Color) : + UiUtil.ConvertU32ColorToVector4(furnitureMatch.DefaultStain.Color); } } @@ -369,30 +372,30 @@ private unsafe Furniture[] ParseTerritoryFurniture(HousingTerritory* territory) continue; } - var housingObjectPtr = (MeddleHousingObject*)objectPtr.Value; - var layoutInstance = housingObjectPtr->HousingObject.SharedGroupLayoutInstance; - var lookupItem = itemDict.GetValueOrDefault(item.Id); - Stain? stain = null; - if (housingObjectPtr->ColorId != 0) + var housingObjectPtr = (HousingObject*)objectPtr.Value; + var layoutInstance = housingObjectPtr->SharedGroupLayoutInstance; + var instanceHandle = (SharedGroupResourceHandle*)layoutInstance->ResourceHandle; + if (instanceHandle == null) { - stain = stainDict.GetValueOrDefault(housingObjectPtr->ColorId); + logger.LogWarning("InstanceHandle is null"); + continue; } - var sLayoutInstance = (MeddleSharedGroupLayoutInstance*)layoutInstance; - if (sLayoutInstance->ColorId != 0) + var housingSettings = instanceHandle->SceneChunk->GetHousingSettings(); + if (housingSettings == null) { - stain = stainDict.GetValueOrDefault(sLayoutInstance->ColorId); + logger.LogWarning("HousingSettings is null"); + continue; } - - - + items.Add(new Furniture { GameObject = housingObjectPtr, LayoutInstance = layoutInstance, HousingFurniture = item, - Stain = stain, - Item = lookupItem + Stain = item.Stain != 0 ? stainDict[item.Stain] : null, + DefaultStain = stainDict[housingSettings.Value->DefaultColorId], + Item = itemDict.GetValueOrDefault(item.Id) }); } @@ -411,11 +414,12 @@ public ParseContext(Furniture[] housingItems) public unsafe class Furniture { - public MeddleHousingObject* GameObject; + public HousingObject* GameObject; public HousingFurniture HousingFurniture; public Item? Item; public SharedGroupLayoutInstance* LayoutInstance; public Stain? Stain; + public Stain DefaultStain; } public void Dispose() diff --git a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs index 277338f..2d48a92 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs @@ -114,6 +114,9 @@ private void DrawInstance(ParsedInstance instance, Stack stack, { ImGui.Text("No Stain"); } + + Vector4 defaultColor = ImGui.ColorConvertU32ToFloat4(ho.DefaultStain.Color); + ImGui.ColorButton("Default Stain", defaultColor); } if (instance is ParsedBgPartsInstance bgPart) diff --git a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs index 2221978..4eda16d 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs @@ -98,19 +98,19 @@ private void DrawTooltip(ParsedInstance instance, bool extras = true) ImGui.Text($"Item Name: {housingInstance.Item.Name}"); } - Vector4? color = housingInstance.Stain == null - ? null - : ImGui.ColorConvertU32ToFloat4(housingInstance.Stain.Color); - if (color != null) + if (housingInstance.Stain != null) { - var colorSq = color.Value * color.Value; - ImGui.ColorButton("Stain", colorSq); + Vector4 color = UiUtil.ConvertU32ColorToVector4(housingInstance.Stain.Color); + ImGui.Text("Stain"); ImGui.SameLine(); - ImGui.ColorButton("Stain", color.Value); + ImGui.ColorButton("Stain", color); } else { - ImGui.Text("No Stain"); + Vector4 defaultColor = UiUtil.ConvertU32ColorToVector4(housingInstance.DefaultStain.Color); + ImGui.Text("Stain (Default)"); + ImGui.SameLine(); + ImGui.ColorButton("Stain", defaultColor); } } diff --git a/Meddle/Meddle.Plugin/UI/Layout/Utils.cs b/Meddle/Meddle.Plugin/UI/Layout/Utils.cs index 6f080d2..0dea788 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Utils.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Utils.cs @@ -5,7 +5,7 @@ namespace Meddle.Plugin.UI.Layout; public partial class LayoutWindow { - private unsafe bool WorldToScreenFallback(Vector3 worldPos, out Vector2 screenPos, out bool inView) + private unsafe bool WorldToScreenFallback(Vector3 worldPos, out Vector2 screenPos, out bool inView) { var currentCamera = sigUtil.GetCamera(); diff --git a/Meddle/Meddle.Plugin/Utils/UIUtil.cs b/Meddle/Meddle.Plugin/Utils/UIUtil.cs index bc37e50..f2d6691 100644 --- a/Meddle/Meddle.Plugin/Utils/UIUtil.cs +++ b/Meddle/Meddle.Plugin/Utils/UIUtil.cs @@ -510,4 +510,13 @@ private static void DrawBoneTree(ParsedPartialSkeleton partial, ParsedHkaPose po ImGui.TreePop(); } + + public static Vector4 ConvertU32ColorToVector4(uint color) + { + var r = (color & 0xFF) / 255f; + var g = ((color >> 8) & 0xFF) / 255f; + var b = ((color >> 16) & 0xFF) / 255f; + var a = ((color >> 24) & 0xFF) / 255f; + return new Vector4(r, g, b, a); + } } From 6bbc602014912e718f9072489b2a59bec95207d0 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:06:59 +1100 Subject: [PATCH 6/6] 7.1 updates --- Meddle/Meddle.Plugin/Meddle.Plugin.csproj | 2 +- .../Models/Layout/ParsedInstance.cs | 10 +- .../Meddle.Plugin/Models/StructExtensions.cs | 33 +-- Meddle/Meddle.Plugin/Plugin.cs | 5 + .../Meddle.Plugin/Services/LayoutService.cs | 23 +-- Meddle/Meddle.Plugin/Services/ParseService.cs | 14 +- Meddle/Meddle.Plugin/UI/DebugTab.cs | 190 ++++++++++++++---- Meddle/Meddle.Plugin/UI/Layout/Instance.cs | 4 +- Meddle/Meddle.Plugin/UI/Layout/Overlay.cs | 10 +- Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs | 49 +++-- .../Meddle.Plugin/UI/MaterialParameterTab.cs | 3 +- Meddle/Meddle.Plugin/packages.lock.json | 6 +- 12 files changed, 235 insertions(+), 114 deletions(-) diff --git a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj index f98f353..3aebf20 100644 --- a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj +++ b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj @@ -22,7 +22,7 @@ - + diff --git a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs index b5aea46..b4618f4 100644 --- a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs +++ b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs @@ -1,7 +1,7 @@ using System.Numerics; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.LayoutEngine; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Meddle.Plugin.Models.Skeletons; using Meddle.Plugin.Models.Structs; using Meddle.Plugin.Services; @@ -168,18 +168,20 @@ private bool SearchInternal(string query, HashSet visited) public class ParsedHousingInstance : ParsedSharedInstance, ISearchableInstance { public ParsedHousingInstance(nint id, Transform transform, string path, string name, - ObjectKind kind, Stain? stain, Stain defaultStain, Item? item, IReadOnlyList children) : base(id, ParsedInstanceType.Housing, transform, path, children) + ObjectKind kind, Stain? stain, Stain defaultStain, + /*Item? item,*/ + IReadOnlyList children) : base(id, ParsedInstanceType.Housing, transform, path, children) { Name = name; Kind = kind; Stain = stain; DefaultStain = defaultStain; - Item = item; + //Item = item; } public Stain? Stain { get; } public Stain DefaultStain { get; } - public Item? Item { get; } + // public Item? Item { get; } public string Name { get; } public ObjectKind Kind { get; } diff --git a/Meddle/Meddle.Plugin/Models/StructExtensions.cs b/Meddle/Meddle.Plugin/Models/StructExtensions.cs index 56a2306..9a7a5aa 100644 --- a/Meddle/Meddle.Plugin/Models/StructExtensions.cs +++ b/Meddle/Meddle.Plugin/Models/StructExtensions.cs @@ -12,9 +12,9 @@ namespace Meddle.Plugin.Models; public static class StructExtensions { public const int CharacterBaseAttachOffset = 0xD0; // CharacterBase + 0xD0 -> Attach - - public const int ModelEnabledAttributeIndexMaskOffset = 0xAC; // Model + 0xAC -> EnabledAttributeIndexMask - public const int ModelEnabledShapeKeyIndexMaskOffset = 0xC8; // Model + 0xC8 -> EnabledShapeKeyIndexMask + // + // public const int ModelEnabledAttributeIndexMaskOffset = 0xAC; // Model + 0xAC -> EnabledAttributeIndexMask + // public const int ModelEnabledShapeKeyIndexMaskOffset = 0xC8; // Model + 0xC8 -> EnabledShapeKeyIndexMask public const int PartialSkeletonFlagsOffset = 0x8; // PartialSkeleton + 0x8 -> Flags @@ -69,17 +69,17 @@ private static unsafe ParsedSkeleton GetParsedSkeleton(this Pointer sk return new ParsedSkeleton(skeleton.Value); } - public static unsafe (uint EnabledAttributeIndexMask, uint EnabledShapeKeyIndexMask) GetModelMasks( - this Pointer model) - { - if (model == null) throw new ArgumentNullException(nameof(model)); - if (model.Value == null) throw new ArgumentNullException(nameof(model)); - - var modelBase = model.Value; - var enabledAttributeIndexMask = *(uint*)((nint)modelBase + ModelEnabledAttributeIndexMaskOffset); - var enabledShapeKeyIndexMask = *(uint*)((nint)modelBase + ModelEnabledShapeKeyIndexMaskOffset); - return (enabledAttributeIndexMask, enabledShapeKeyIndexMask); - } + // public static unsafe (uint EnabledAttributeIndexMask, uint EnabledShapeKeyIndexMask) GetModelMasks( + // this Pointer model) + // { + // if (model == null) throw new ArgumentNullException(nameof(model)); + // if (model.Value == null) throw new ArgumentNullException(nameof(model)); + // + // var modelBase = model.Value; + // var enabledAttributeIndexMask = *(uint*)((nint)modelBase + ModelEnabledAttributeIndexMaskOffset); + // var enabledShapeKeyIndexMask = *(uint*)((nint)modelBase + ModelEnabledShapeKeyIndexMaskOffset); + // return (enabledAttributeIndexMask, enabledShapeKeyIndexMask); + // } public static unsafe Meddle.Utils.Export.Model.ShapeAttributeGroup ParseModelShapeAttributes( Pointer modelPointer) @@ -87,7 +87,7 @@ public static unsafe Meddle.Utils.Export.Model.ShapeAttributeGroup ParseModelSha if (modelPointer == null) throw new ArgumentNullException(nameof(modelPointer)); if (modelPointer.Value == null) throw new ArgumentNullException(nameof(modelPointer)); var model = modelPointer.Value; - var (enabledAttributeIndexMask, enabledShapeKeyIndexMask) = modelPointer.GetModelMasks(); + // var (enabledAttributeIndexMask, enabledShapeKeyIndexMask) = modelPointer.GetModelMasks(); var shapes = new List<(string, short)>(); foreach (var shape in model->ModelResourceHandle->Shapes) { @@ -101,7 +101,8 @@ public static unsafe Meddle.Utils.Export.Model.ShapeAttributeGroup ParseModelSha } var shapeAttributeGroup = new Meddle.Utils.Export.Model.ShapeAttributeGroup( - enabledShapeKeyIndexMask, enabledAttributeIndexMask, shapes.ToArray(), attributes.ToArray()); + model->EnabledShapeKeyIndexMask, model->EnabledAttributeIndexMask, + shapes.ToArray(), attributes.ToArray()); return shapeAttributeGroup; } diff --git a/Meddle/Meddle.Plugin/Plugin.cs b/Meddle/Meddle.Plugin/Plugin.cs index bf9d60a..e777710 100644 --- a/Meddle/Meddle.Plugin/Plugin.cs +++ b/Meddle/Meddle.Plugin/Plugin.cs @@ -3,6 +3,7 @@ using Dalamud.Configuration; using Dalamud.IoC; using Dalamud.Plugin; +//using InteropGenerator.Runtime; using Meddle.Plugin.Models; using Meddle.Plugin.Services; using Meddle.Plugin.UI.Layout; @@ -26,6 +27,10 @@ public Plugin(IDalamudPluginInterface pluginInterface) { try { + // FFXIVClientStructs.Interop.Generated.Addresses.Register(); + // Resolver.GetInstance.Setup(); + // Resolver.GetInstance.Resolve(); + var config = pluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); pluginInterface.Inject(config); config.Migrate(); diff --git a/Meddle/Meddle.Plugin/Services/LayoutService.cs b/Meddle/Meddle.Plugin/Services/LayoutService.cs index 9e00d3f..68efdd3 100644 --- a/Meddle/Meddle.Plugin/Services/LayoutService.cs +++ b/Meddle/Meddle.Plugin/Services/LayoutService.cs @@ -1,6 +1,4 @@ -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Numerics; +using System.Numerics; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Object; @@ -9,8 +7,7 @@ using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Layer; using FFXIVClientStructs.FFXIV.Client.LayoutEngine.Terrain; using FFXIVClientStructs.Interop; -using ImGuiNET; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Meddle.Plugin.Models.Layout; using Meddle.Plugin.Models.Structs; using Meddle.Plugin.Utils; @@ -22,7 +19,7 @@ namespace Meddle.Plugin.Services; public class LayoutService : IService, IDisposable { - private readonly Dictionary itemDict; + // private readonly Dictionary itemDict; private readonly Dictionary stainDict; private readonly ILogger logger; private readonly IFramework framework; @@ -38,9 +35,9 @@ public LayoutService( this.logger = logger; this.framework = framework; stainDict = dataManager.GetExcelSheet()!.ToDictionary(row => row.RowId, row => row); - itemDict = dataManager.GetExcelSheet()! - .Where(item => item.AdditionalData != 0 && item.ItemSearchCategory.Row is 65 or 66) - .ToDictionary(row => row.AdditionalData, row => row); + // itemDict = dataManager.GetExcelSheet()! + // .Where(item => item.AdditionalData.RowId != 0 && item.ItemSearchCategory.Value.RowId is 65 or 66) + // .ToDictionary(row => row.AdditionalData.RowId, row => row); this.framework.Update += Update; } @@ -264,13 +261,13 @@ private unsafe ParsedInstanceSet[] ParseLayout(LayoutManager* activeLayout, Pars furnitureMatch.GameObject->ObjectKind, furnitureMatch.Stain, furnitureMatch.DefaultStain, - furnitureMatch.Item, children); + /*furnitureMatch.Item,*/ children); foreach (var child in housing.Flatten()) { if (child is ParsedBgPartsInstance parsedBgPartsInstance) { parsedBgPartsInstance.StainColor = furnitureMatch.Stain != null ? - UiUtil.ConvertU32ColorToVector4(furnitureMatch.Stain.Color) : + UiUtil.ConvertU32ColorToVector4(furnitureMatch.Stain.Value.Color) : UiUtil.ConvertU32ColorToVector4(furnitureMatch.DefaultStain.Color); } } @@ -395,7 +392,7 @@ private unsafe Furniture[] ParseTerritoryFurniture(HousingTerritory* territory) HousingFurniture = item, Stain = item.Stain != 0 ? stainDict[item.Stain] : null, DefaultStain = stainDict[housingSettings.Value->DefaultColorId], - Item = itemDict.GetValueOrDefault(item.Id) + //Item = itemDict.GetValueOrDefault(item.Id) }); } @@ -416,7 +413,7 @@ public unsafe class Furniture { public HousingObject* GameObject; public HousingFurniture HousingFurniture; - public Item? Item; + //public Item? Item; public SharedGroupLayoutInstance* LayoutInstance; public Stain? Stain; public Stain DefaultStain; diff --git a/Meddle/Meddle.Plugin/Services/ParseService.cs b/Meddle/Meddle.Plugin/Services/ParseService.cs index 5b244b2..9f97d2a 100644 --- a/Meddle/Meddle.Plugin/Services/ParseService.cs +++ b/Meddle/Meddle.Plugin/Services/ParseService.cs @@ -86,11 +86,11 @@ public unsafe IColorTableSet ParseColorTableTexture(Texture* colorTableTexture) $"Color table is not R16G16B16A16F ({(TexFile.TextureFormat)colorTableTexture->TextureFormat})"); } - if (colorTableTexture->Width == 4 && colorTableTexture->Height == 16) + if (colorTableTexture->ActualWidth == 4 && colorTableTexture->ActualHeight == 16) { // legacy table - var stridedData = ImageUtils.AdjustStride(stride, (int)colorTableTexture->Width * 8, - (int)colorTableTexture->Height, colorTableRes.Data); + var stridedData = ImageUtils.AdjustStride(stride, (int)colorTableTexture->ActualWidth * 8, + (int)colorTableTexture->ActualHeight, colorTableRes.Data); var reader = new SpanBinaryReader(stridedData); return new LegacyColorTableSet { @@ -98,11 +98,11 @@ public unsafe IColorTableSet ParseColorTableTexture(Texture* colorTableTexture) }; } - if (colorTableTexture->Width == 8 && colorTableTexture->Height == 32) + if (colorTableTexture->ActualWidth == 8 && colorTableTexture->ActualHeight == 32) { // new table - var stridedData = ImageUtils.AdjustStride(stride, (int)colorTableTexture->Width * 8, - (int)colorTableTexture->Height, colorTableRes.Data); + var stridedData = ImageUtils.AdjustStride(stride, (int)colorTableTexture->ActualWidth * 8, + (int)colorTableTexture->ActualHeight, colorTableRes.Data); var reader = new SpanBinaryReader(stridedData); return new ColorTableSet { @@ -111,6 +111,6 @@ public unsafe IColorTableSet ParseColorTableTexture(Texture* colorTableTexture) } throw new ArgumentException( - $"Color table is not 4x16 or 8x32 ({colorTableTexture->Width}x{colorTableTexture->Height})"); + $"Color table is not 4x16 or 8x32 ({colorTableTexture->ActualWidth}x{colorTableTexture->ActualHeight})"); } } diff --git a/Meddle/Meddle.Plugin/UI/DebugTab.cs b/Meddle/Meddle.Plugin/UI/DebugTab.cs index 4958927..981f0e3 100644 --- a/Meddle/Meddle.Plugin/UI/DebugTab.cs +++ b/Meddle/Meddle.Plugin/UI/DebugTab.cs @@ -8,7 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.Havok.Animation.Rig; using ImGuiNET; -using Lumina.Excel.GeneratedSheets2; +using Lumina.Excel.Sheets; using Meddle.Plugin.Models; using Meddle.Plugin.Services; using Meddle.Plugin.Services.UI; @@ -164,7 +164,7 @@ private unsafe void DrawObjectTable() var obj = objPtr.Value; var kind = obj->GetObjectKind(); - if (ImGui.CollapsingHeader($"[{i}|{(nint)obj:X8}] {kind} - {obj->NameString}")) + if (ImGui.CollapsingHeader($"[{i}|{(nint)obj:X8}] {kind} - {obj->NameString} {(obj->DrawObject != null ? $"Visible: {obj->DrawObject->IsVisible}" : "")}")) { UiUtil.Text($"Address: {(nint)obj:X8}", $"{(nint)obj:X8}"); ImGui.Text($"Name: {obj->NameString}"); @@ -173,7 +173,7 @@ private unsafe void DrawObjectTable() if (drawObject != null) { var drawObjectType = drawObject->GetObjectType(); - ImGui.Text($"DrawObject Address: {(nint)drawObject:X8}"); + UiUtil.Text($"DrawObject Address: {(nint)drawObject:X8}", $"{(nint)drawObject:X8}"); ImGui.Text($"DrawObject Type: {drawObjectType}"); ImGui.Text($"DrawObject Position: {drawObject->Position}"); ImGui.Text($"DrawObject Rotation: {drawObject->Rotation}"); @@ -380,65 +380,173 @@ private unsafe void DrawCharacterBase(CharacterBase* cBase, string name) { ImGui.Text($"Visible: {cBase->IsVisible}"); ImGui.Text($"ModelType: {cBase->GetModelType()}"); + + if (cBase->GetModelType() == CharacterBase.ModelType.Human) + { + var human = (Human*)cBase; + ImGui.Text($"RaceSexId: {human->RaceSexId}"); + ImGui.Text($"HairId: {human->HairId}"); + ImGui.Text($"FaceId: {human->FaceId}"); + ImGui.Text($"TailEarId: {human->TailEarId}"); + ImGui.Text($"FurId: {human->FurId}"); + + ImGui.Text($"Highlights: {human->Customize.Highlights}"); + ImGui.Text($"Lipstick: {human->Customize.Lipstick}"); + } + var skeleton = cBase->Skeleton; if (skeleton == null) { ImGui.Text($"{name} Skeleton is null"); - return; } - - ImGui.Text($"Skeleton: {(nint)cBase->Skeleton:X8}"); - ImGui.Text($"Partial Skeleton Count: {cBase->Skeleton->PartialSkeletonCount}"); - if (ImGui.CollapsingHeader("Draw Bones")) + else { - // boneMode - ImGui.Text("Bone Mode"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200); - using (var combo = ImRaii.Combo("##BoneMode", boneMode.ToString())) + ImGui.Text($"Skeleton: {(nint)cBase->Skeleton:X8}"); + ImGui.Text($"Partial Skeleton Count: {cBase->Skeleton->PartialSkeletonCount}"); + using var skeletonIndent = ImRaii.PushIndent(); + if (ImGui.CollapsingHeader("Draw Bones")) { - if (combo.Success) + // boneMode + ImGui.Text("Bone Mode"); + ImGui.SameLine(); + ImGui.SetNextItemWidth(200); + using (var combo = ImRaii.Combo("##BoneMode", boneMode.ToString())) { - foreach (BoneMode mode in Enum.GetValues(typeof(BoneMode))) + if (combo.Success) { - if (ImGui.Selectable(mode.ToString(), mode == boneMode)) + foreach (BoneMode mode in Enum.GetValues(typeof(BoneMode))) { - boneMode = mode; + if (ImGui.Selectable(mode.ToString(), mode == boneMode)) + { + boneMode = mode; + } } } } + + ImGui.Text("Bone Search"); + ImGui.SameLine(); + ImGui.SetNextItemWidth(200); + ImGui.InputText("##BoneSearch", ref boneSearch, 100); + + // imgui select partial skeleton by index + for (int i = 0; i < skeleton->PartialSkeletonCount; i++) + { + using var pskeletonIndent = ImRaii.PushIndent(); + var partialSkeleton = skeleton->PartialSkeletons[i]; + var handle = partialSkeleton.SkeletonResourceHandle; + if (handle == null) continue; + var path = handle->FileName.ParseString(); + if (ImGui.CollapsingHeader($"Partial Skeleton {i}: {path}")) + { + var boneCount = StructExtensions.GetBoneCount(&partialSkeleton); + ImGui.Text($"Partial Skeleton Bone Count: {boneCount}"); + using var boneTable = ImRaii.Table($"##BoneTable{i}", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); + ImGui.TableSetupColumn("Bone", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Parent", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Translation", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Rotation", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Scale", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableHeadersRow(); + DrawBoneTransformsOnScreen(partialSkeleton, boneMode); + } + } } - - ImGui.Text("Bone Search"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200); - ImGui.InputText("##BoneSearch", ref boneSearch, 100); - - // imgui select partial skeleton by index - for (int i = 0; i < skeleton->PartialSkeletonCount; i++) + } + + + if (ImGui.CollapsingHeader("Draw Models")) + { + using var skeletonIndent = ImRaii.PushIndent(); + DrawModels(cBase); + } + } + } + + private unsafe void DrawModels(CharacterBase* cBase) + { + var models = cBase->ModelsSpan; + for (var i = 0; i < models.Length; i++) + { + var modelPtr = models[i]; + if (ImGui.CollapsingHeader($"Model {i} - {((nint)modelPtr.Value):X8}") && modelPtr.Value != null) + { + try { - var partialSkeleton = skeleton->PartialSkeletons[i]; - var handle = partialSkeleton.SkeletonResourceHandle; - if (handle == null) continue; - var path = handle->FileName.ParseString(); - if (ImGui.CollapsingHeader($"Partial Skeleton {i}: {path}")) + using var modelIndent = ImRaii.PushIndent(); + using var modelId = ImRaii.PushId(i); + var model = modelPtr.Value; + UiUtil.Text($"Model: {(nint)model:X8}", $"{(nint)model:X8}"); + ImGui.Text($"Name: {model->ModelResourceHandle->FileName.ToString()}"); + ImGui.Text($"Material Count: {model->MaterialsSpan.Length}"); + ImGui.Text($"Bone Count: {model->BoneCount}"); + + if (ImGui.CollapsingHeader("Materials")) { - var boneCount = StructExtensions.GetBoneCount(&partialSkeleton); - ImGui.Text($"Partial Skeleton Bone Count: {boneCount}"); - using var boneTable = ImRaii.Table($"##BoneTable{i}", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); - ImGui.TableSetupColumn("Bone", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Parent", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Translation", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Rotation", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Scale", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableHeadersRow(); - DrawBoneTransformsOnScreen(partialSkeleton, boneMode); + try + { + using var materialIndent = ImRaii.PushIndent(); + for (var materialIdx = 0; materialIdx < model->MaterialsSpan.Length; materialIdx++) + { + var material = model->MaterialsSpan[materialIdx]; + if (ImGui.CollapsingHeader($"Material {materialIdx} - {(nint)material.Value:X8}") && material.Value != null) + { + try + { + using var materialId = ImRaii.PushId(materialIdx); + UiUtil.Text($"Material: {(nint)material.Value:X8}", $"{(nint)material.Value:X8}"); + UiUtil.Text($"Material Resource: {(nint)material.Value->MaterialResourceHandle:X8}", $"{(nint)material.Value->MaterialResourceHandle:X8}"); + ImGui.Text($"Texture Count: {material.Value->TextureCount}"); + + for (var j = 0; j < material.Value->TextureCount; j++) + { + try + { + using var textureIndent = ImRaii.PushIndent(); + var texture = material.Value->Textures[j]; + if (ImGui.CollapsingHeader($"Texture {j} - {(nint)texture.Texture:X8}") && texture.Texture != null) + { + try + { + using var textureId = ImRaii.PushId(j); + UiUtil.Text($"Texture: {(nint)texture.Texture:X8}", $"{(nint)texture.Texture:X8}"); + ImGui.Text($"Id: {texture.Id}"); + ImGui.Text($"SamplerFlags: {texture.SamplerFlags}"); + ImGui.Text($"Texture: {texture.Texture->FileName.ToString()}"); + } + catch (Exception e) + { + ImGui.Text($"Error: {e.Message}"); + } + } + } + catch (Exception e) + { + ImGui.Text($"Error: {e.Message}"); + } + } + } + catch (Exception e) + { + ImGui.Text($"Error: {e.Message}"); + } + } + } + } + catch (Exception e) + { + ImGui.Text($"Error: {e.Message}"); + } } } + catch (Exception e) + { + ImGui.Text($"Error: {e.Message}"); + } } } } - + private unsafe void DrawBoneTransformsOnScreen(PartialSkeleton partialSkeleton, BoneMode boneMode) { var rootPos = partialSkeleton.Skeleton->Transform; diff --git a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs index 2d48a92..025397d 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Instance.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Instance.cs @@ -104,8 +104,8 @@ private void DrawInstance(ParsedInstance instance, Stack stack, { ImGui.Text($"Kind: {ho.Kind}"); ImGui.Text($"Object Name: {ho.Name}"); - ImGui.Text($"Item Name: {ho.Item?.Name}"); - Vector4? color = ho.Stain == null ? null : ImGui.ColorConvertU32ToFloat4(ho.Stain.Color); + //ImGui.Text($"Item Name: {ho.Item?.Name}"); + Vector4? color = ho.Stain == null ? null : ImGui.ColorConvertU32ToFloat4(ho.Stain.Value.Color); if (color != null) { ImGui.ColorButton("Stain", color.Value); diff --git a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs index 4eda16d..dce241e 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/Overlay.cs @@ -93,14 +93,14 @@ private void DrawTooltip(ParsedInstance instance, bool extras = true) { ImGui.Text($"Housing: {housingInstance.Name}"); ImGui.Text($"Kind: {housingInstance.Kind}"); - if (housingInstance.Item != null) - { - ImGui.Text($"Item Name: {housingInstance.Item.Name}"); - } + // if (housingInstance.Item != null) + // { + // ImGui.Text($"Item Name: {housingInstance.Item.Value.Name}"); + // } if (housingInstance.Stain != null) { - Vector4 color = UiUtil.ConvertU32ColorToVector4(housingInstance.Stain.Color); + Vector4 color = UiUtil.ConvertU32ColorToVector4(housingInstance.Stain.Value.Color); ImGui.Text("Stain"); ImGui.SameLine(); ImGui.ColorButton("Stain", color); diff --git a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs index ea07cbe..934dab8 100644 --- a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs +++ b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs @@ -219,35 +219,42 @@ private void DrawCharacter(CSCharacter* character, string name, int depth = 0) } DrawDrawObject(drawObject, customizeData, customizeParams, genderRace); - - if (character->Mount.MountObject != null) - { - ImGui.Separator(); - DrawCharacter(character->Mount.MountObject, "Mount", depth + 1); - } - if (character->CompanionData.CompanionObject != null) + try { - ImGui.Separator(); - DrawCharacter(&character->CompanionData.CompanionObject->Character, "Companion", depth + 1); - } + if (character->Mount.MountObject != null) + { + ImGui.Separator(); + DrawCharacter(character->Mount.MountObject, "Mount", depth + 1); + } - if (character->OrnamentData.OrnamentObject != null) - { - ImGui.Separator(); - DrawCharacter(&character->OrnamentData.OrnamentObject->Character, "Ornament", depth + 1); - } + if (character->CompanionData.CompanionObject != null) + { + ImGui.Separator(); + DrawCharacter(&character->CompanionData.CompanionObject->Character, "Companion", depth + 1); + } - for (var weaponIdx = 0; weaponIdx < character->DrawData.WeaponData.Length; weaponIdx++) - { - var weaponData = character->DrawData.WeaponData[weaponIdx]; - if (weaponData.DrawObject != null) + if (character->OrnamentData.OrnamentObject != null) { ImGui.Separator(); - ImGui.Text($"Weapon {weaponIdx}"); - DrawDrawObject(weaponData.DrawObject, null, null, GenderRace.Unknown); + DrawCharacter(&character->OrnamentData.OrnamentObject->Character, "Ornament", depth + 1); + } + + for (var weaponIdx = 0; weaponIdx < character->DrawData.WeaponData.Length; weaponIdx++) + { + var weaponData = character->DrawData.WeaponData[weaponIdx]; + if (weaponData.DrawObject != null) + { + ImGui.Separator(); + ImGui.Text($"Weapon {weaponIdx}"); + DrawDrawObject(weaponData.DrawObject, null, null, GenderRace.Unknown); + } } } + catch (Exception ex) + { + ImGui.Text($"Error: {ex.Message}"); + } } private void DrawDrawObject(DrawObject* drawObject, CustomizeData? customizeData, CustomizeParameter? customizeParams, GenderRace genderRace) diff --git a/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs b/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs index bb9a3fe..7e62518 100644 --- a/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs +++ b/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Utility.Raii; +using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.Interop; using ImGuiNET; @@ -91,7 +92,7 @@ public unsafe void DrawCustomizeParams(CharacterBase* cbase) customizeParameters = null; lastHuman = human; } - + var parameter = human->CustomizeParameterCBuffer->TryGetBuffer(); if (customizeParameters == null) { diff --git a/Meddle/Meddle.Plugin/packages.lock.json b/Meddle/Meddle.Plugin/packages.lock.json index 9a8bbd3..a9b0e04 100644 --- a/Meddle/Meddle.Plugin/packages.lock.json +++ b/Meddle/Meddle.Plugin/packages.lock.json @@ -4,9 +4,9 @@ "net8.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[2.1.13, )", - "resolved": "2.1.13", - "contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ==" + "requested": "[11.0.0, )", + "resolved": "11.0.0", + "contentHash": "bjT7XUlhIJSmsE/O76b7weUX+evvGQctbQB8aKXt94o+oPWxHpCepxAGMs7Thow3AzCyqWs7cOpp9/2wcgRRQA==" }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct",