mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Compare commits
3 commits
cb275f57bf
...
34f067f13d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34f067f13d | ||
|
|
7ed81a9823 | ||
|
|
d88593c500 |
19 changed files with 856 additions and 826 deletions
2
Luna
2
Luna
|
|
@ -1 +1 @@
|
|||
Subproject commit 7214f079cb9b8eeea6fa1a9fe1c6ca8118049969
|
||||
Subproject commit 78216203f4570a6194fce9422204d8abb536c828
|
||||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit f354444776591ae423e2d8374aae346308d81424
|
||||
Subproject commit 9af1e5fce4c13ef98842807d4f593dec8ae80c87
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using OtterGui.Filesystem;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.DataContainers.Bases;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -224,7 +224,7 @@ public sealed partial class IndividualCollections
|
|||
{
|
||||
return identifier.Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})",
|
||||
IdentifierType.Player => $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})",
|
||||
IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)",
|
||||
IdentifierType.Owned =>
|
||||
$"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})'s {_actors.Data.ToName(identifier.Kind, identifier.DataId)}",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using OtterGui.Filesystem;
|
||||
using Luna;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
using OtterGui.Log;
|
||||
using Luna;
|
||||
|
||||
namespace Penumbra.Import.Models;
|
||||
|
||||
public record class IoNotifier
|
||||
public record IoNotifier(Logger Log)
|
||||
{
|
||||
private readonly List<string> _messages = [];
|
||||
private string _context = "";
|
||||
|
||||
/// <summary> Create a new notifier with the specified context appended to any other context already present. </summary>
|
||||
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>
|
||||
public void Warning(string content)
|
||||
|
|
@ -34,7 +34,7 @@ public record class IoNotifier
|
|||
private void SendMessage(string message, Logger.LogLevel type)
|
||||
{
|
||||
var fullText = $"{_context}{message}";
|
||||
Penumbra.Log.Message(type, fullText);
|
||||
Log.Message(type, fullText);
|
||||
_messages.Add(fullText);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Data.Parsing;
|
||||
using Luna;
|
||||
using OtterGui.Tasks;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.GameData;
|
||||
|
|
@ -22,9 +23,15 @@ namespace Penumbra.Import.Models;
|
|||
using Schema2 = SharpGLTF.Schema2;
|
||||
using LuminaMaterial = Lumina.Models.Materials.Material;
|
||||
|
||||
public sealed class ModelManager(IFramework framework, MetaFileManager metaFileManager, ActiveCollections collections, GamePathParser parser)
|
||||
: SingleTaskQueue, IDisposable, Luna.IService
|
||||
public sealed class ModelManager(
|
||||
Logger log,
|
||||
IFramework framework,
|
||||
MetaFileManager metaFileManager,
|
||||
ActiveCollections collections,
|
||||
GamePathParser parser)
|
||||
: SingleTaskQueue, IDisposable, IService
|
||||
{
|
||||
public readonly Logger Log = log;
|
||||
private readonly IFramework _framework = framework;
|
||||
|
||||
private readonly ConcurrentDictionary<IAction, (Task, CancellationTokenSource)> _tasks = new();
|
||||
|
|
@ -48,7 +55,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
|
||||
public Task<(MdlFile?, IoNotifier)> ImportGltf(string inputPath)
|
||||
=> EnqueueWithResult(
|
||||
new ImportGltfAction(inputPath),
|
||||
new ImportGltfAction(this, inputPath),
|
||||
action => (action.Out, action.Notifier)
|
||||
);
|
||||
|
||||
|
|
@ -88,10 +95,9 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
{
|
||||
// Try to find an EST entry from the manipulations provided.
|
||||
var modEst = estManipulations
|
||||
.FirstOrNull(
|
||||
est => est.Key.GenderRace == info.GenderRace
|
||||
&& est.Key.Slot == type
|
||||
&& est.Key.SetId == info.PrimaryId
|
||||
.FirstOrNull(est => est.Key.GenderRace == info.GenderRace
|
||||
&& est.Key.Slot == type
|
||||
&& est.Key.SetId == info.PrimaryId
|
||||
);
|
||||
|
||||
// Try to use an entry from provided manipulations, falling back to the current collection.
|
||||
|
|
@ -190,7 +196,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
string outputPath)
|
||||
: IAction
|
||||
{
|
||||
public readonly IoNotifier Notifier = new();
|
||||
public readonly IoNotifier Notifier = new(manager.Log);
|
||||
|
||||
public void Execute(CancellationToken cancel)
|
||||
{
|
||||
|
|
@ -292,7 +298,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
|
||||
public bool Equals(IAction? other)
|
||||
{
|
||||
if (other is not ExportToGltfAction rhs)
|
||||
if (other is not ExportToGltfAction)
|
||||
return false;
|
||||
|
||||
// TODO: compare configuration and such
|
||||
|
|
@ -300,10 +306,10 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
}
|
||||
}
|
||||
|
||||
private partial class ImportGltfAction(string inputPath) : IAction
|
||||
private class ImportGltfAction(ModelManager manager, string inputPath) : IAction
|
||||
{
|
||||
public MdlFile? Out;
|
||||
public readonly IoNotifier Notifier = new();
|
||||
public readonly IoNotifier Notifier = new(manager.Log);
|
||||
|
||||
public void Execute(CancellationToken cancel)
|
||||
{
|
||||
|
|
@ -314,7 +320,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
|||
|
||||
public bool Equals(IAction? other)
|
||||
{
|
||||
if (other is not ImportGltfAction rhs)
|
||||
if (other is not ImportGltfAction)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using Dalamud.Utility;
|
||||
using Luna;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Import.Structs;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Services;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
|
|
@ -16,7 +15,7 @@ namespace Penumbra.Import;
|
|||
|
||||
public partial class TexToolsImporter
|
||||
{
|
||||
private static readonly ExtractionOptions _extractionOptions = new()
|
||||
private static readonly ExtractionOptions ExtractionOptions = new()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true,
|
||||
|
|
@ -79,7 +78,7 @@ public partial class TexToolsImporter
|
|||
using var t = new StreamReader(s);
|
||||
using var j = new JsonTextReader(t);
|
||||
var obj = JObject.Load(j);
|
||||
name = obj[nameof(Mod.Name)]?.Value<string>()?.RemoveInvalidPathSymbols() ?? string.Empty;
|
||||
name = obj[nameof(Mod.Name)]?.Value<string>()?.RemoveInvalidFileNameSymbols() ?? string.Empty;
|
||||
if (name.Length == 0)
|
||||
throw new Exception("Invalid mod archive: mod meta has no name.");
|
||||
|
||||
|
|
@ -142,16 +141,16 @@ public partial class TexToolsImporter
|
|||
switch (Path.GetExtension(reader.Entry.Key))
|
||||
{
|
||||
case ".mdl":
|
||||
_migrationManager.MigrateMdlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
|
||||
_migrationManager.MigrateMdlDuringExtraction(reader, _currentModDirectory!.FullName, ExtractionOptions);
|
||||
break;
|
||||
case ".mtrl":
|
||||
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
|
||||
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, ExtractionOptions);
|
||||
break;
|
||||
case ".tex":
|
||||
_migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, _extractionOptions);
|
||||
_migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, ExtractionOptions);
|
||||
break;
|
||||
default:
|
||||
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, _extractionOptions);
|
||||
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, ExtractionOptions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui;
|
||||
using Dalamud.Interface.Utility;
|
||||
using ImSharp;
|
||||
using Penumbra.UI;
|
||||
using OtterGui.Text;
|
||||
using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
|
||||
|
||||
namespace Penumbra.Import.Textures;
|
||||
|
|
@ -29,20 +25,19 @@ public partial class CombinedTexture
|
|||
private const float BWeight = 0.0722f;
|
||||
|
||||
// @formatter:off
|
||||
private static readonly IReadOnlyList<(string Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms =
|
||||
new[]
|
||||
{
|
||||
("No Transform (Identity)", Matrix4x4.Identity, 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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
("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 ),
|
||||
};
|
||||
private static readonly IReadOnlyList<(StringU8 Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms =
|
||||
[
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
(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 ),
|
||||
];
|
||||
// @formatter:on
|
||||
|
||||
private Vector4 DataLeft(int offset)
|
||||
|
|
@ -211,15 +206,15 @@ public partial class CombinedTexture
|
|||
return transformed;
|
||||
}
|
||||
|
||||
private static bool DragFloat(string label, float width, ref float value)
|
||||
private static bool DragFloat(Utf8StringHandler<LabelStringHandlerBuffer> label, float width, ref float value)
|
||||
{
|
||||
var tmp = value;
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImGui.DragFloat(label, ref tmp, 0.001f, -1f, 1f))
|
||||
Im.Table.NextColumn();
|
||||
Im.Item.SetNextWidth(width);
|
||||
if (Im.Drag(label, ref tmp, speed: 0.001f, min: -1f, max: 1f))
|
||||
value = tmp;
|
||||
|
||||
return ImGui.IsItemDeactivatedAfterEdit();
|
||||
return Im.Item.DeactivatedAfterEdit;
|
||||
}
|
||||
|
||||
public void DrawMatrixInputLeft(float width)
|
||||
|
|
@ -230,53 +225,69 @@ public partial class CombinedTexture
|
|||
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)
|
||||
{
|
||||
var ret = DrawMatrixInput(ref _multiplierRight, ref _constantRight, width);
|
||||
ret |= DrawMatrixTools(ref _multiplierRight, ref _constantRight);
|
||||
|
||||
ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
|
||||
ImGui.DragInt("##XOffset", ref _offsetX, 0.5f);
|
||||
ret |= ImGui.IsItemDeactivatedAfterEdit();
|
||||
Im.Item.SetNextWidthScaled(75);
|
||||
Im.Drag("##XOffset"u8, ref _offsetX, speed: 0.5f);
|
||||
ret |= Im.Item.DeactivatedAfterEdit;
|
||||
Im.Line.Same();
|
||||
ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
|
||||
ImGui.DragInt("Offsets##YOffset", ref _offsetY, 0.5f);
|
||||
ret |= ImGui.IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui.SetNextItemWidth(200.0f * UiHelpers.Scale);
|
||||
using (var c = ImRaii.Combo("Combine Operation", CombineOpLabels[(int)_combineOp]))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
ImGuiUtil.SelectableHelpMarker(CombineOpTooltips[(int)op]);
|
||||
}
|
||||
}
|
||||
Im.Item.SetNextWidthScaled(75);
|
||||
Im.Drag("Offsets##YOffset"u8, ref _offsetY, speed: 0.5f);
|
||||
ret |= Im.Item.DeactivatedAfterEdit;
|
||||
|
||||
Im.Item.SetNextWidthScaled(200);
|
||||
ret |= _combineCombo.Draw("Combine Operation"u8, ref _combineOp, StringU8.Empty, 200 * Im.Style.GlobalScale);
|
||||
var resizeOp = GetActualResizeOp(_resizeOp, _combineOp);
|
||||
using (var dis = ImRaii.Disabled((int)resizeOp < 0))
|
||||
using (Im.Disabled((int)resizeOp < 0))
|
||||
{
|
||||
ret |= ImGuiUtil.GenericEnumCombo("Resizing Mode", 200.0f * UiHelpers.Scale, _resizeOp, out _resizeOp,
|
||||
Enum.GetValues<ResizeOp>().Where(op => (int)op >= 0), op => ResizeOpLabels[(int)op]);
|
||||
ret |= _resizeCombo.Draw("Resizing Mode"u8, ref _resizeOp, StringU8.Empty, 200 * Im.Style.GlobalScale);
|
||||
}
|
||||
|
||||
using (var dis = ImRaii.Disabled(_combineOp != CombineOp.CopyChannels))
|
||||
using (Im.Disabled(_combineOp != CombineOp.CopyChannels))
|
||||
{
|
||||
ImGui.TextUnformatted("Copy");
|
||||
Im.Text("Copy"u8);
|
||||
foreach (var channel in Enum.GetValues<Channels>())
|
||||
{
|
||||
Im.Line.Same();
|
||||
var copy = (_copyChannels & channel) != 0;
|
||||
if (ImGui.Checkbox(channel.ToString(), ref copy))
|
||||
if (Im.Checkbox(channel.ToString(), ref copy))
|
||||
{
|
||||
_copyChannels = copy ? _copyChannels | channel : _copyChannels & ~channel;
|
||||
ret = true;
|
||||
|
|
@ -290,62 +301,52 @@ public partial class CombinedTexture
|
|||
|
||||
private static bool DrawMatrixInput(ref Matrix4x4 multiplier, ref Vector4 constant, float width)
|
||||
{
|
||||
using var table = ImRaii.Table(string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit);
|
||||
using var table = Im.Table.Begin(StringU8.Empty, 5, TableFlags.BordersInner | TableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return false;
|
||||
|
||||
var changes = false;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center("R");
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center("G");
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center("B");
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center("A");
|
||||
table.NextColumn();
|
||||
table.NextColumn();
|
||||
ImEx.TextCentered("R"u8);
|
||||
table.NextColumn();
|
||||
ImEx.TextCentered("G"u8);
|
||||
table.NextColumn();
|
||||
ImEx.TextCentered("B"u8);
|
||||
table.NextColumn();
|
||||
ImEx.TextCentered("A"u8);
|
||||
|
||||
var inputWidth = width / 6;
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text("R ");
|
||||
changes |= DragFloat("##RR", inputWidth, ref multiplier.M11);
|
||||
changes |= DragFloat("##RG", inputWidth, ref multiplier.M12);
|
||||
changes |= DragFloat("##RB", inputWidth, ref multiplier.M13);
|
||||
changes |= DragFloat("##RA", inputWidth, ref multiplier.M14);
|
||||
table.DrawFrameColumn("R "u8);
|
||||
changes |= DragFloat("##RR"u8, inputWidth, ref multiplier.M11);
|
||||
changes |= DragFloat("##RG"u8, inputWidth, ref multiplier.M12);
|
||||
changes |= DragFloat("##RB"u8, inputWidth, ref multiplier.M13);
|
||||
changes |= DragFloat("##RA"u8, inputWidth, ref multiplier.M14);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text("G ");
|
||||
changes |= DragFloat("##GR", inputWidth, ref multiplier.M21);
|
||||
changes |= DragFloat("##GG", inputWidth, ref multiplier.M22);
|
||||
changes |= DragFloat("##GB", inputWidth, ref multiplier.M23);
|
||||
changes |= DragFloat("##GA", inputWidth, ref multiplier.M24);
|
||||
table.DrawFrameColumn("G "u8);
|
||||
changes |= DragFloat("##GR"u8, inputWidth, ref multiplier.M21);
|
||||
changes |= DragFloat("##GG"u8, inputWidth, ref multiplier.M22);
|
||||
changes |= DragFloat("##GB"u8, inputWidth, ref multiplier.M23);
|
||||
changes |= DragFloat("##GA"u8, inputWidth, ref multiplier.M24);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text("B ");
|
||||
changes |= DragFloat("##BR", inputWidth, ref multiplier.M31);
|
||||
changes |= DragFloat("##BG", inputWidth, ref multiplier.M32);
|
||||
changes |= DragFloat("##BB", inputWidth, ref multiplier.M33);
|
||||
changes |= DragFloat("##BA", inputWidth, ref multiplier.M34);
|
||||
table.DrawFrameColumn("B "u8);
|
||||
changes |= DragFloat("##BR"u8, inputWidth, ref multiplier.M31);
|
||||
changes |= DragFloat("##BG"u8, inputWidth, ref multiplier.M32);
|
||||
changes |= DragFloat("##BB"u8, inputWidth, ref multiplier.M33);
|
||||
changes |= DragFloat("##BA"u8, inputWidth, ref multiplier.M34);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text("A ");
|
||||
changes |= DragFloat("##AR", inputWidth, ref multiplier.M41);
|
||||
changes |= DragFloat("##AG", inputWidth, ref multiplier.M42);
|
||||
changes |= DragFloat("##AB", inputWidth, ref multiplier.M43);
|
||||
changes |= DragFloat("##AA", inputWidth, ref multiplier.M44);
|
||||
table.DrawFrameColumn("A "u8);
|
||||
changes |= DragFloat("##AR"u8, inputWidth, ref multiplier.M41);
|
||||
changes |= DragFloat("##AG"u8, inputWidth, ref multiplier.M42);
|
||||
changes |= DragFloat("##AB"u8, inputWidth, ref multiplier.M43);
|
||||
changes |= DragFloat("##AA"u8, inputWidth, ref multiplier.M44);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text("1 ");
|
||||
changes |= DragFloat("##1R", inputWidth, ref constant.X);
|
||||
changes |= DragFloat("##1G", inputWidth, ref constant.Y);
|
||||
changes |= DragFloat("##1B", inputWidth, ref constant.Z);
|
||||
changes |= DragFloat("##1A", inputWidth, ref constant.W);
|
||||
table.DrawFrameColumn("1 "u8);
|
||||
changes |= DragFloat("##1R"u8, inputWidth, ref constant.X);
|
||||
changes |= DragFloat("##1G"u8, inputWidth, ref constant.Y);
|
||||
changes |= DragFloat("##1B"u8, inputWidth, ref constant.Z);
|
||||
changes |= DragFloat("##1A"u8, inputWidth, ref constant.W);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
|
@ -354,28 +355,28 @@ public partial class CombinedTexture
|
|||
{
|
||||
var changes = PresetCombo(ref multiplier, ref constant);
|
||||
Im.Line.Same();
|
||||
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
|
||||
Im.ScaledDummy(20);
|
||||
Im.Line.Same();
|
||||
ImGui.TextUnformatted("Invert");
|
||||
Im.Text("Invert"u8);
|
||||
Im.Line.Same();
|
||||
|
||||
Channels channels = 0;
|
||||
if (ImGui.Button("Colors"))
|
||||
if (Im.Button("Colors"u8))
|
||||
channels |= Channels.Red | Channels.Green | Channels.Blue;
|
||||
Im.Line.Same();
|
||||
if (ImGui.Button("R"))
|
||||
if (Im.Button("R"u8))
|
||||
channels |= Channels.Red;
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImGui.Button("G"))
|
||||
if (Im.Button("G"u8))
|
||||
channels |= Channels.Green;
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImGui.Button("B"))
|
||||
if (Im.Button("B"u8))
|
||||
channels |= Channels.Blue;
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImGui.Button("A"))
|
||||
if (Im.Button("A"u8))
|
||||
channels |= Channels.Alpha;
|
||||
|
||||
changes |= InvertChannels(channels, ref multiplier, ref constant);
|
||||
|
|
@ -384,14 +385,14 @@ public partial class CombinedTexture
|
|||
|
||||
private static bool PresetCombo(ref Matrix4x4 multiplier, ref Vector4 constant)
|
||||
{
|
||||
using var combo = ImRaii.Combo("Presets", string.Empty, ImGuiComboFlags.NoPreview);
|
||||
using var combo = Im.Combo.Begin("Presets"u8, StringU8.Empty, ComboFlags.NoPreview);
|
||||
if (!combo)
|
||||
return false;
|
||||
|
||||
var ret = false;
|
||||
foreach (var (label, preMultiplier, preConstant) in PredefinedColorTransforms)
|
||||
{
|
||||
if (!ImGui.Selectable(label, multiplier == preMultiplier && constant == preConstant))
|
||||
if (!Im.Selectable(label, multiplier == preMultiplier && constant == preConstant))
|
||||
continue;
|
||||
|
||||
multiplier = preMultiplier;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,54 @@
|
|||
using Luna.Generators;
|
||||
|
||||
namespace Penumbra.Import.Textures;
|
||||
|
||||
public partial class CombinedTexture
|
||||
{
|
||||
private enum CombineOp
|
||||
[NamedEnum("ToLabel")]
|
||||
[TooltipEnum]
|
||||
public enum CombineOp
|
||||
{
|
||||
LeftMultiply = -4,
|
||||
LeftCopy = -3,
|
||||
RightCopy = -2,
|
||||
Invalid = -1,
|
||||
Over = 0,
|
||||
Under = 1,
|
||||
LeftMultiply = -4,
|
||||
LeftCopy = -3,
|
||||
RightCopy = -2,
|
||||
Invalid = -1,
|
||||
|
||||
[Name("Overlay over Input")]
|
||||
[Tooltip("Standard composition.\nApply the overlay over the input.")]
|
||||
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,
|
||||
|
||||
[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,
|
||||
CopyChannels = 3,
|
||||
|
||||
[Name("Copy Channels")]
|
||||
[Tooltip("Replace some input channels with those from the overlay.\nUseful for Multi maps.")]
|
||||
CopyChannels = 3,
|
||||
}
|
||||
|
||||
private enum ResizeOp
|
||||
[NamedEnum("ToLabel")]
|
||||
public enum ResizeOp
|
||||
{
|
||||
LeftOnly = -2,
|
||||
RightOnly = -1,
|
||||
None = 0,
|
||||
ToLeft = 1,
|
||||
ToRight = 2,
|
||||
|
||||
[Name("No Resizing")]
|
||||
None = 0,
|
||||
|
||||
[Name("Adjust Overlay to Input")]
|
||||
ToLeft = 1,
|
||||
|
||||
[Name("Adjust Input to Overlay")]
|
||||
ToRight = 2,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum Channels : byte
|
||||
[NamedEnum]
|
||||
public enum Channels : byte
|
||||
{
|
||||
Red = 1,
|
||||
Green = 2,
|
||||
|
|
@ -32,29 +56,6 @@ public partial class CombinedTexture
|
|||
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)
|
||||
=> combineOp switch
|
||||
{
|
||||
|
|
|
|||
80
Penumbra/Import/Textures/PathSelectCombo.cs
Normal file
80
Penumbra/Import/Textures/PathSelectCombo.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using ImSharp;
|
||||
using Lumina.Data.Files;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
using Luna;
|
||||
using OtterTex;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.UI;
|
||||
using Penumbra.UI.Classes;
|
||||
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
|
||||
using VectorExtensions = Luna.VectorExtensions;
|
||||
|
||||
namespace Penumbra.Import.Textures;
|
||||
|
||||
|
|
@ -20,44 +14,42 @@ public static class TextureDrawer
|
|||
{
|
||||
if (texture.TextureWrap != null)
|
||||
{
|
||||
size = texture.TextureWrap.Size.Contain(size);
|
||||
size = VectorExtensions.Contain(texture.TextureWrap.Size, size);
|
||||
|
||||
ImGui.Image(texture.TextureWrap.Handle, size);
|
||||
Im.Image.Draw(texture.TextureWrap.Id(), size);
|
||||
DrawData(texture);
|
||||
}
|
||||
else if (texture.LoadError != null)
|
||||
{
|
||||
const string link = "https://aka.ms/vcredist";
|
||||
ImGui.TextUnformatted("Could not load file:");
|
||||
Im.Text("Could not load file:"u8);
|
||||
|
||||
if (texture.LoadError is DllNotFoundException)
|
||||
{
|
||||
ImGuiUtil.TextColored(Colors.RegexWarningBorder,
|
||||
"A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable.");
|
||||
if (ImGui.Button("Microsoft VC Redistributables"))
|
||||
Im.Text("A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable."u8,
|
||||
Colors.RegexWarningBorder);
|
||||
if (Im.Button("Microsoft VC Redistributables"u8))
|
||||
Dalamud.Utility.Util.OpenLink(link);
|
||||
ImGuiUtil.HoverTooltip($"Open {link} in your browser.");
|
||||
Im.Tooltip.OnHover($"Open {link} in your browser.");
|
||||
}
|
||||
|
||||
ImGuiUtil.TextColored(Colors.RegexWarningBorder, texture.LoadError.ToString());
|
||||
Im.Text($"{texture.LoadError}", Colors.RegexWarningBorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PathInputBox(TextureManager textures, Texture current, ref string? tmpPath, string label, string hint, string tooltip,
|
||||
public static void PathInputBox(TextureManager textures, Texture current, ref string? tmpPath, ReadOnlySpan<byte> label,
|
||||
ReadOnlySpan<byte> hint, ReadOnlySpan<byte> tooltip,
|
||||
string startPath, FileDialogService fileDialog, string defaultModImportPath)
|
||||
{
|
||||
tmpPath ??= current.Path;
|
||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2(UiHelpers.ScaleX3, ImGui.GetStyle().ItemSpacing.Y));
|
||||
ImGui.SetNextItemWidth(-2 * ImGui.GetFrameHeight() - 7 * UiHelpers.Scale);
|
||||
ImGui.InputTextWithHint(label, hint, ref tmpPath, Utf8GamePath.MaxGamePathLength);
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
using var spacing = ImStyleDouble.ItemSpacing.PushX(UiHelpers.ScaleX3);
|
||||
Im.Item.SetNextWidth(-2 * Im.Style.FrameHeight - 7 * Im.Style.GlobalScale);
|
||||
if (ImEx.InputOnDeactivation.Text(label, tmpPath, out tmpPath, hint))
|
||||
current.Load(textures, tmpPath);
|
||||
|
||||
ImGuiUtil.HoverTooltip(tooltip);
|
||||
Im.Tooltip.OnHover(tooltip);
|
||||
Im.Line.Same();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Folder.ToIconString(), new Vector2(ImGui.GetFrameHeight()), string.Empty, false,
|
||||
true))
|
||||
if (ImEx.Icon.Button(LunaStyle.FolderIcon))
|
||||
{
|
||||
if (defaultModImportPath.Length > 0)
|
||||
startPath = defaultModImportPath;
|
||||
|
|
@ -72,97 +64,41 @@ public static class TextureDrawer
|
|||
}
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Recycle.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
"Reload the currently selected path.", false,
|
||||
true))
|
||||
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Reload the currently selected path."u8))
|
||||
current.Reload(textures);
|
||||
}
|
||||
|
||||
private static void DrawData(Texture texture)
|
||||
{
|
||||
using var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
ImGuiUtil.DrawTableColumn("Width");
|
||||
ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Width.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Height");
|
||||
ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Height.ToString());
|
||||
ImGuiUtil.DrawTableColumn("File Type");
|
||||
ImGuiUtil.DrawTableColumn(texture.Type.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Bitmap Size");
|
||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)");
|
||||
using var table = Im.Table.Begin("##data"u8, 2, TableFlags.SizingFixedFit);
|
||||
table.DrawColumn("Width"u8);
|
||||
table.DrawColumn($"{texture.TextureWrap!.Width}");
|
||||
table.DrawColumn("Height"u8);
|
||||
table.DrawColumn($"{texture.TextureWrap!.Height}");
|
||||
table.DrawColumn("File Type"u8);
|
||||
table.DrawColumn($"{texture.Type}");
|
||||
table.DrawColumn("Bitmap Size"u8);
|
||||
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)");
|
||||
switch (texture.BaseImage.Image)
|
||||
{
|
||||
case ScratchImage s:
|
||||
ImGuiUtil.DrawTableColumn("Format");
|
||||
ImGuiUtil.DrawTableColumn(s.Meta.Format.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Mip Levels");
|
||||
ImGuiUtil.DrawTableColumn(s.Meta.MipLevels.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Data Size");
|
||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)");
|
||||
ImGuiUtil.DrawTableColumn("Number of Images");
|
||||
ImGuiUtil.DrawTableColumn(s.Images.Length.ToString());
|
||||
table.DrawColumn("Format"u8);
|
||||
table.DrawColumn($"{s.Meta.Format}");
|
||||
table.DrawColumn("Mip Levels"u8);
|
||||
table.DrawColumn($"{s.Meta.MipLevels}");
|
||||
table.DrawColumn("Data Size"u8);
|
||||
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)");
|
||||
table.DrawColumn("Number of Images"u8);
|
||||
table.DrawColumn($"{s.Images.Length}");
|
||||
break;
|
||||
case TexFile t:
|
||||
ImGuiUtil.DrawTableColumn("Format");
|
||||
ImGuiUtil.DrawTableColumn(t.Header.Format.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Mip Levels");
|
||||
ImGuiUtil.DrawTableColumn(t.Header.MipCount.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Data Size");
|
||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(t.ImageData.Length)} ({t.ImageData.Length} Bytes)");
|
||||
table.DrawColumn("Format"u8);
|
||||
table.DrawColumn($"{t.Header.Format}");
|
||||
table.DrawColumn("Mip Levels"u8);
|
||||
table.DrawColumn($"{t.Header.MipCount}");
|
||||
table.DrawColumn("Data Size"u8);
|
||||
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(t.ImageData.Length)} ({t.ImageData.Length} Bytes)");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using OtterGui.Services;
|
||||
using Luna;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.CrashHandler.Buffers;
|
||||
using Penumbra.GameData;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using OtterGui.Services;
|
||||
using Luna;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
|
||||
|
|
@ -12,9 +12,9 @@ public sealed unsafe class PlayFootstep : FastHook<PlayFootstep.Delegate>
|
|||
|
||||
public PlayFootstep(HookManager hooks, GameState state, CollectionResolver collectionResolver)
|
||||
{
|
||||
_state = state;
|
||||
_state = state;
|
||||
_collectionResolver = collectionResolver;
|
||||
Task = hooks.CreateHook<Delegate>("Play Footstep", Sigs.FootStepSound, Detour, !HookOverrides.Instance.Animation.PlayFootstep);
|
||||
Task = hooks.CreateHook<Delegate>("Play Footstep", Sigs.FootStepSound, Detour, !HookOverrides.Instance.Animation.PlayFootstep);
|
||||
}
|
||||
|
||||
public delegate void Delegate(GameObject* gameObject, int id, int unk);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,16 @@ namespace Penumbra.Interop.ResourceTree;
|
|||
|
||||
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(
|
||||
IEnumerable<(ICharacter, ResourceTree)> resourceTrees)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@
|
|||
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
|
||||
|
||||
<ProjectReference Include="..\Luna\Luna.Generators\Luna.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using ImSharp;
|
||||
using Lumina.Data;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
|
|
@ -20,7 +18,7 @@ public partial class ModEditWindow
|
|||
private readonly ResourceTreeViewer _quickImportViewer;
|
||||
private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new();
|
||||
|
||||
private HashSet<string> GetPlayerResourcesOfType(ResourceType type)
|
||||
public HashSet<string> GetPlayerResourcesOfType(ResourceType type)
|
||||
{
|
||||
var resources = ResourceTreeApiHelper
|
||||
.GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type)
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ public partial class ModEditWindow
|
|||
{
|
||||
private readonly TextureManager _textures;
|
||||
|
||||
private readonly Texture _left = new();
|
||||
private readonly Texture _right = new();
|
||||
private readonly CombinedTexture _center;
|
||||
private readonly TextureDrawer.PathSelectCombo _textureSelectCombo;
|
||||
private readonly Texture _left = new();
|
||||
private readonly Texture _right = new();
|
||||
private readonly CombinedTexture _center;
|
||||
private readonly TextureSelectCombo _textureSelectCombo;
|
||||
|
||||
private bool _overlayCollapsed = true;
|
||||
private bool _addMipMaps = true;
|
||||
|
|
@ -49,13 +49,13 @@ public partial class ModEditWindow
|
|||
return;
|
||||
|
||||
using var id = ImRaii.PushId(label);
|
||||
ImEx.TextFramed(label, new Vector2(-1, 0), ImGuiColor.FrameBackground.Get());
|
||||
ImEx.TextFramed(label, Im.ContentRegion.Available with { Y = 0 }, ImGuiColor.FrameBackground.Get());
|
||||
ImGui.NewLine();
|
||||
|
||||
using (ImRaii.Disabled(!_center.SaveTask.IsCompleted))
|
||||
{
|
||||
TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input", "Import Image...",
|
||||
"Can import game paths as well as your own files.", Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath);
|
||||
TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input"u8, "Import Image..."u8,
|
||||
"Can import game paths as well as your own files."u8, Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath);
|
||||
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,
|
||||
Mod.ModPath.FullName.Length + 1, out var newPath)
|
||||
|
|
@ -200,7 +200,6 @@ public partial class ModEditWindow
|
|||
case TaskStatus.WaitingToRun:
|
||||
case TaskStatus.Running:
|
||||
ImGuiUtil.DrawTextButton("Computing...", -Vector2.UnitX, Colors.PressEnterWarningBg);
|
||||
|
||||
break;
|
||||
case TaskStatus.Canceled:
|
||||
case TaskStatus.Faulted:
|
||||
|
|
@ -210,9 +209,7 @@ public partial class ModEditWindow
|
|||
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight()));
|
||||
break;
|
||||
default: ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight())); break;
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
|
|
|
|||
|
|
@ -656,7 +656,7 @@ public partial class ModEditWindow : IndexedWindow, IDisposable
|
|||
() => Mod?.ModPath.FullName ?? string.Empty,
|
||||
(bytes, path, _) => new PbdTab(bytes, path));
|
||||
_center = new CombinedTexture(_left, _right);
|
||||
_textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex));
|
||||
_textureSelectCombo = new TextureSelectCombo(resourceTreeFactory, editor, gameData);
|
||||
_resourceTreeFactory = resourceTreeFactory;
|
||||
_quickImportViewer = resourceTreeViewerFactory.Create(1, OnQuickImportRefresh, DrawQuickImportActions);
|
||||
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModEditWindow);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class ResourceTreeViewerFactory(
|
|||
PcpService pcpService,
|
||||
IDataManager gameData,
|
||||
FileDialogService fileDialog,
|
||||
FileCompactor compactor) : Luna.IService
|
||||
FileCompactor compactor) : IService
|
||||
{
|
||||
public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action<ResourceNode, IWritable?, Vector2> drawActions)
|
||||
=> new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator, pcpService, gameData,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue