Compare commits

..

No commits in common. "34f067f13d68eb310fce111e39d94a2bb131d5a9" and "cb275f57bfb41c7d7fd7ba43c736028bfcdec52c" have entirely different histories.

19 changed files with 827 additions and 857 deletions

2
Luna

@ -1 +1 @@
Subproject commit 78216203f4570a6194fce9422204d8abb536c828 Subproject commit 7214f079cb9b8eeea6fa1a9fe1c6ca8118049969

@ -1 +1 @@
Subproject commit 9af1e5fce4c13ef98842807d4f593dec8ae80c87 Subproject commit f354444776591ae423e2d8374aae346308d81424

View file

@ -1,5 +1,5 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Luna; using OtterGui.Filesystem;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers.Bases; using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;

View file

@ -1,4 +1,4 @@
using Luna; using OtterGui.Filesystem;
namespace Penumbra.Collections; namespace Penumbra.Collections;

View file

@ -1,15 +1,15 @@
using Luna; using OtterGui.Log;
namespace Penumbra.Import.Models; namespace Penumbra.Import.Models;
public record IoNotifier(Logger Log) public record class IoNotifier
{ {
private readonly List<string> _messages = []; private readonly List<string> _messages = [];
private string _context = ""; private string _context = "";
/// <summary> Create a new notifier with the specified context appended to any other context already present. </summary> /// <summary> Create a new notifier with the specified context appended to any other context already present. </summary>
public IoNotifier WithContext(string context) public IoNotifier WithContext(string context)
=> this with { _context = $"{_context}{context}: " }; => this with { _context = $"{_context}{context}: "};
/// <summary> Send a warning with any current context to notification channels. </summary> /// <summary> Send a warning with any current context to notification channels. </summary>
public void Warning(string content) public void Warning(string content)
@ -34,7 +34,7 @@ public record IoNotifier(Logger Log)
private void SendMessage(string message, Logger.LogLevel type) private void SendMessage(string message, Logger.LogLevel type)
{ {
var fullText = $"{_context}{message}"; var fullText = $"{_context}{message}";
Log.Message(type, fullText); Penumbra.Log.Message(type, fullText);
_messages.Add(fullText); _messages.Add(fullText);
} }
} }

View file

@ -1,6 +1,5 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Data.Parsing; using Lumina.Data.Parsing;
using Luna;
using OtterGui.Tasks; using OtterGui.Tasks;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData; using Penumbra.GameData;
@ -23,15 +22,9 @@ namespace Penumbra.Import.Models;
using Schema2 = SharpGLTF.Schema2; using Schema2 = SharpGLTF.Schema2;
using LuminaMaterial = Lumina.Models.Materials.Material; using LuminaMaterial = Lumina.Models.Materials.Material;
public sealed class ModelManager( public sealed class ModelManager(IFramework framework, MetaFileManager metaFileManager, ActiveCollections collections, GamePathParser parser)
Logger log, : SingleTaskQueue, IDisposable, Luna.IService
IFramework framework,
MetaFileManager metaFileManager,
ActiveCollections collections,
GamePathParser parser)
: SingleTaskQueue, IDisposable, IService
{ {
public readonly Logger Log = log;
private readonly IFramework _framework = framework; private readonly IFramework _framework = framework;
private readonly ConcurrentDictionary<IAction, (Task, CancellationTokenSource)> _tasks = new(); private readonly ConcurrentDictionary<IAction, (Task, CancellationTokenSource)> _tasks = new();
@ -55,7 +48,7 @@ public sealed class ModelManager(
public Task<(MdlFile?, IoNotifier)> ImportGltf(string inputPath) public Task<(MdlFile?, IoNotifier)> ImportGltf(string inputPath)
=> EnqueueWithResult( => EnqueueWithResult(
new ImportGltfAction(this, inputPath), new ImportGltfAction(inputPath),
action => (action.Out, action.Notifier) action => (action.Out, action.Notifier)
); );
@ -95,7 +88,8 @@ public sealed class ModelManager(
{ {
// Try to find an EST entry from the manipulations provided. // Try to find an EST entry from the manipulations provided.
var modEst = estManipulations var modEst = estManipulations
.FirstOrNull(est => est.Key.GenderRace == info.GenderRace .FirstOrNull(
est => est.Key.GenderRace == info.GenderRace
&& est.Key.Slot == type && est.Key.Slot == type
&& est.Key.SetId == info.PrimaryId && est.Key.SetId == info.PrimaryId
); );
@ -196,7 +190,7 @@ public sealed class ModelManager(
string outputPath) string outputPath)
: IAction : IAction
{ {
public readonly IoNotifier Notifier = new(manager.Log); public readonly IoNotifier Notifier = new();
public void Execute(CancellationToken cancel) public void Execute(CancellationToken cancel)
{ {
@ -298,7 +292,7 @@ public sealed class ModelManager(
public bool Equals(IAction? other) public bool Equals(IAction? other)
{ {
if (other is not ExportToGltfAction) if (other is not ExportToGltfAction rhs)
return false; return false;
// TODO: compare configuration and such // TODO: compare configuration and such
@ -306,10 +300,10 @@ public sealed class ModelManager(
} }
} }
private class ImportGltfAction(ModelManager manager, string inputPath) : IAction private partial class ImportGltfAction(string inputPath) : IAction
{ {
public MdlFile? Out; public MdlFile? Out;
public readonly IoNotifier Notifier = new(manager.Log); public readonly IoNotifier Notifier = new();
public void Execute(CancellationToken cancel) public void Execute(CancellationToken cancel)
{ {
@ -320,7 +314,7 @@ public sealed class ModelManager(
public bool Equals(IAction? other) public bool Equals(IAction? other)
{ {
if (other is not ImportGltfAction) if (other is not ImportGltfAction rhs)
return false; return false;
return true; return true;

View file

@ -1,9 +1,10 @@
using Dalamud.Utility; using Dalamud.Utility;
using Luna;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Filesystem;
using Penumbra.Import.Structs; using Penumbra.Import.Structs;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Services;
using SharpCompress.Archives; using SharpCompress.Archives;
using SharpCompress.Archives.Rar; using SharpCompress.Archives.Rar;
using SharpCompress.Archives.SevenZip; using SharpCompress.Archives.SevenZip;
@ -15,7 +16,7 @@ namespace Penumbra.Import;
public partial class TexToolsImporter public partial class TexToolsImporter
{ {
private static readonly ExtractionOptions ExtractionOptions = new() private static readonly ExtractionOptions _extractionOptions = new()
{ {
ExtractFullPath = true, ExtractFullPath = true,
Overwrite = true, Overwrite = true,
@ -78,7 +79,7 @@ public partial class TexToolsImporter
using var t = new StreamReader(s); using var t = new StreamReader(s);
using var j = new JsonTextReader(t); using var j = new JsonTextReader(t);
var obj = JObject.Load(j); var obj = JObject.Load(j);
name = obj[nameof(Mod.Name)]?.Value<string>()?.RemoveInvalidFileNameSymbols() ?? string.Empty; name = obj[nameof(Mod.Name)]?.Value<string>()?.RemoveInvalidPathSymbols() ?? string.Empty;
if (name.Length == 0) if (name.Length == 0)
throw new Exception("Invalid mod archive: mod meta has no name."); throw new Exception("Invalid mod archive: mod meta has no name.");
@ -141,16 +142,16 @@ public partial class TexToolsImporter
switch (Path.GetExtension(reader.Entry.Key)) switch (Path.GetExtension(reader.Entry.Key))
{ {
case ".mdl": case ".mdl":
_migrationManager.MigrateMdlDuringExtraction(reader, _currentModDirectory!.FullName, ExtractionOptions); _migrationManager.MigrateMdlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
break; break;
case ".mtrl": case ".mtrl":
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, ExtractionOptions); _migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
break; break;
case ".tex": case ".tex":
_migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, ExtractionOptions); _migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, _extractionOptions);
break; break;
default: default:
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, ExtractionOptions); reader.WriteEntryToDirectory(_currentModDirectory!.FullName, _extractionOptions);
break; break;
} }
} }

View file

@ -1,5 +1,9 @@
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using OtterGui;
using Dalamud.Interface.Utility;
using ImSharp; using ImSharp;
using OtterGui.Text; using Penumbra.UI;
using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32; using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
namespace Penumbra.Import.Textures; namespace Penumbra.Import.Textures;
@ -25,19 +29,20 @@ public partial class CombinedTexture
private const float BWeight = 0.0722f; private const float BWeight = 0.0722f;
// @formatter:off // @formatter:off
private static readonly IReadOnlyList<(StringU8 Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms = private static readonly IReadOnlyList<(string Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms =
[ new[]
(new StringU8("No Transform (Identity)"u8), Matrix4x4.Identity, Vector4.Zero ), {
(new StringU8("Grayscale (Average)"u8), new Matrix4x4(OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero ), ("No Transform (Identity)", Matrix4x4.Identity, Vector4.Zero ),
(new StringU8("Grayscale (Weighted)"u8), new Matrix4x4(RWeight, RWeight, RWeight, 0.0f, GWeight, GWeight, GWeight, 0.0f, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero ), ("Grayscale (Average)", new Matrix4x4(OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero ),
(new StringU8("Grayscale (Average) to Alpha"u8), new Matrix4x4(OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero ), ("Grayscale (Weighted)", new Matrix4x4(RWeight, RWeight, RWeight, 0.0f, GWeight, GWeight, GWeight, 0.0f, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), Vector4.Zero ),
(new StringU8("Grayscale (Weighted) to Alpha"u8), new Matrix4x4(RWeight, RWeight, RWeight, RWeight, GWeight, GWeight, GWeight, GWeight, BWeight, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero ), ("Grayscale (Average) to Alpha", new Matrix4x4(OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, OneThird, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero ),
(new StringU8("Make Opaque (Drop Alpha)"u8), new Matrix4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ), ("Grayscale (Weighted) to Alpha", new Matrix4x4(RWeight, RWeight, RWeight, RWeight, GWeight, GWeight, GWeight, GWeight, BWeight, BWeight, BWeight, BWeight, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.Zero ),
(new StringU8("Extract Red"u8), new Matrix4x4(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ), ("Make Opaque (Drop Alpha)", new Matrix4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ),
(new StringU8("Extract Green"u8), new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ), ("Extract Red", new Matrix4x4(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ),
(new StringU8("Extract Blue"u8), new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ), ("Extract Green", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ),
(new StringU8("Extract Alpha"u8), new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vector4.UnitW ), ("Extract Blue", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), Vector4.UnitW ),
]; ("Extract Alpha", new Matrix4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vector4.UnitW ),
};
// @formatter:on // @formatter:on
private Vector4 DataLeft(int offset) private Vector4 DataLeft(int offset)
@ -206,15 +211,15 @@ public partial class CombinedTexture
return transformed; return transformed;
} }
private static bool DragFloat(Utf8StringHandler<LabelStringHandlerBuffer> label, float width, ref float value) private static bool DragFloat(string label, float width, ref float value)
{ {
var tmp = value; var tmp = value;
Im.Table.NextColumn(); ImGui.TableNextColumn();
Im.Item.SetNextWidth(width); ImGui.SetNextItemWidth(width);
if (Im.Drag(label, ref tmp, speed: 0.001f, min: -1f, max: 1f)) if (ImGui.DragFloat(label, ref tmp, 0.001f, -1f, 1f))
value = tmp; value = tmp;
return Im.Item.DeactivatedAfterEdit; return ImGui.IsItemDeactivatedAfterEdit();
} }
public void DrawMatrixInputLeft(float width) public void DrawMatrixInputLeft(float width)
@ -225,69 +230,53 @@ public partial class CombinedTexture
Update(); Update();
} }
private sealed class CombineOperationCombo() : SimpleFilterCombo<CombineOp>(SimpleFilterType.None)
{
private static readonly CombineOp[] UserValues = Enum.GetValues<CombineOp>().Where(c => (int)c >= 0).ToArray();
public override StringU8 DisplayString(in CombineOp value)
=> new(value.ToLabelU8());
public override string FilterString(in CombineOp value)
=> value.ToLabel();
public override IEnumerable<CombineOp> GetBaseItems()
=> UserValues;
public override StringU8 Tooltip(in CombineOp value)
=> new(value.Tooltip());
}
private sealed class ResizeOperationCombo() : SimpleFilterCombo<ResizeOp>(SimpleFilterType.None)
{
private static readonly ResizeOp[] UserValues = Enum.GetValues<ResizeOp>().Where(c => (int)c >= 0).ToArray();
public override StringU8 DisplayString(in ResizeOp value)
=> new(value.ToLabelU8());
public override string FilterString(in ResizeOp value)
=> value.ToLabel();
public override IEnumerable<ResizeOp> GetBaseItems()
=> UserValues;
}
private readonly CombineOperationCombo _combineCombo = new();
private readonly ResizeOperationCombo _resizeCombo = new();
public void DrawMatrixInputRight(float width) public void DrawMatrixInputRight(float width)
{ {
var ret = DrawMatrixInput(ref _multiplierRight, ref _constantRight, width); var ret = DrawMatrixInput(ref _multiplierRight, ref _constantRight, width);
ret |= DrawMatrixTools(ref _multiplierRight, ref _constantRight); ret |= DrawMatrixTools(ref _multiplierRight, ref _constantRight);
Im.Item.SetNextWidthScaled(75); ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
Im.Drag("##XOffset"u8, ref _offsetX, speed: 0.5f); ImGui.DragInt("##XOffset", ref _offsetX, 0.5f);
ret |= Im.Item.DeactivatedAfterEdit; ret |= ImGui.IsItemDeactivatedAfterEdit();
Im.Line.Same(); Im.Line.Same();
Im.Item.SetNextWidthScaled(75); ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
Im.Drag("Offsets##YOffset"u8, ref _offsetY, speed: 0.5f); ImGui.DragInt("Offsets##YOffset", ref _offsetY, 0.5f);
ret |= Im.Item.DeactivatedAfterEdit; ret |= ImGui.IsItemDeactivatedAfterEdit();
Im.Item.SetNextWidthScaled(200); ImGui.SetNextItemWidth(200.0f * UiHelpers.Scale);
ret |= _combineCombo.Draw("Combine Operation"u8, ref _combineOp, StringU8.Empty, 200 * Im.Style.GlobalScale); using (var c = ImRaii.Combo("Combine Operation", CombineOpLabels[(int)_combineOp]))
var resizeOp = GetActualResizeOp(_resizeOp, _combineOp);
using (Im.Disabled((int)resizeOp < 0))
{ {
ret |= _resizeCombo.Draw("Resizing Mode"u8, ref _resizeOp, StringU8.Empty, 200 * Im.Style.GlobalScale); if (c)
foreach (var op in Enum.GetValues<CombineOp>())
{
if ((int)op < 0) // Negative codes are for internal use only.
continue;
if (ImGui.Selectable(CombineOpLabels[(int)op], op == _combineOp))
{
_combineOp = op;
ret = true;
} }
using (Im.Disabled(_combineOp != CombineOp.CopyChannels)) ImGuiUtil.SelectableHelpMarker(CombineOpTooltips[(int)op]);
}
}
var resizeOp = GetActualResizeOp(_resizeOp, _combineOp);
using (var dis = ImRaii.Disabled((int)resizeOp < 0))
{ {
Im.Text("Copy"u8); ret |= ImGuiUtil.GenericEnumCombo("Resizing Mode", 200.0f * UiHelpers.Scale, _resizeOp, out _resizeOp,
Enum.GetValues<ResizeOp>().Where(op => (int)op >= 0), op => ResizeOpLabels[(int)op]);
}
using (var dis = ImRaii.Disabled(_combineOp != CombineOp.CopyChannels))
{
ImGui.TextUnformatted("Copy");
foreach (var channel in Enum.GetValues<Channels>()) foreach (var channel in Enum.GetValues<Channels>())
{ {
Im.Line.Same(); Im.Line.Same();
var copy = (_copyChannels & channel) != 0; var copy = (_copyChannels & channel) != 0;
if (Im.Checkbox(channel.ToString(), ref copy)) if (ImGui.Checkbox(channel.ToString(), ref copy))
{ {
_copyChannels = copy ? _copyChannels | channel : _copyChannels & ~channel; _copyChannels = copy ? _copyChannels | channel : _copyChannels & ~channel;
ret = true; ret = true;
@ -301,52 +290,62 @@ public partial class CombinedTexture
private static bool DrawMatrixInput(ref Matrix4x4 multiplier, ref Vector4 constant, float width) private static bool DrawMatrixInput(ref Matrix4x4 multiplier, ref Vector4 constant, float width)
{ {
using var table = Im.Table.Begin(StringU8.Empty, 5, TableFlags.BordersInner | TableFlags.SizingFixedFit); using var table = ImRaii.Table(string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit);
if (!table) if (!table)
return false; return false;
var changes = false; var changes = false;
table.NextColumn(); ImGui.TableNextColumn();
table.NextColumn(); ImGui.TableNextColumn();
ImEx.TextCentered("R"u8); ImGuiUtil.Center("R");
table.NextColumn(); ImGui.TableNextColumn();
ImEx.TextCentered("G"u8); ImGuiUtil.Center("G");
table.NextColumn(); ImGui.TableNextColumn();
ImEx.TextCentered("B"u8); ImGuiUtil.Center("B");
table.NextColumn(); ImGui.TableNextColumn();
ImEx.TextCentered("A"u8); ImGuiUtil.Center("A");
var inputWidth = width / 6; var inputWidth = width / 6;
table.DrawFrameColumn("R "u8); ImGui.TableNextColumn();
changes |= DragFloat("##RR"u8, inputWidth, ref multiplier.M11); ImGui.AlignTextToFramePadding();
changes |= DragFloat("##RG"u8, inputWidth, ref multiplier.M12); ImGui.Text("R ");
changes |= DragFloat("##RB"u8, inputWidth, ref multiplier.M13); changes |= DragFloat("##RR", inputWidth, ref multiplier.M11);
changes |= DragFloat("##RA"u8, inputWidth, ref multiplier.M14); changes |= DragFloat("##RG", inputWidth, ref multiplier.M12);
changes |= DragFloat("##RB", inputWidth, ref multiplier.M13);
changes |= DragFloat("##RA", inputWidth, ref multiplier.M14);
table.DrawFrameColumn("G "u8); ImGui.TableNextColumn();
changes |= DragFloat("##GR"u8, inputWidth, ref multiplier.M21); ImGui.AlignTextToFramePadding();
changes |= DragFloat("##GG"u8, inputWidth, ref multiplier.M22); ImGui.Text("G ");
changes |= DragFloat("##GB"u8, inputWidth, ref multiplier.M23); changes |= DragFloat("##GR", inputWidth, ref multiplier.M21);
changes |= DragFloat("##GA"u8, inputWidth, ref multiplier.M24); changes |= DragFloat("##GG", inputWidth, ref multiplier.M22);
changes |= DragFloat("##GB", inputWidth, ref multiplier.M23);
changes |= DragFloat("##GA", inputWidth, ref multiplier.M24);
table.DrawFrameColumn("B "u8); ImGui.TableNextColumn();
changes |= DragFloat("##BR"u8, inputWidth, ref multiplier.M31); ImGui.AlignTextToFramePadding();
changes |= DragFloat("##BG"u8, inputWidth, ref multiplier.M32); ImGui.Text("B ");
changes |= DragFloat("##BB"u8, inputWidth, ref multiplier.M33); changes |= DragFloat("##BR", inputWidth, ref multiplier.M31);
changes |= DragFloat("##BA"u8, inputWidth, ref multiplier.M34); changes |= DragFloat("##BG", inputWidth, ref multiplier.M32);
changes |= DragFloat("##BB", inputWidth, ref multiplier.M33);
changes |= DragFloat("##BA", inputWidth, ref multiplier.M34);
table.DrawFrameColumn("A "u8); ImGui.TableNextColumn();
changes |= DragFloat("##AR"u8, inputWidth, ref multiplier.M41); ImGui.AlignTextToFramePadding();
changes |= DragFloat("##AG"u8, inputWidth, ref multiplier.M42); ImGui.Text("A ");
changes |= DragFloat("##AB"u8, inputWidth, ref multiplier.M43); changes |= DragFloat("##AR", inputWidth, ref multiplier.M41);
changes |= DragFloat("##AA"u8, inputWidth, ref multiplier.M44); changes |= DragFloat("##AG", inputWidth, ref multiplier.M42);
changes |= DragFloat("##AB", inputWidth, ref multiplier.M43);
changes |= DragFloat("##AA", inputWidth, ref multiplier.M44);
table.DrawFrameColumn("1 "u8); ImGui.TableNextColumn();
changes |= DragFloat("##1R"u8, inputWidth, ref constant.X); ImGui.AlignTextToFramePadding();
changes |= DragFloat("##1G"u8, inputWidth, ref constant.Y); ImGui.Text("1 ");
changes |= DragFloat("##1B"u8, inputWidth, ref constant.Z); changes |= DragFloat("##1R", inputWidth, ref constant.X);
changes |= DragFloat("##1A"u8, inputWidth, ref constant.W); changes |= DragFloat("##1G", inputWidth, ref constant.Y);
changes |= DragFloat("##1B", inputWidth, ref constant.Z);
changes |= DragFloat("##1A", inputWidth, ref constant.W);
return changes; return changes;
} }
@ -355,28 +354,28 @@ public partial class CombinedTexture
{ {
var changes = PresetCombo(ref multiplier, ref constant); var changes = PresetCombo(ref multiplier, ref constant);
Im.Line.Same(); Im.Line.Same();
Im.ScaledDummy(20); ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
Im.Line.Same(); Im.Line.Same();
Im.Text("Invert"u8); ImGui.TextUnformatted("Invert");
Im.Line.Same(); Im.Line.Same();
Channels channels = 0; Channels channels = 0;
if (Im.Button("Colors"u8)) if (ImGui.Button("Colors"))
channels |= Channels.Red | Channels.Green | Channels.Blue; channels |= Channels.Red | Channels.Green | Channels.Blue;
Im.Line.Same(); Im.Line.Same();
if (Im.Button("R"u8)) if (ImGui.Button("R"))
channels |= Channels.Red; channels |= Channels.Red;
Im.Line.Same(); Im.Line.Same();
if (Im.Button("G"u8)) if (ImGui.Button("G"))
channels |= Channels.Green; channels |= Channels.Green;
Im.Line.Same(); Im.Line.Same();
if (Im.Button("B"u8)) if (ImGui.Button("B"))
channels |= Channels.Blue; channels |= Channels.Blue;
Im.Line.Same(); Im.Line.Same();
if (Im.Button("A"u8)) if (ImGui.Button("A"))
channels |= Channels.Alpha; channels |= Channels.Alpha;
changes |= InvertChannels(channels, ref multiplier, ref constant); changes |= InvertChannels(channels, ref multiplier, ref constant);
@ -385,14 +384,14 @@ public partial class CombinedTexture
private static bool PresetCombo(ref Matrix4x4 multiplier, ref Vector4 constant) private static bool PresetCombo(ref Matrix4x4 multiplier, ref Vector4 constant)
{ {
using var combo = Im.Combo.Begin("Presets"u8, StringU8.Empty, ComboFlags.NoPreview); using var combo = ImRaii.Combo("Presets", string.Empty, ImGuiComboFlags.NoPreview);
if (!combo) if (!combo)
return false; return false;
var ret = false; var ret = false;
foreach (var (label, preMultiplier, preConstant) in PredefinedColorTransforms) foreach (var (label, preMultiplier, preConstant) in PredefinedColorTransforms)
{ {
if (!Im.Selectable(label, multiplier == preMultiplier && constant == preConstant)) if (!ImGui.Selectable(label, multiplier == preMultiplier && constant == preConstant))
continue; continue;
multiplier = preMultiplier; multiplier = preMultiplier;

View file

@ -1,54 +1,30 @@
using Luna.Generators;
namespace Penumbra.Import.Textures; namespace Penumbra.Import.Textures;
public partial class CombinedTexture public partial class CombinedTexture
{ {
[NamedEnum("ToLabel")] private enum CombineOp
[TooltipEnum]
public enum CombineOp
{ {
LeftMultiply = -4, LeftMultiply = -4,
LeftCopy = -3, LeftCopy = -3,
RightCopy = -2, RightCopy = -2,
Invalid = -1, Invalid = -1,
[Name("Overlay over Input")]
[Tooltip("Standard composition.\nApply the overlay over the input.")]
Over = 0, Over = 0,
[Name("Input over Overlay")]
[Tooltip("Standard composition, reversed.\nApply the input over the overlay ; can be used to fix some wrong imports.")]
Under = 1, Under = 1,
[Name("Replace Input")]
[Tooltip("Completely replace the input with the overlay.\nCan be used to select the destination file as input and the source file as overlay.")]
RightMultiply = 2, RightMultiply = 2,
[Name("Copy Channels")]
[Tooltip("Replace some input channels with those from the overlay.\nUseful for Multi maps.")]
CopyChannels = 3, CopyChannels = 3,
} }
[NamedEnum("ToLabel")] private enum ResizeOp
public enum ResizeOp
{ {
LeftOnly = -2, LeftOnly = -2,
RightOnly = -1, RightOnly = -1,
[Name("No Resizing")]
None = 0, None = 0,
[Name("Adjust Overlay to Input")]
ToLeft = 1, ToLeft = 1,
[Name("Adjust Input to Overlay")]
ToRight = 2, ToRight = 2,
} }
[Flags] [Flags]
[NamedEnum] private enum Channels : byte
public enum Channels : byte
{ {
Red = 1, Red = 1,
Green = 2, Green = 2,
@ -56,6 +32,29 @@ public partial class CombinedTexture
Alpha = 8, Alpha = 8,
} }
private static readonly IReadOnlyList<string> CombineOpLabels = new[]
{
"Overlay over Input",
"Input over Overlay",
"Replace Input",
"Copy Channels",
};
private static readonly IReadOnlyList<string> CombineOpTooltips = new[]
{
"Standard composition.\nApply the overlay over the input.",
"Standard composition, reversed.\nApply the input over the overlay ; can be used to fix some wrong imports.",
"Completely replace the input with the overlay.\nCan be used to select the destination file as input and the source file as overlay.",
"Replace some input channels with those from the overlay.\nUseful for Multi maps.",
};
private static readonly IReadOnlyList<string> ResizeOpLabels = new string[]
{
"No Resizing",
"Adjust Overlay to Input",
"Adjust Input to Overlay",
};
private static ResizeOp GetActualResizeOp(ResizeOp resizeOp, CombineOp combineOp) private static ResizeOp GetActualResizeOp(ResizeOp resizeOp, CombineOp combineOp)
=> combineOp switch => combineOp switch
{ {

View file

@ -1,80 +0,0 @@
using Dalamud.Plugin.Services;
using ImSharp;
using Penumbra.Api.Enums;
using Penumbra.Interop.ResourceTree;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.Import.Textures;
public abstract class PathSelectCombo(IDataManager dataManager) : FilterComboBase<PathSelectCombo.PathData>
{
public bool Draw(Utf8StringHandler<LabelStringHandlerBuffer> label, Utf8StringHandler<HintStringHandlerBuffer> tooltip, string current,
int skipPrefix, out string newPath)
{
_skipPrefix = skipPrefix;
_selected = current;
if (!base.Draw(label, current.Length > 0 ? current : "Choose a modded texture from this mod here..."u8, tooltip,
Im.ContentRegion.Available.X, out var ret))
{
newPath = string.Empty;
return false;
}
newPath = ret.SearchPath;
return true;
}
public record PathData(StringU8 Path, string SearchPath, bool IsOnPlayer, bool IsGame);
private int _skipPrefix;
private string _selected = string.Empty;
protected abstract IEnumerable<FileRegistry> GetFiles();
protected abstract ISet<string> GetPlayerResources();
protected override IEnumerable<PathData> GetItems()
{
var playerResources = GetPlayerResources();
var files = GetFiles();
foreach (var (file, game) in files.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true))
.Prepend((f.File.FullName, false)))
.Where(p => p.Item2 ? dataManager.FileExists(p.Item1) : File.Exists(p.Item1)))
{
var onPlayer = playerResources.Contains(file);
var displayString = game ? new StringU8($"--> {file}") : new StringU8(file.AsSpan(_skipPrefix));
yield return new PathData(displayString, file, onPlayer, game);
}
}
protected override float ItemHeight
=> Im.Style.TextHeightWithSpacing;
protected override bool DrawItem(in PathData item, int globalIndex, bool selected)
{
var textColor = item.IsOnPlayer ? ColorId.HandledConflictMod.Value() :
item.IsGame ? ColorId.FolderExpanded.Value() : ColorParameter.Default;
bool ret;
using (ImGuiColor.Text.Push(textColor))
{
ret = Im.Selectable(item.Path, selected);
}
Im.Tooltip.OnHover(item.IsGame
? "This is a game path and refers to an unmanipulated file from your game data."u8
: "This is a path to a modded file on your file system."u8);
return ret;
}
protected override bool IsSelected(PathData item, int globalIndex)
=> string.Equals(_selected, item.SearchPath, StringComparison.OrdinalIgnoreCase);
}
public sealed class TextureSelectCombo(ResourceTreeFactory resources, ModEditor editor, IDataManager dataManager) : PathSelectCombo(dataManager)
{
protected override IEnumerable<FileRegistry> GetFiles()
=> editor.Files.Tex;
protected override ISet<string> GetPlayerResources()
=> ResourceTreeApiHelper.GetPlayerResourcesOfType(resources, ResourceType.Tex);
}

View file

@ -1,10 +1,16 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp; using ImSharp;
using Lumina.Data.Files; using Lumina.Data.Files;
using Luna; using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using OtterTex; using OtterTex;
using Penumbra.Mods.Editor;
using Penumbra.String.Classes;
using Penumbra.UI; using Penumbra.UI;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using VectorExtensions = Luna.VectorExtensions; using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.Import.Textures; namespace Penumbra.Import.Textures;
@ -14,42 +20,44 @@ public static class TextureDrawer
{ {
if (texture.TextureWrap != null) if (texture.TextureWrap != null)
{ {
size = VectorExtensions.Contain(texture.TextureWrap.Size, size); size = texture.TextureWrap.Size.Contain(size);
Im.Image.Draw(texture.TextureWrap.Id(), size); ImGui.Image(texture.TextureWrap.Handle, size);
DrawData(texture); DrawData(texture);
} }
else if (texture.LoadError != null) else if (texture.LoadError != null)
{ {
const string link = "https://aka.ms/vcredist"; const string link = "https://aka.ms/vcredist";
Im.Text("Could not load file:"u8); ImGui.TextUnformatted("Could not load file:");
if (texture.LoadError is DllNotFoundException) if (texture.LoadError is DllNotFoundException)
{ {
Im.Text("A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable."u8, ImGuiUtil.TextColored(Colors.RegexWarningBorder,
Colors.RegexWarningBorder); "A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable.");
if (Im.Button("Microsoft VC Redistributables"u8)) if (ImGui.Button("Microsoft VC Redistributables"))
Dalamud.Utility.Util.OpenLink(link); Dalamud.Utility.Util.OpenLink(link);
Im.Tooltip.OnHover($"Open {link} in your browser."); ImGuiUtil.HoverTooltip($"Open {link} in your browser.");
} }
Im.Text($"{texture.LoadError}", Colors.RegexWarningBorder); ImGuiUtil.TextColored(Colors.RegexWarningBorder, texture.LoadError.ToString());
} }
} }
public static void PathInputBox(TextureManager textures, Texture current, ref string? tmpPath, ReadOnlySpan<byte> label, public static void PathInputBox(TextureManager textures, Texture current, ref string? tmpPath, string label, string hint, string tooltip,
ReadOnlySpan<byte> hint, ReadOnlySpan<byte> tooltip,
string startPath, FileDialogService fileDialog, string defaultModImportPath) string startPath, FileDialogService fileDialog, string defaultModImportPath)
{ {
tmpPath ??= current.Path; tmpPath ??= current.Path;
using var spacing = ImStyleDouble.ItemSpacing.PushX(UiHelpers.ScaleX3); using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
Im.Item.SetNextWidth(-2 * Im.Style.FrameHeight - 7 * Im.Style.GlobalScale); new Vector2(UiHelpers.ScaleX3, ImGui.GetStyle().ItemSpacing.Y));
if (ImEx.InputOnDeactivation.Text(label, tmpPath, out tmpPath, hint)) ImGui.SetNextItemWidth(-2 * ImGui.GetFrameHeight() - 7 * UiHelpers.Scale);
ImGui.InputTextWithHint(label, hint, ref tmpPath, Utf8GamePath.MaxGamePathLength);
if (ImGui.IsItemDeactivatedAfterEdit())
current.Load(textures, tmpPath); current.Load(textures, tmpPath);
Im.Tooltip.OnHover(tooltip); ImGuiUtil.HoverTooltip(tooltip);
Im.Line.Same(); Im.Line.Same();
if (ImEx.Icon.Button(LunaStyle.FolderIcon)) if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Folder.ToIconString(), new Vector2(ImGui.GetFrameHeight()), string.Empty, false,
true))
{ {
if (defaultModImportPath.Length > 0) if (defaultModImportPath.Length > 0)
startPath = defaultModImportPath; startPath = defaultModImportPath;
@ -64,41 +72,97 @@ public static class TextureDrawer
} }
Im.Line.Same(); Im.Line.Same();
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Reload the currently selected path."u8)) if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Recycle.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
"Reload the currently selected path.", false,
true))
current.Reload(textures); current.Reload(textures);
} }
private static void DrawData(Texture texture) private static void DrawData(Texture texture)
{ {
using var table = Im.Table.Begin("##data"u8, 2, TableFlags.SizingFixedFit); using var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit);
table.DrawColumn("Width"u8); ImGuiUtil.DrawTableColumn("Width");
table.DrawColumn($"{texture.TextureWrap!.Width}"); ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Width.ToString());
table.DrawColumn("Height"u8); ImGuiUtil.DrawTableColumn("Height");
table.DrawColumn($"{texture.TextureWrap!.Height}"); ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Height.ToString());
table.DrawColumn("File Type"u8); ImGuiUtil.DrawTableColumn("File Type");
table.DrawColumn($"{texture.Type}"); ImGuiUtil.DrawTableColumn(texture.Type.ToString());
table.DrawColumn("Bitmap Size"u8); ImGuiUtil.DrawTableColumn("Bitmap Size");
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)"); ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)");
switch (texture.BaseImage.Image) switch (texture.BaseImage.Image)
{ {
case ScratchImage s: case ScratchImage s:
table.DrawColumn("Format"u8); ImGuiUtil.DrawTableColumn("Format");
table.DrawColumn($"{s.Meta.Format}"); ImGuiUtil.DrawTableColumn(s.Meta.Format.ToString());
table.DrawColumn("Mip Levels"u8); ImGuiUtil.DrawTableColumn("Mip Levels");
table.DrawColumn($"{s.Meta.MipLevels}"); ImGuiUtil.DrawTableColumn(s.Meta.MipLevels.ToString());
table.DrawColumn("Data Size"u8); ImGuiUtil.DrawTableColumn("Data Size");
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)"); ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)");
table.DrawColumn("Number of Images"u8); ImGuiUtil.DrawTableColumn("Number of Images");
table.DrawColumn($"{s.Images.Length}"); ImGuiUtil.DrawTableColumn(s.Images.Length.ToString());
break; break;
case TexFile t: case TexFile t:
table.DrawColumn("Format"u8); ImGuiUtil.DrawTableColumn("Format");
table.DrawColumn($"{t.Header.Format}"); ImGuiUtil.DrawTableColumn(t.Header.Format.ToString());
table.DrawColumn("Mip Levels"u8); ImGuiUtil.DrawTableColumn("Mip Levels");
table.DrawColumn($"{t.Header.MipCount}"); ImGuiUtil.DrawTableColumn(t.Header.MipCount.ToString());
table.DrawColumn("Data Size"u8); ImGuiUtil.DrawTableColumn("Data Size");
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(t.ImageData.Length)} ({t.ImageData.Length} Bytes)"); ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(t.ImageData.Length)} ({t.ImageData.Length} Bytes)");
break; break;
} }
} }
public sealed class PathSelectCombo(TextureManager textures, ModEditor editor, Func<ISet<string>> getPlayerResources)
: FilterComboCache<(string Path, bool Game, bool IsOnPlayer)>(() => CreateFiles(textures, editor, getPlayerResources),
MouseWheelType.None, Penumbra.Log)
{
private int _skipPrefix = 0;
protected override string ToString((string Path, bool Game, bool IsOnPlayer) obj)
=> obj.Path;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var (path, game, isOnPlayer) = Items[globalIdx];
bool ret;
using (var color = ImGuiColor.Text.Push(ColorId.FolderExpanded.Value(), game))
{
color.Push(ImGuiColor.Text, ColorId.HandledConflictMod.Value(), isOnPlayer);
var equals = string.Equals(CurrentSelection.Path, path, StringComparison.OrdinalIgnoreCase);
var p = game ? $"--> {path}" : path[_skipPrefix..];
ret = ImGui.Selectable(p, selected) && !equals;
}
ImGuiUtil.HoverTooltip(game
? "This is a game path and refers to an unmanipulated file from your game data."
: "This is a path to a modded file on your file system.");
return ret;
}
private static IReadOnlyList<(string Path, bool Game, bool IsOnPlayer)> CreateFiles(TextureManager textures, ModEditor editor,
Func<ISet<string>> getPlayerResources)
{
var playerResources = getPlayerResources();
return editor.Files.Tex.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true))
.Prepend((f.File.FullName, false)))
.Where(p => p.Item2 ? textures.GameFileExists(p.Item1) : File.Exists(p.Item1))
.Select(p => (p.Item1, p.Item2, playerResources.Contains(p.Item1)))
.ToList();
}
public bool Draw(string label, string tooltip, string current, int skipPrefix, out string newPath)
{
_skipPrefix = skipPrefix;
var startPath = current.Length > 0 ? current : "Choose a modded texture from this mod here...";
if (!Draw(label, startPath, tooltip, -0.0001f, ImGui.GetTextLineHeightWithSpacing()))
{
newPath = current;
return false;
}
newPath = CurrentSelection.Item1;
return true;
}
}
} }

View file

@ -1,5 +1,5 @@
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Luna; using OtterGui.Services;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.CrashHandler.Buffers; using Penumbra.CrashHandler.Buffers;
using Penumbra.GameData; using Penumbra.GameData;

View file

@ -1,5 +1,5 @@
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Luna; using OtterGui.Services;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;

View file

@ -9,16 +9,6 @@ namespace Penumbra.Interop.ResourceTree;
internal static class ResourceTreeApiHelper internal static class ResourceTreeApiHelper
{ {
public static HashSet<string> GetPlayerResourcesOfType(ResourceTreeFactory factory, ResourceType type)
{
var resources = GetResourcesOfType(factory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type)
.Values
.SelectMany(r => r.Values)
.Select(r => r.Item1);
return new HashSet<string>(resources, StringComparer.OrdinalIgnoreCase);
}
public static Dictionary<ushort, Dictionary<string, HashSet<string>>> GetResourcePathDictionaries( public static Dictionary<ushort, Dictionary<string, HashSet<string>>> GetResourcePathDictionaries(
IEnumerable<(ICharacter, ResourceTree)> resourceTrees) IEnumerable<(ICharacter, ResourceTree)> resourceTrees)
{ {

View file

@ -71,8 +71,6 @@
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" /> <ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" /> <ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" /> <ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
<ProjectReference Include="..\Luna\Luna.Generators\Luna.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion"> <Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">

View file

@ -1,5 +1,7 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using Lumina.Data;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
@ -18,7 +20,7 @@ public partial class ModEditWindow
private readonly ResourceTreeViewer _quickImportViewer; private readonly ResourceTreeViewer _quickImportViewer;
private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new(); private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new();
public HashSet<string> GetPlayerResourcesOfType(ResourceType type) private HashSet<string> GetPlayerResourcesOfType(ResourceType type)
{ {
var resources = ResourceTreeApiHelper var resources = ResourceTreeApiHelper
.GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type) .GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type)

View file

@ -18,7 +18,7 @@ public partial class ModEditWindow
private readonly Texture _left = new(); private readonly Texture _left = new();
private readonly Texture _right = new(); private readonly Texture _right = new();
private readonly CombinedTexture _center; private readonly CombinedTexture _center;
private readonly TextureSelectCombo _textureSelectCombo; private readonly TextureDrawer.PathSelectCombo _textureSelectCombo;
private bool _overlayCollapsed = true; private bool _overlayCollapsed = true;
private bool _addMipMaps = true; private bool _addMipMaps = true;
@ -49,13 +49,13 @@ public partial class ModEditWindow
return; return;
using var id = ImRaii.PushId(label); using var id = ImRaii.PushId(label);
ImEx.TextFramed(label, Im.ContentRegion.Available with { Y = 0 }, ImGuiColor.FrameBackground.Get()); ImEx.TextFramed(label, new Vector2(-1, 0), ImGuiColor.FrameBackground.Get());
ImGui.NewLine(); ImGui.NewLine();
using (ImRaii.Disabled(!_center.SaveTask.IsCompleted)) using (ImRaii.Disabled(!_center.SaveTask.IsCompleted))
{ {
TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input"u8, "Import Image..."u8, TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input", "Import Image...",
"Can import game paths as well as your own files."u8, Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath); "Can import game paths as well as your own files.", Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath);
if (_textureSelectCombo.Draw("##combo", if (_textureSelectCombo.Draw("##combo",
"Select the textures included in this mod on your drive or the ones they replace from the game files.", tex.Path, "Select the textures included in this mod on your drive or the ones they replace from the game files.", tex.Path,
Mod.ModPath.FullName.Length + 1, out var newPath) Mod.ModPath.FullName.Length + 1, out var newPath)
@ -200,6 +200,7 @@ public partial class ModEditWindow
case TaskStatus.WaitingToRun: case TaskStatus.WaitingToRun:
case TaskStatus.Running: case TaskStatus.Running:
ImGuiUtil.DrawTextButton("Computing...", -Vector2.UnitX, Colors.PressEnterWarningBg); ImGuiUtil.DrawTextButton("Computing...", -Vector2.UnitX, Colors.PressEnterWarningBg);
break; break;
case TaskStatus.Canceled: case TaskStatus.Canceled:
case TaskStatus.Faulted: case TaskStatus.Faulted:
@ -209,7 +210,9 @@ public partial class ModEditWindow
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error"); ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
break; break;
} }
default: ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight())); break; default:
ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight()));
break;
} }
ImGui.NewLine(); ImGui.NewLine();

View file

@ -656,7 +656,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
() => Mod?.ModPath.FullName ?? string.Empty, () => Mod?.ModPath.FullName ?? string.Empty,
(bytes, path, _) => new PbdTab(bytes, path)); (bytes, path, _) => new PbdTab(bytes, path));
_center = new CombinedTexture(_left, _right); _center = new CombinedTexture(_left, _right);
_textureSelectCombo = new TextureSelectCombo(resourceTreeFactory, editor, gameData); _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex));
_resourceTreeFactory = resourceTreeFactory; _resourceTreeFactory = resourceTreeFactory;
_quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions); _quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions);
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow);

View file

@ -15,7 +15,7 @@ public class ResourceTreeViewerFactory(
PcpService pcpService, PcpService pcpService,
IDataManager gameData, IDataManager gameData,
FileDialogService fileDialog, FileDialogService fileDialog,
FileCompactor compactor) : IService FileCompactor compactor) : Luna.IService
{ {
public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action<ResourceNode, IWritable?, Vector2> drawActions) public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action<ResourceNode, IWritable?, Vector2> drawActions)
=> new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator, pcpService, gameData, => new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator, pcpService, gameData,