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 Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using OtterGui.Filesystem;
|
using Luna;
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using OtterGui.Filesystem;
|
using Luna;
|
||||||
|
|
||||||
namespace Penumbra.Collections;
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
using OtterGui.Log;
|
using Luna;
|
||||||
|
|
||||||
namespace Penumbra.Import.Models;
|
namespace Penumbra.Import.Models;
|
||||||
|
|
||||||
public record class IoNotifier
|
public record IoNotifier(Logger Log)
|
||||||
{
|
{
|
||||||
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 class IoNotifier
|
||||||
private void SendMessage(string message, Logger.LogLevel type)
|
private void SendMessage(string message, Logger.LogLevel type)
|
||||||
{
|
{
|
||||||
var fullText = $"{_context}{message}";
|
var fullText = $"{_context}{message}";
|
||||||
Penumbra.Log.Message(type, fullText);
|
Log.Message(type, fullText);
|
||||||
_messages.Add(fullText);
|
_messages.Add(fullText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
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;
|
||||||
|
|
@ -22,9 +23,15 @@ 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(IFramework framework, MetaFileManager metaFileManager, ActiveCollections collections, GamePathParser parser)
|
public sealed class ModelManager(
|
||||||
: SingleTaskQueue, IDisposable, Luna.IService
|
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 IFramework _framework = framework;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<IAction, (Task, CancellationTokenSource)> _tasks = new();
|
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)
|
public Task<(MdlFile?, IoNotifier)> ImportGltf(string inputPath)
|
||||||
=> EnqueueWithResult(
|
=> EnqueueWithResult(
|
||||||
new ImportGltfAction(inputPath),
|
new ImportGltfAction(this, inputPath),
|
||||||
action => (action.Out, action.Notifier)
|
action => (action.Out, action.Notifier)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -88,8 +95,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
{
|
{
|
||||||
// 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(
|
.FirstOrNull(est => est.Key.GenderRace == info.GenderRace
|
||||||
est => est.Key.GenderRace == info.GenderRace
|
|
||||||
&& est.Key.Slot == type
|
&& est.Key.Slot == type
|
||||||
&& est.Key.SetId == info.PrimaryId
|
&& est.Key.SetId == info.PrimaryId
|
||||||
);
|
);
|
||||||
|
|
@ -190,7 +196,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
string outputPath)
|
string outputPath)
|
||||||
: IAction
|
: IAction
|
||||||
{
|
{
|
||||||
public readonly IoNotifier Notifier = new();
|
public readonly IoNotifier Notifier = new(manager.Log);
|
||||||
|
|
||||||
public void Execute(CancellationToken cancel)
|
public void Execute(CancellationToken cancel)
|
||||||
{
|
{
|
||||||
|
|
@ -292,7 +298,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
|
|
||||||
public bool Equals(IAction? other)
|
public bool Equals(IAction? other)
|
||||||
{
|
{
|
||||||
if (other is not ExportToGltfAction rhs)
|
if (other is not ExportToGltfAction)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// TODO: compare configuration and such
|
// 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 MdlFile? Out;
|
||||||
public readonly IoNotifier Notifier = new();
|
public readonly IoNotifier Notifier = new(manager.Log);
|
||||||
|
|
||||||
public void Execute(CancellationToken cancel)
|
public void Execute(CancellationToken cancel)
|
||||||
{
|
{
|
||||||
|
|
@ -314,7 +320,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
|
|
||||||
public bool Equals(IAction? other)
|
public bool Equals(IAction? other)
|
||||||
{
|
{
|
||||||
if (other is not ImportGltfAction rhs)
|
if (other is not ImportGltfAction)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
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;
|
||||||
|
|
@ -16,7 +15,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,
|
||||||
|
|
@ -79,7 +78,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>()?.RemoveInvalidPathSymbols() ?? string.Empty;
|
name = obj[nameof(Mod.Name)]?.Value<string>()?.RemoveInvalidFileNameSymbols() ?? 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.");
|
||||||
|
|
||||||
|
|
@ -142,16 +141,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using OtterGui.Raii;
|
|
||||||
using OtterGui;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using ImSharp;
|
using ImSharp;
|
||||||
using Penumbra.UI;
|
using OtterGui.Text;
|
||||||
using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
|
using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
|
||||||
|
|
||||||
namespace Penumbra.Import.Textures;
|
namespace Penumbra.Import.Textures;
|
||||||
|
|
@ -29,20 +25,19 @@ public partial class CombinedTexture
|
||||||
private const float BWeight = 0.0722f;
|
private const float BWeight = 0.0722f;
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
private static readonly IReadOnlyList<(string Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms =
|
private static readonly IReadOnlyList<(StringU8 Label, Matrix4x4 Multiplier, Vector4 Constant)> PredefinedColorTransforms =
|
||||||
new[]
|
[
|
||||||
{
|
(new StringU8("No Transform (Identity)"u8), Matrix4x4.Identity, Vector4.Zero ),
|
||||||
("No Transform (Identity)", 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 ),
|
||||||
("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 (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 (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 (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 (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("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 (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("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 ),
|
||||||
("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 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 ),
|
||||||
("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 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 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 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 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 ),
|
(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 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)
|
||||||
|
|
@ -211,15 +206,15 @@ public partial class CombinedTexture
|
||||||
return transformed;
|
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;
|
var tmp = value;
|
||||||
ImGui.TableNextColumn();
|
Im.Table.NextColumn();
|
||||||
ImGui.SetNextItemWidth(width);
|
Im.Item.SetNextWidth(width);
|
||||||
if (ImGui.DragFloat(label, ref tmp, 0.001f, -1f, 1f))
|
if (Im.Drag(label, ref tmp, speed: 0.001f, min: -1f, max: 1f))
|
||||||
value = tmp;
|
value = tmp;
|
||||||
|
|
||||||
return ImGui.IsItemDeactivatedAfterEdit();
|
return Im.Item.DeactivatedAfterEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawMatrixInputLeft(float width)
|
public void DrawMatrixInputLeft(float width)
|
||||||
|
|
@ -230,53 +225,69 @@ 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);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
|
Im.Item.SetNextWidthScaled(75);
|
||||||
ImGui.DragInt("##XOffset", ref _offsetX, 0.5f);
|
Im.Drag("##XOffset"u8, ref _offsetX, speed: 0.5f);
|
||||||
ret |= ImGui.IsItemDeactivatedAfterEdit();
|
ret |= Im.Item.DeactivatedAfterEdit;
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
|
Im.Item.SetNextWidthScaled(75);
|
||||||
ImGui.DragInt("Offsets##YOffset", ref _offsetY, 0.5f);
|
Im.Drag("Offsets##YOffset"u8, ref _offsetY, speed: 0.5f);
|
||||||
ret |= ImGui.IsItemDeactivatedAfterEdit();
|
ret |= Im.Item.DeactivatedAfterEdit;
|
||||||
|
|
||||||
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(200);
|
||||||
|
ret |= _combineCombo.Draw("Combine Operation"u8, ref _combineOp, StringU8.Empty, 200 * Im.Style.GlobalScale);
|
||||||
var resizeOp = GetActualResizeOp(_resizeOp, _combineOp);
|
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,
|
ret |= _resizeCombo.Draw("Resizing Mode"u8, ref _resizeOp, StringU8.Empty, 200 * Im.Style.GlobalScale);
|
||||||
Enum.GetValues<ResizeOp>().Where(op => (int)op >= 0), op => ResizeOpLabels[(int)op]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>())
|
foreach (var channel in Enum.GetValues<Channels>())
|
||||||
{
|
{
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
var copy = (_copyChannels & channel) != 0;
|
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;
|
_copyChannels = copy ? _copyChannels | channel : _copyChannels & ~channel;
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
@ -290,62 +301,52 @@ 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 = ImRaii.Table(string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit);
|
using var table = Im.Table.Begin(StringU8.Empty, 5, TableFlags.BordersInner | TableFlags.SizingFixedFit);
|
||||||
if (!table)
|
if (!table)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var changes = false;
|
var changes = false;
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
table.NextColumn();
|
||||||
ImGui.TableNextColumn();
|
table.NextColumn();
|
||||||
ImGuiUtil.Center("R");
|
ImEx.TextCentered("R"u8);
|
||||||
ImGui.TableNextColumn();
|
table.NextColumn();
|
||||||
ImGuiUtil.Center("G");
|
ImEx.TextCentered("G"u8);
|
||||||
ImGui.TableNextColumn();
|
table.NextColumn();
|
||||||
ImGuiUtil.Center("B");
|
ImEx.TextCentered("B"u8);
|
||||||
ImGui.TableNextColumn();
|
table.NextColumn();
|
||||||
ImGuiUtil.Center("A");
|
ImEx.TextCentered("A"u8);
|
||||||
|
|
||||||
var inputWidth = width / 6;
|
var inputWidth = width / 6;
|
||||||
ImGui.TableNextColumn();
|
table.DrawFrameColumn("R "u8);
|
||||||
ImGui.AlignTextToFramePadding();
|
changes |= DragFloat("##RR"u8, inputWidth, ref multiplier.M11);
|
||||||
ImGui.Text("R ");
|
changes |= DragFloat("##RG"u8, inputWidth, ref multiplier.M12);
|
||||||
changes |= DragFloat("##RR", inputWidth, ref multiplier.M11);
|
changes |= DragFloat("##RB"u8, inputWidth, ref multiplier.M13);
|
||||||
changes |= DragFloat("##RG", inputWidth, ref multiplier.M12);
|
changes |= DragFloat("##RA"u8, inputWidth, ref multiplier.M14);
|
||||||
changes |= DragFloat("##RB", inputWidth, ref multiplier.M13);
|
|
||||||
changes |= DragFloat("##RA", inputWidth, ref multiplier.M14);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
table.DrawFrameColumn("G "u8);
|
||||||
ImGui.AlignTextToFramePadding();
|
changes |= DragFloat("##GR"u8, inputWidth, ref multiplier.M21);
|
||||||
ImGui.Text("G ");
|
changes |= DragFloat("##GG"u8, inputWidth, ref multiplier.M22);
|
||||||
changes |= DragFloat("##GR", inputWidth, ref multiplier.M21);
|
changes |= DragFloat("##GB"u8, inputWidth, ref multiplier.M23);
|
||||||
changes |= DragFloat("##GG", inputWidth, ref multiplier.M22);
|
changes |= DragFloat("##GA"u8, inputWidth, ref multiplier.M24);
|
||||||
changes |= DragFloat("##GB", inputWidth, ref multiplier.M23);
|
|
||||||
changes |= DragFloat("##GA", inputWidth, ref multiplier.M24);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
table.DrawFrameColumn("B "u8);
|
||||||
ImGui.AlignTextToFramePadding();
|
changes |= DragFloat("##BR"u8, inputWidth, ref multiplier.M31);
|
||||||
ImGui.Text("B ");
|
changes |= DragFloat("##BG"u8, inputWidth, ref multiplier.M32);
|
||||||
changes |= DragFloat("##BR", inputWidth, ref multiplier.M31);
|
changes |= DragFloat("##BB"u8, inputWidth, ref multiplier.M33);
|
||||||
changes |= DragFloat("##BG", inputWidth, ref multiplier.M32);
|
changes |= DragFloat("##BA"u8, inputWidth, ref multiplier.M34);
|
||||||
changes |= DragFloat("##BB", inputWidth, ref multiplier.M33);
|
|
||||||
changes |= DragFloat("##BA", inputWidth, ref multiplier.M34);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
table.DrawFrameColumn("A "u8);
|
||||||
ImGui.AlignTextToFramePadding();
|
changes |= DragFloat("##AR"u8, inputWidth, ref multiplier.M41);
|
||||||
ImGui.Text("A ");
|
changes |= DragFloat("##AG"u8, inputWidth, ref multiplier.M42);
|
||||||
changes |= DragFloat("##AR", inputWidth, ref multiplier.M41);
|
changes |= DragFloat("##AB"u8, inputWidth, ref multiplier.M43);
|
||||||
changes |= DragFloat("##AG", inputWidth, ref multiplier.M42);
|
changes |= DragFloat("##AA"u8, inputWidth, ref multiplier.M44);
|
||||||
changes |= DragFloat("##AB", inputWidth, ref multiplier.M43);
|
|
||||||
changes |= DragFloat("##AA", inputWidth, ref multiplier.M44);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
table.DrawFrameColumn("1 "u8);
|
||||||
ImGui.AlignTextToFramePadding();
|
changes |= DragFloat("##1R"u8, inputWidth, ref constant.X);
|
||||||
ImGui.Text("1 ");
|
changes |= DragFloat("##1G"u8, inputWidth, ref constant.Y);
|
||||||
changes |= DragFloat("##1R", inputWidth, ref constant.X);
|
changes |= DragFloat("##1B"u8, inputWidth, ref constant.Z);
|
||||||
changes |= DragFloat("##1G", inputWidth, ref constant.Y);
|
changes |= DragFloat("##1A"u8, inputWidth, ref constant.W);
|
||||||
changes |= DragFloat("##1B", inputWidth, ref constant.Z);
|
|
||||||
changes |= DragFloat("##1A", inputWidth, ref constant.W);
|
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
@ -354,28 +355,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();
|
||||||
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
|
Im.ScaledDummy(20);
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
ImGui.TextUnformatted("Invert");
|
Im.Text("Invert"u8);
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
|
|
||||||
Channels channels = 0;
|
Channels channels = 0;
|
||||||
if (ImGui.Button("Colors"))
|
if (Im.Button("Colors"u8))
|
||||||
channels |= Channels.Red | Channels.Green | Channels.Blue;
|
channels |= Channels.Red | Channels.Green | Channels.Blue;
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGui.Button("R"))
|
if (Im.Button("R"u8))
|
||||||
channels |= Channels.Red;
|
channels |= Channels.Red;
|
||||||
|
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGui.Button("G"))
|
if (Im.Button("G"u8))
|
||||||
channels |= Channels.Green;
|
channels |= Channels.Green;
|
||||||
|
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGui.Button("B"))
|
if (Im.Button("B"u8))
|
||||||
channels |= Channels.Blue;
|
channels |= Channels.Blue;
|
||||||
|
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGui.Button("A"))
|
if (Im.Button("A"u8))
|
||||||
channels |= Channels.Alpha;
|
channels |= Channels.Alpha;
|
||||||
|
|
||||||
changes |= InvertChannels(channels, ref multiplier, ref constant);
|
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)
|
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)
|
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 (!ImGui.Selectable(label, multiplier == preMultiplier && constant == preConstant))
|
if (!Im.Selectable(label, multiplier == preMultiplier && constant == preConstant))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
multiplier = preMultiplier;
|
multiplier = preMultiplier;
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,54 @@
|
||||||
|
using Luna.Generators;
|
||||||
|
|
||||||
namespace Penumbra.Import.Textures;
|
namespace Penumbra.Import.Textures;
|
||||||
|
|
||||||
public partial class CombinedTexture
|
public partial class CombinedTexture
|
||||||
{
|
{
|
||||||
private enum CombineOp
|
[NamedEnum("ToLabel")]
|
||||||
|
[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,
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ResizeOp
|
[NamedEnum("ToLabel")]
|
||||||
|
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]
|
||||||
private enum Channels : byte
|
[NamedEnum]
|
||||||
|
public enum Channels : byte
|
||||||
{
|
{
|
||||||
Red = 1,
|
Red = 1,
|
||||||
Green = 2,
|
Green = 2,
|
||||||
|
|
@ -32,29 +56,6 @@ 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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
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 ImSharp;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
using OtterGui;
|
using Luna;
|
||||||
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 MouseWheelType = OtterGui.Widgets.MouseWheelType;
|
using VectorExtensions = Luna.VectorExtensions;
|
||||||
|
|
||||||
namespace Penumbra.Import.Textures;
|
namespace Penumbra.Import.Textures;
|
||||||
|
|
||||||
|
|
@ -20,44 +14,42 @@ public static class TextureDrawer
|
||||||
{
|
{
|
||||||
if (texture.TextureWrap != null)
|
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);
|
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";
|
||||||
ImGui.TextUnformatted("Could not load file:");
|
Im.Text("Could not load file:"u8);
|
||||||
|
|
||||||
if (texture.LoadError is DllNotFoundException)
|
if (texture.LoadError is DllNotFoundException)
|
||||||
{
|
{
|
||||||
ImGuiUtil.TextColored(Colors.RegexWarningBorder,
|
Im.Text("A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable."u8,
|
||||||
"A texture handling dependency could not be found. Try installing a current Microsoft VC Redistributable.");
|
Colors.RegexWarningBorder);
|
||||||
if (ImGui.Button("Microsoft VC Redistributables"))
|
if (Im.Button("Microsoft VC Redistributables"u8))
|
||||||
Dalamud.Utility.Util.OpenLink(link);
|
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)
|
string startPath, FileDialogService fileDialog, string defaultModImportPath)
|
||||||
{
|
{
|
||||||
tmpPath ??= current.Path;
|
tmpPath ??= current.Path;
|
||||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
using var spacing = ImStyleDouble.ItemSpacing.PushX(UiHelpers.ScaleX3);
|
||||||
new Vector2(UiHelpers.ScaleX3, ImGui.GetStyle().ItemSpacing.Y));
|
Im.Item.SetNextWidth(-2 * Im.Style.FrameHeight - 7 * Im.Style.GlobalScale);
|
||||||
ImGui.SetNextItemWidth(-2 * ImGui.GetFrameHeight() - 7 * UiHelpers.Scale);
|
if (ImEx.InputOnDeactivation.Text(label, tmpPath, out tmpPath, hint))
|
||||||
ImGui.InputTextWithHint(label, hint, ref tmpPath, Utf8GamePath.MaxGamePathLength);
|
|
||||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
||||||
current.Load(textures, tmpPath);
|
current.Load(textures, tmpPath);
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip(tooltip);
|
Im.Tooltip.OnHover(tooltip);
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Folder.ToIconString(), new Vector2(ImGui.GetFrameHeight()), string.Empty, false,
|
if (ImEx.Icon.Button(LunaStyle.FolderIcon))
|
||||||
true))
|
|
||||||
{
|
{
|
||||||
if (defaultModImportPath.Length > 0)
|
if (defaultModImportPath.Length > 0)
|
||||||
startPath = defaultModImportPath;
|
startPath = defaultModImportPath;
|
||||||
|
|
@ -72,97 +64,41 @@ public static class TextureDrawer
|
||||||
}
|
}
|
||||||
|
|
||||||
Im.Line.Same();
|
Im.Line.Same();
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Recycle.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Reload the currently selected path."u8))
|
||||||
"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 = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit);
|
using var table = Im.Table.Begin("##data"u8, 2, TableFlags.SizingFixedFit);
|
||||||
ImGuiUtil.DrawTableColumn("Width");
|
table.DrawColumn("Width"u8);
|
||||||
ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Width.ToString());
|
table.DrawColumn($"{texture.TextureWrap!.Width}");
|
||||||
ImGuiUtil.DrawTableColumn("Height");
|
table.DrawColumn("Height"u8);
|
||||||
ImGuiUtil.DrawTableColumn(texture.TextureWrap!.Height.ToString());
|
table.DrawColumn($"{texture.TextureWrap!.Height}");
|
||||||
ImGuiUtil.DrawTableColumn("File Type");
|
table.DrawColumn("File Type"u8);
|
||||||
ImGuiUtil.DrawTableColumn(texture.Type.ToString());
|
table.DrawColumn($"{texture.Type}");
|
||||||
ImGuiUtil.DrawTableColumn("Bitmap Size");
|
table.DrawColumn("Bitmap Size"u8);
|
||||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)");
|
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(texture.RgbaPixels.Length)} ({texture.RgbaPixels.Length} Bytes)");
|
||||||
switch (texture.BaseImage.Image)
|
switch (texture.BaseImage.Image)
|
||||||
{
|
{
|
||||||
case ScratchImage s:
|
case ScratchImage s:
|
||||||
ImGuiUtil.DrawTableColumn("Format");
|
table.DrawColumn("Format"u8);
|
||||||
ImGuiUtil.DrawTableColumn(s.Meta.Format.ToString());
|
table.DrawColumn($"{s.Meta.Format}");
|
||||||
ImGuiUtil.DrawTableColumn("Mip Levels");
|
table.DrawColumn("Mip Levels"u8);
|
||||||
ImGuiUtil.DrawTableColumn(s.Meta.MipLevels.ToString());
|
table.DrawColumn($"{s.Meta.MipLevels}");
|
||||||
ImGuiUtil.DrawTableColumn("Data Size");
|
table.DrawColumn("Data Size"u8);
|
||||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)");
|
table.DrawColumn($"{FormattingFunctions.HumanReadableSize(s.Pixels.Length)} ({s.Pixels.Length} Bytes)");
|
||||||
ImGuiUtil.DrawTableColumn("Number of Images");
|
table.DrawColumn("Number of Images"u8);
|
||||||
ImGuiUtil.DrawTableColumn(s.Images.Length.ToString());
|
table.DrawColumn($"{s.Images.Length}");
|
||||||
break;
|
break;
|
||||||
case TexFile t:
|
case TexFile t:
|
||||||
ImGuiUtil.DrawTableColumn("Format");
|
table.DrawColumn("Format"u8);
|
||||||
ImGuiUtil.DrawTableColumn(t.Header.Format.ToString());
|
table.DrawColumn($"{t.Header.Format}");
|
||||||
ImGuiUtil.DrawTableColumn("Mip Levels");
|
table.DrawColumn("Mip Levels"u8);
|
||||||
ImGuiUtil.DrawTableColumn(t.Header.MipCount.ToString());
|
table.DrawColumn($"{t.Header.MipCount}");
|
||||||
ImGuiUtil.DrawTableColumn("Data Size");
|
table.DrawColumn("Data Size"u8);
|
||||||
ImGuiUtil.DrawTableColumn($"{Functions.HumanReadableSize(t.ImageData.Length)} ({t.ImageData.Length} Bytes)");
|
table.DrawColumn($"{FormattingFunctions.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using OtterGui.Services;
|
using Luna;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.CrashHandler.Buffers;
|
using Penumbra.CrashHandler.Buffers;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using OtterGui.Services;
|
using Luna;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,16 @@ 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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,8 @@
|
||||||
<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">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
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;
|
||||||
|
|
@ -20,7 +18,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();
|
||||||
|
|
||||||
private HashSet<string> GetPlayerResourcesOfType(ResourceType type)
|
public 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)
|
||||||
|
|
|
||||||
|
|
@ -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 TextureDrawer.PathSelectCombo _textureSelectCombo;
|
private readonly TextureSelectCombo _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, new Vector2(-1, 0), ImGuiColor.FrameBackground.Get());
|
ImEx.TextFramed(label, Im.ContentRegion.Available with { Y = 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", "Import Image...",
|
TextureDrawer.PathInputBox(_textures, tex, ref tex.TmpPath, "##input"u8, "Import Image..."u8,
|
||||||
"Can import game paths as well as your own files.", Mod!.ModPath.FullName, _fileDialog, _config.DefaultModImportPath);
|
"Can import game paths as well as your own files."u8, 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,7 +200,6 @@ 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:
|
||||||
|
|
@ -210,9 +209,7 @@ public partial class ModEditWindow
|
||||||
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
|
ImGuiUtil.TextWrapped(_center.SaveTask.Exception?.ToString() ?? "Unknown Error");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default: ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight())); break;
|
||||||
ImGui.Dummy(new Vector2(1, ImGui.GetFrameHeight()));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
|
|
||||||
|
|
@ -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 TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex));
|
_textureSelectCombo = new TextureSelectCombo(resourceTreeFactory, editor, gameData);
|
||||||
_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);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ public class ResourceTreeViewerFactory(
|
||||||
PcpService pcpService,
|
PcpService pcpService,
|
||||||
IDataManager gameData,
|
IDataManager gameData,
|
||||||
FileDialogService fileDialog,
|
FileDialogService fileDialog,
|
||||||
FileCompactor compactor) : Luna.IService
|
FileCompactor compactor) : 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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue