test-abel-cake-changes
This commit is contained in:
@@ -39,7 +39,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly LightlessMediator _lightlessMediator;
|
||||
private readonly PairLedger _pairLedger;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, IReadOnlyDictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly DrawEntityFactory _drawEntityFactory;
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||
@@ -944,6 +944,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
|
||||
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
|
||||
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
|
||||
VisiblePairSortMode.EffectiveTriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveTris),
|
||||
VisiblePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
|
||||
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
||||
_ => SortEntries(entryList),
|
||||
|
||||
@@ -326,6 +326,7 @@ public class DrawFolderTag : DrawFolderBase
|
||||
VisiblePairSortMode.VramUsage => "VRAM usage (descending)",
|
||||
VisiblePairSortMode.EffectiveVramUsage => "Effective VRAM usage (descending)",
|
||||
VisiblePairSortMode.TriangleCount => "Triangle count (descending)",
|
||||
VisiblePairSortMode.EffectiveTriangleCount => "Effective triangle count (descending)",
|
||||
VisiblePairSortMode.PreferredDirectPairs => "Preferred permissions & Direct pairs",
|
||||
_ => "Default",
|
||||
};
|
||||
|
||||
@@ -429,6 +429,7 @@ public class DrawUserPair
|
||||
_pair.LastAppliedApproximateVRAMBytes,
|
||||
_pair.LastAppliedApproximateEffectiveVRAMBytes,
|
||||
_pair.LastAppliedDataTris,
|
||||
_pair.LastAppliedApproximateEffectiveTris,
|
||||
_pair.IsPaired,
|
||||
groupDisplays is null ? ImmutableArray<string>.Empty : ImmutableArray.CreateRange(groupDisplays));
|
||||
|
||||
@@ -444,6 +445,8 @@ public class DrawUserPair
|
||||
private static string BuildTooltip(in TooltipSnapshot snapshot)
|
||||
{
|
||||
var builder = new StringBuilder(256);
|
||||
static string FormatTriangles(long count) =>
|
||||
count > 1000 ? (count / 1000d).ToString("0.0'k'") : count.ToString();
|
||||
|
||||
if (snapshot.IsPaused)
|
||||
{
|
||||
@@ -510,9 +513,13 @@ public class DrawUserPair
|
||||
{
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append("Approx. Triangle Count (excl. Vanilla): ");
|
||||
builder.Append(snapshot.LastAppliedDataTris > 1000
|
||||
? (snapshot.LastAppliedDataTris / 1000d).ToString("0.0'k'")
|
||||
: snapshot.LastAppliedDataTris);
|
||||
builder.Append(FormatTriangles(snapshot.LastAppliedDataTris));
|
||||
if (snapshot.LastAppliedApproximateEffectiveTris >= 0)
|
||||
{
|
||||
builder.Append(" (Effective: ");
|
||||
builder.Append(FormatTriangles(snapshot.LastAppliedApproximateEffectiveTris));
|
||||
builder.Append(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,11 +551,12 @@ public class DrawUserPair
|
||||
long LastAppliedApproximateVRAMBytes,
|
||||
long LastAppliedApproximateEffectiveVRAMBytes,
|
||||
long LastAppliedDataTris,
|
||||
long LastAppliedApproximateEffectiveTris,
|
||||
bool IsPaired,
|
||||
ImmutableArray<string> GroupDisplays)
|
||||
{
|
||||
public static TooltipSnapshot Empty { get; } =
|
||||
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
||||
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
||||
}
|
||||
|
||||
private void DrawPairedClientMenu()
|
||||
|
||||
@@ -34,6 +34,8 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
private const float TextureDetailSplitterWidth = 12f;
|
||||
private const float TextureDetailSplitterCollapsedWidth = 18f;
|
||||
private const float SelectedFilePanelLogicalHeight = 90f;
|
||||
private const float TextureHoverPreviewDelaySeconds = 1.75f;
|
||||
private const float TextureHoverPreviewSize = 350f;
|
||||
private static readonly Vector4 SelectedTextureRowTextColor = new(0f, 0f, 0f, 1f);
|
||||
|
||||
private readonly CharacterAnalyzer _characterAnalyzer;
|
||||
@@ -77,6 +79,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
private string _selectedJobEntry = string.Empty;
|
||||
private string _filterGamePath = string.Empty;
|
||||
private string _filterFilePath = string.Empty;
|
||||
private string _textureHoverKey = string.Empty;
|
||||
|
||||
private int _conversionCurrentFileProgress = 0;
|
||||
private int _conversionTotalJobs;
|
||||
@@ -87,6 +90,11 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
private bool _textureRowsDirty = true;
|
||||
private bool _textureDetailCollapsed = false;
|
||||
private bool _conversionFailed;
|
||||
private double _textureHoverStartTime = 0;
|
||||
#if DEBUG
|
||||
private bool _debugCompressionModalOpen = false;
|
||||
private TextureConversionProgress? _debugConversionProgress;
|
||||
#endif
|
||||
private bool _showAlreadyAddedTransients = false;
|
||||
private bool _acknowledgeReview = false;
|
||||
|
||||
@@ -135,21 +143,30 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
|
||||
private void HandleConversionModal()
|
||||
{
|
||||
if (_conversionTask == null)
|
||||
bool hasConversion = _conversionTask != null;
|
||||
#if DEBUG
|
||||
bool showDebug = _debugCompressionModalOpen && !hasConversion;
|
||||
#else
|
||||
const bool showDebug = false;
|
||||
#endif
|
||||
if (!hasConversion && !showDebug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_conversionTask.IsCompleted)
|
||||
if (hasConversion && _conversionTask!.IsCompleted)
|
||||
{
|
||||
ResetConversionModalState();
|
||||
return;
|
||||
if (!showDebug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_showModal = true;
|
||||
if (ImGui.BeginPopupModal("Texture Compression in Progress", ImGuiWindowFlags.AlwaysAutoResize))
|
||||
if (ImGui.BeginPopupModal("Texture Compression in Progress", UiSharedService.PopupWindowFlags))
|
||||
{
|
||||
DrawConversionModalContent();
|
||||
DrawConversionModalContent(showDebug);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
else
|
||||
@@ -164,31 +181,190 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConversionModalContent()
|
||||
private void DrawConversionModalContent(bool isDebugPreview)
|
||||
{
|
||||
var progress = _lastConversionProgress;
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
TextureConversionProgress? progress;
|
||||
#if DEBUG
|
||||
progress = isDebugPreview ? _debugConversionProgress : _lastConversionProgress;
|
||||
#else
|
||||
progress = _lastConversionProgress;
|
||||
#endif
|
||||
var total = progress?.Total ?? Math.Max(_conversionTotalJobs, 1);
|
||||
var completed = progress != null
|
||||
? Math.Min(progress.Completed + 1, total)
|
||||
: _conversionCurrentFileProgress;
|
||||
var currentLabel = !string.IsNullOrEmpty(_conversionCurrentFileName)
|
||||
? _conversionCurrentFileName
|
||||
: "Preparing...";
|
||||
? Math.Clamp(progress.Completed + 1, 0, total)
|
||||
: Math.Clamp(_conversionCurrentFileProgress, 0, total);
|
||||
var percent = total > 0 ? Math.Clamp(completed / (float)total, 0f, 1f) : 0f;
|
||||
|
||||
ImGui.TextUnformatted($"Compressing textures ({completed}/{total})");
|
||||
UiSharedService.TextWrapped("Current file: " + currentLabel);
|
||||
var job = progress?.CurrentJob;
|
||||
var inputPath = job?.InputFile ?? string.Empty;
|
||||
var targetLabel = job != null ? job.TargetType.ToString() : "Unknown";
|
||||
var currentLabel = !string.IsNullOrEmpty(inputPath)
|
||||
? Path.GetFileName(inputPath)
|
||||
: !string.IsNullOrEmpty(_conversionCurrentFileName) ? _conversionCurrentFileName : "Preparing...";
|
||||
var mapKind = !string.IsNullOrEmpty(inputPath)
|
||||
? _textureMetadataHelper.DetermineMapKind(inputPath)
|
||||
: TextureMapKind.Unknown;
|
||||
|
||||
if (_conversionFailed)
|
||||
var accent = UIColors.Get("LightlessPurple");
|
||||
var accentBg = new Vector4(accent.X, accent.Y, accent.Z, 0.18f);
|
||||
var accentBorder = new Vector4(accent.X, accent.Y, accent.Z, 0.4f);
|
||||
var headerHeight = MathF.Max(ImGui.GetTextLineHeightWithSpacing() * 2.6f, 46f * scale);
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 6f * scale))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ChildBorderSize, MathF.Max(1f, ImGui.GetStyle().ChildBorderSize)))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(12f * scale, 6f * scale)))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(12f * scale, 2f * scale)))
|
||||
using (ImRaii.PushColor(ImGuiCol.ChildBg, UiSharedService.Color(accentBg)))
|
||||
using (ImRaii.PushColor(ImGuiCol.Border, UiSharedService.Color(accentBorder)))
|
||||
using (var header = ImRaii.Child("compressionHeader", new Vector2(-1f, headerHeight), true, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse))
|
||||
{
|
||||
UiSharedService.ColorText("Conversion encountered errors. Please review the log for details.", ImGuiColors.DalamudRed);
|
||||
if (header)
|
||||
{
|
||||
if (ImGui.BeginTable("compressionHeaderTable", 2,
|
||||
ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoBordersInBody | ImGuiTableFlags.NoHostExtendX))
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
DrawCompressionTitle(accent, scale);
|
||||
|
||||
var statusText = isDebugPreview ? "Preview mode" : "Working...";
|
||||
var statusColor = isDebugPreview ? UIColors.Get("LightlessYellow") : ImGuiColors.DalamudGrey;
|
||||
UiSharedService.ColorText(statusText, statusColor);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var progressText = $"{completed}/{total}";
|
||||
var percentText = $"{percent * 100f:0}%";
|
||||
var summaryText = $"{progressText} ({percentText})";
|
||||
var summaryWidth = ImGui.CalcTextSize(summaryText).X;
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + MathF.Max(0f, ImGui.GetColumnWidth() - summaryWidth));
|
||||
UiSharedService.ColorText(summaryText, ImGuiColors.DalamudGrey);
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion"))
|
||||
ImGuiHelpers.ScaledDummy(6);
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 4f * scale))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.FramePadding, new Vector2(0f, 4f * scale)))
|
||||
using (ImRaii.PushColor(ImGuiCol.FrameBg, UiSharedService.Color(new Vector4(0.15f, 0.15f, 0.18f, 1f))))
|
||||
using (ImRaii.PushColor(ImGuiCol.PlotHistogram, UiSharedService.Color(accent)))
|
||||
{
|
||||
_conversionCancellationTokenSource.Cancel();
|
||||
ImGui.ProgressBar(percent, new Vector2(-1f, 0f), $"{percent * 100f:0}%");
|
||||
}
|
||||
|
||||
UiSharedService.SetScaledWindowSize(520);
|
||||
ImGuiHelpers.ScaledDummy(6);
|
||||
|
||||
var infoAccent = UIColors.Get("LightlessBlue");
|
||||
var infoBg = new Vector4(infoAccent.X, infoAccent.Y, infoAccent.Z, 0.12f);
|
||||
var infoBorder = new Vector4(infoAccent.X, infoAccent.Y, infoAccent.Z, 0.32f);
|
||||
const int detailRows = 3;
|
||||
var detailHeight = MathF.Max(ImGui.GetTextLineHeightWithSpacing() * (detailRows + 1.2f), 72f * scale);
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 5f * scale))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ChildBorderSize, MathF.Max(1f, ImGui.GetStyle().ChildBorderSize)))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(10f * scale, 6f * scale)))
|
||||
using (ImRaii.PushColor(ImGuiCol.ChildBg, UiSharedService.Color(infoBg)))
|
||||
using (ImRaii.PushColor(ImGuiCol.Border, UiSharedService.Color(infoBorder)))
|
||||
using (var details = ImRaii.Child("compressionDetail", new Vector2(-1f, detailHeight), true, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse))
|
||||
{
|
||||
if (details)
|
||||
{
|
||||
if (ImGui.BeginTable("compressionDetailTable", 2,
|
||||
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoBordersInBody | ImGuiTableFlags.PadOuterX))
|
||||
{
|
||||
DrawDetailRow("Current file", currentLabel, inputPath);
|
||||
DrawDetailRow("Target format", targetLabel, null);
|
||||
DrawDetailRow("Map type", mapKind.ToString(), null);
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_conversionFailed && !isDebugPreview)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(4);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, ImGuiColors.DalamudRed);
|
||||
ImGui.SameLine(0f, 6f * scale);
|
||||
UiSharedService.TextWrapped("Conversion encountered errors. Please review the log for details.", color: ImGuiColors.DalamudRed);
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(6);
|
||||
if (!isDebugPreview)
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion"))
|
||||
{
|
||||
_conversionCancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Times, "Close preview"))
|
||||
{
|
||||
CloseDebugCompressionModal();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
UiSharedService.SetScaledWindowSize(600);
|
||||
|
||||
void DrawDetailRow(string label, string value, string? tooltip)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey))
|
||||
{
|
||||
ImGui.TextUnformatted(label);
|
||||
}
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(value);
|
||||
if (!string.IsNullOrEmpty(tooltip))
|
||||
{
|
||||
UiSharedService.AttachToolTip(tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCompressionTitle(Vector4 iconColor, float localScale)
|
||||
{
|
||||
const string title = "Texture Compression";
|
||||
var spacing = 6f * localScale;
|
||||
|
||||
var iconText = FontAwesomeIcon.CompressArrowsAlt.ToIconString();
|
||||
Vector2 iconSize;
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
{
|
||||
iconSize = ImGui.CalcTextSize(iconText);
|
||||
}
|
||||
|
||||
Vector2 titleSize;
|
||||
using (_uiSharedService.MediumFont.Push())
|
||||
{
|
||||
titleSize = ImGui.CalcTextSize(title);
|
||||
}
|
||||
|
||||
var lineHeight = MathF.Max(iconSize.Y, titleSize.Y);
|
||||
var iconOffsetY = (lineHeight - iconSize.Y) / 2f;
|
||||
var textOffsetY = (lineHeight - titleSize.Y) / 2f;
|
||||
|
||||
var start = ImGui.GetCursorScreenPos();
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
{
|
||||
drawList.AddText(new Vector2(start.X, start.Y + iconOffsetY), UiSharedService.Color(iconColor), iconText);
|
||||
}
|
||||
|
||||
using (_uiSharedService.MediumFont.Push())
|
||||
{
|
||||
var textPos = new Vector2(start.X + iconSize.X + spacing, start.Y + textOffsetY);
|
||||
drawList.AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), title);
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(iconSize.X + spacing + titleSize.X, lineHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetConversionModalState()
|
||||
@@ -202,6 +378,41 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
_conversionTotalJobs = 0;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private void OpenCompressionDebugModal()
|
||||
{
|
||||
if (_conversionTask != null && !_conversionTask.IsCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_debugCompressionModalOpen = true;
|
||||
_debugConversionProgress = new TextureConversionProgress(
|
||||
Completed: 3,
|
||||
Total: 10,
|
||||
CurrentJob: new TextureConversionJob(
|
||||
@"C:\Lightless\Mods\Textures\example_diffuse.tex",
|
||||
@"C:\Lightless\Mods\Textures\example_diffuse_bc7.tex",
|
||||
Penumbra.Api.Enums.TextureType.Bc7Tex));
|
||||
_showModal = true;
|
||||
_modalOpen = false;
|
||||
}
|
||||
|
||||
private void ResetDebugCompressionModalState()
|
||||
{
|
||||
_debugCompressionModalOpen = false;
|
||||
_debugConversionProgress = null;
|
||||
}
|
||||
|
||||
private void CloseDebugCompressionModal()
|
||||
{
|
||||
ResetDebugCompressionModalState();
|
||||
_showModal = false;
|
||||
_modalOpen = false;
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void RefreshAnalysisCache()
|
||||
{
|
||||
if (!_hasUpdate)
|
||||
@@ -757,6 +968,9 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
ResetTextureFilters();
|
||||
InvalidateTextureRows();
|
||||
_conversionFailed = false;
|
||||
#if DEBUG
|
||||
ResetDebugCompressionModalState();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -1955,6 +2169,17 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
InvalidateTextureRows();
|
||||
}
|
||||
#if DEBUG
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(conversionRunning || !UiSharedService.CtrlPressed()))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Cog, "Preview popup (debug)", 200f * scale))
|
||||
{
|
||||
OpenCompressionDebugModal();
|
||||
}
|
||||
}
|
||||
UiSharedService.AttachToolTip("Hold CTRL to open the compression popup preview.");
|
||||
#endif
|
||||
|
||||
TextureRow? lastSelected = null;
|
||||
using (var table = ImRaii.Table("textureDataTable", 9,
|
||||
@@ -2335,11 +2560,30 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
if (_texturePreviews.TryGetValue(key, out var state))
|
||||
{
|
||||
var loadTask = state.LoadTask;
|
||||
if (loadTask is { IsCompleted: false })
|
||||
{
|
||||
_ = loadTask.ContinueWith(_ =>
|
||||
{
|
||||
state.Texture?.Dispose();
|
||||
}, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
state.Texture?.Dispose();
|
||||
_texturePreviews.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearHoverPreview(TextureRow row)
|
||||
{
|
||||
if (string.Equals(_selectedTextureKey, row.Key, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResetPreview(row.Key);
|
||||
}
|
||||
|
||||
private TextureResolutionInfo? GetTextureResolution(TextureRow row)
|
||||
{
|
||||
if (_textureResolutionCache.TryGetValue(row.Key, out var cached))
|
||||
@@ -2440,7 +2684,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
UiSharedService.AttachToolTip("Already stored in a compressed format; additional compression is disabled.");
|
||||
}
|
||||
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
var nameHovered = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
var selectableLabel = $"{row.DisplayName}##texName{index}";
|
||||
if (ImGui.Selectable(selectableLabel, isSelected))
|
||||
@@ -2448,20 +2692,20 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
_selectedTextureKey = isSelected ? string.Empty : key;
|
||||
}
|
||||
|
||||
return () => UiSharedService.AttachToolTip($"{row.PrimaryFilePath}{UiSharedService.TooltipSeparator}{string.Join(Environment.NewLine, row.GamePaths)}");
|
||||
return null;
|
||||
});
|
||||
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
ImGui.TextUnformatted(row.Slot);
|
||||
return null;
|
||||
});
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
ImGui.TextUnformatted(row.MapKind.ToString());
|
||||
return null;
|
||||
});
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
Action? tooltipAction = null;
|
||||
ImGui.TextUnformatted(row.Format);
|
||||
@@ -2475,7 +2719,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
return tooltipAction;
|
||||
});
|
||||
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
if (row.SuggestedTarget.HasValue)
|
||||
{
|
||||
@@ -2537,19 +2781,21 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
UiSharedService.AttachToolTip("This texture is already compressed and cannot be processed again.");
|
||||
}
|
||||
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
ImGui.TextUnformatted(UiSharedService.ByteToString(row.OriginalSize));
|
||||
return null;
|
||||
});
|
||||
DrawSelectableColumn(isSelected, () =>
|
||||
_ = DrawSelectableColumn(isSelected, () =>
|
||||
{
|
||||
ImGui.TextUnformatted(UiSharedService.ByteToString(row.CompressedSize));
|
||||
return null;
|
||||
});
|
||||
|
||||
DrawTextureRowHoverTooltip(row, nameHovered);
|
||||
}
|
||||
|
||||
private static void DrawSelectableColumn(bool isSelected, Func<Action?> draw)
|
||||
private static bool DrawSelectableColumn(bool isSelected, Func<Action?> draw)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
if (isSelected)
|
||||
@@ -2558,6 +2804,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
var after = draw();
|
||||
var hovered = ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem);
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
@@ -2565,6 +2812,127 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
after?.Invoke();
|
||||
return hovered;
|
||||
}
|
||||
|
||||
private void DrawTextureRowHoverTooltip(TextureRow row, bool isHovered)
|
||||
{
|
||||
if (!isHovered)
|
||||
{
|
||||
if (string.Equals(_textureHoverKey, row.Key, StringComparison.Ordinal))
|
||||
{
|
||||
_textureHoverKey = string.Empty;
|
||||
_textureHoverStartTime = 0;
|
||||
ClearHoverPreview(row);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var now = ImGui.GetTime();
|
||||
if (!string.Equals(_textureHoverKey, row.Key, StringComparison.Ordinal))
|
||||
{
|
||||
_textureHoverKey = row.Key;
|
||||
_textureHoverStartTime = now;
|
||||
}
|
||||
|
||||
var elapsed = now - _textureHoverStartTime;
|
||||
if (elapsed < TextureHoverPreviewDelaySeconds)
|
||||
{
|
||||
var progress = (float)Math.Clamp(elapsed / TextureHoverPreviewDelaySeconds, 0f, 1f);
|
||||
DrawTextureRowTextTooltip(row, progress);
|
||||
return;
|
||||
}
|
||||
|
||||
DrawTextureRowPreviewTooltip(row);
|
||||
}
|
||||
|
||||
private void DrawTextureRowTextTooltip(TextureRow row, float progress)
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.SetWindowFontScale(1f);
|
||||
DrawTextureRowTooltipBody(row);
|
||||
ImGuiHelpers.ScaledDummy(4);
|
||||
DrawTextureHoverProgressBar(progress, GetTooltipContentWidth());
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
private void DrawTextureRowPreviewTooltip(TextureRow row)
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.SetWindowFontScale(1f);
|
||||
|
||||
DrawTextureRowTooltipBody(row);
|
||||
ImGuiHelpers.ScaledDummy(4);
|
||||
|
||||
var previewSize = new Vector2(TextureHoverPreviewSize * ImGuiHelpers.GlobalScale);
|
||||
var (previewTexture, previewLoading, previewError) = GetTexturePreview(row);
|
||||
if (previewTexture != null)
|
||||
{
|
||||
ImGui.Image(previewTexture.Handle, previewSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (ImRaii.Child("textureHoverPreview", previewSize, true))
|
||||
{
|
||||
UiSharedService.TextWrapped(previewLoading ? "Loading preview..." : previewError ?? "Preview unavailable.");
|
||||
}
|
||||
}
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
private static void DrawTextureRowTooltipBody(TextureRow row)
|
||||
{
|
||||
var text = row.GamePaths.Count > 0
|
||||
? $"{row.PrimaryFilePath}{UiSharedService.TooltipSeparator}{string.Join(Environment.NewLine, row.GamePaths)}"
|
||||
: row.PrimaryFilePath;
|
||||
|
||||
var wrapWidth = GetTextureHoverTooltipWidth();
|
||||
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + wrapWidth);
|
||||
if (text.Contains(UiSharedService.TooltipSeparator, StringComparison.Ordinal))
|
||||
{
|
||||
var splitText = text.Split(UiSharedService.TooltipSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
for (int i = 0; i < splitText.Length; i++)
|
||||
{
|
||||
ImGui.TextUnformatted(splitText[i]);
|
||||
if (i != splitText.Length - 1)
|
||||
{
|
||||
ImGui.Separator();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(text);
|
||||
}
|
||||
ImGui.PopTextWrapPos();
|
||||
}
|
||||
|
||||
private static void DrawTextureHoverProgressBar(float progress, float width)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
var barHeight = 4f * scale;
|
||||
var barWidth = width > 0f ? width : -1f;
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 3f * scale))
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.FramePadding, Vector2.Zero))
|
||||
using (ImRaii.PushColor(ImGuiCol.PlotHistogram, UiSharedService.Color(UIColors.Get("LightlessPurple"))))
|
||||
{
|
||||
ImGui.ProgressBar(progress, new Vector2(barWidth, barHeight), string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetTextureHoverTooltipWidth()
|
||||
=> ImGui.GetFontSize() * 35f;
|
||||
|
||||
private static float GetTooltipContentWidth()
|
||||
{
|
||||
var min = ImGui.GetWindowContentRegionMin();
|
||||
var max = ImGui.GetWindowContentRegionMax();
|
||||
var width = max.X - min.X;
|
||||
if (width <= 0f)
|
||||
{
|
||||
width = ImGui.GetContentRegionAvail().X;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
private static void ApplyTextureRowBackground(TextureRow row, bool isSelected)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace LightlessSync.UI;
|
||||
public class DownloadUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, IReadOnlyDictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly UiSharedService _uiShared;
|
||||
@@ -170,7 +170,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
const float rounding = 6f;
|
||||
var shadowOffset = new Vector2(2, 2);
|
||||
|
||||
List<KeyValuePair<GameObjectHandler, Dictionary<string, FileDownloadStatus>>> transfers;
|
||||
List<KeyValuePair<GameObjectHandler, IReadOnlyDictionary<string, FileDownloadStatus>>> transfers;
|
||||
try
|
||||
{
|
||||
transfers = [.. _currentDownloads];
|
||||
@@ -212,12 +212,16 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
var dlQueue = 0;
|
||||
var dlProg = 0;
|
||||
var dlDecomp = 0;
|
||||
var dlComplete = 0;
|
||||
|
||||
foreach (var entry in transfer.Value)
|
||||
{
|
||||
var fileStatus = entry.Value;
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.Initializing:
|
||||
dlQueue++;
|
||||
break;
|
||||
case DownloadStatus.WaitingForSlot:
|
||||
dlSlot++;
|
||||
break;
|
||||
@@ -230,15 +234,20 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
case DownloadStatus.Decompressing:
|
||||
dlDecomp++;
|
||||
break;
|
||||
case DownloadStatus.Completed:
|
||||
dlComplete++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var isAllComplete = dlComplete > 0 && dlProg == 0 && dlDecomp == 0 && dlQueue == 0 && dlSlot == 0;
|
||||
|
||||
string statusText;
|
||||
if (dlProg > 0)
|
||||
{
|
||||
statusText = "Downloading";
|
||||
}
|
||||
else if (dlDecomp > 0 || (totalBytes > 0 && transferredBytes >= totalBytes))
|
||||
else if (dlDecomp > 0)
|
||||
{
|
||||
statusText = "Decompressing";
|
||||
}
|
||||
@@ -250,6 +259,10 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
statusText = "Waiting for slot";
|
||||
}
|
||||
else if (isAllComplete)
|
||||
{
|
||||
statusText = "Completed";
|
||||
}
|
||||
else
|
||||
{
|
||||
statusText = "Waiting";
|
||||
@@ -315,7 +328,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
fillPercent = transferredBytes / (double)totalBytes;
|
||||
showFill = true;
|
||||
}
|
||||
else if (dlDecomp > 0 || transferredBytes >= totalBytes)
|
||||
else if (dlDecomp > 0 || dlComplete > 0 || transferredBytes >= totalBytes)
|
||||
{
|
||||
fillPercent = 1.0;
|
||||
showFill = true;
|
||||
@@ -347,10 +360,14 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
downloadText =
|
||||
$"{statusText} {UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}";
|
||||
}
|
||||
else if ((dlDecomp > 0 || transferredBytes >= totalBytes) && hasValidSize)
|
||||
else if (dlDecomp > 0)
|
||||
{
|
||||
downloadText = "Decompressing";
|
||||
}
|
||||
else if (isAllComplete)
|
||||
{
|
||||
downloadText = "Completed";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Waiting states
|
||||
@@ -423,6 +440,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
var totalDlQueue = 0;
|
||||
var totalDlProg = 0;
|
||||
var totalDlDecomp = 0;
|
||||
var totalDlComplete = 0;
|
||||
|
||||
var perPlayer = new List<(
|
||||
string Name,
|
||||
@@ -434,7 +452,8 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
int DlSlot,
|
||||
int DlQueue,
|
||||
int DlProg,
|
||||
int DlDecomp)>();
|
||||
int DlDecomp,
|
||||
int DlComplete)>();
|
||||
|
||||
foreach (var transfer in _currentDownloads)
|
||||
{
|
||||
@@ -460,12 +479,17 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
var playerDlQueue = 0;
|
||||
var playerDlProg = 0;
|
||||
var playerDlDecomp = 0;
|
||||
var playerDlComplete = 0;
|
||||
|
||||
foreach (var entry in transfer.Value)
|
||||
{
|
||||
var fileStatus = entry.Value;
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.Initializing:
|
||||
playerDlQueue++;
|
||||
totalDlQueue++;
|
||||
break;
|
||||
case DownloadStatus.WaitingForSlot:
|
||||
playerDlSlot++;
|
||||
totalDlSlot++;
|
||||
@@ -482,6 +506,10 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
playerDlDecomp++;
|
||||
totalDlDecomp++;
|
||||
break;
|
||||
case DownloadStatus.Completed:
|
||||
playerDlComplete++;
|
||||
totalDlComplete++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +535,8 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
playerDlSlot,
|
||||
playerDlQueue,
|
||||
playerDlProg,
|
||||
playerDlDecomp
|
||||
playerDlDecomp,
|
||||
playerDlComplete
|
||||
));
|
||||
}
|
||||
|
||||
@@ -531,7 +560,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
|
||||
// Overall texts
|
||||
var headerText =
|
||||
$"Downloading {transferredFiles}/{totalFiles} files [W:{totalDlSlot}/Q:{totalDlQueue}/P:{totalDlProg}/D:{totalDlDecomp}]";
|
||||
$"Downloading {transferredFiles}/{totalFiles} files [W:{totalDlSlot}/Q:{totalDlQueue}/P:{totalDlProg}/D:{totalDlDecomp}/C:{totalDlComplete}]";
|
||||
|
||||
var bytesText =
|
||||
$"{UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}";
|
||||
@@ -554,7 +583,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
foreach (var p in perPlayer)
|
||||
{
|
||||
var line =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}/C:{p.DlComplete}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
var lineSize = ImGui.CalcTextSize(line);
|
||||
if (lineSize.X > contentWidth)
|
||||
@@ -672,7 +701,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
&& p.TransferredBytes > 0;
|
||||
|
||||
var labelLine =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}/C:{p.DlComplete}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
if (!showBar)
|
||||
{
|
||||
@@ -731,13 +760,18 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
// Text inside bar: downloading vs decompressing
|
||||
string barText;
|
||||
|
||||
var isDecompressing = p.DlDecomp > 0 && p.TransferredBytes >= p.TotalBytes && p.TotalBytes > 0;
|
||||
var isDecompressing = p.DlDecomp > 0;
|
||||
var isAllComplete = p.DlComplete > 0 && p.DlProg == 0 && p.DlDecomp == 0 && p.DlQueue == 0 && p.DlSlot == 0;
|
||||
|
||||
if (isDecompressing)
|
||||
{
|
||||
// Keep bar full, static text showing decompressing
|
||||
barText = "Decompressing...";
|
||||
}
|
||||
else if (isAllComplete)
|
||||
{
|
||||
barText = "Completed";
|
||||
}
|
||||
else
|
||||
{
|
||||
var bytesInside =
|
||||
@@ -818,6 +852,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
var dlQueue = 0;
|
||||
var dlProg = 0;
|
||||
var dlDecomp = 0;
|
||||
var dlComplete = 0;
|
||||
long totalBytes = 0;
|
||||
long transferredBytes = 0;
|
||||
|
||||
@@ -827,22 +862,29 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
var fileStatus = entry.Value;
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.Initializing: dlQueue++; break;
|
||||
case DownloadStatus.WaitingForSlot: dlSlot++; break;
|
||||
case DownloadStatus.WaitingForQueue: dlQueue++; break;
|
||||
case DownloadStatus.Downloading: dlProg++; break;
|
||||
case DownloadStatus.Decompressing: dlDecomp++; break;
|
||||
case DownloadStatus.Completed: dlComplete++; break;
|
||||
}
|
||||
totalBytes += fileStatus.TotalBytes;
|
||||
transferredBytes += fileStatus.TransferredBytes;
|
||||
}
|
||||
|
||||
var progress = totalBytes > 0 ? (float)transferredBytes / totalBytes : 0f;
|
||||
if (dlComplete > 0 && dlProg == 0 && dlDecomp == 0 && dlQueue == 0 && dlSlot == 0)
|
||||
{
|
||||
progress = 1f;
|
||||
}
|
||||
|
||||
string status;
|
||||
if (dlDecomp > 0) status = "decompressing";
|
||||
else if (dlProg > 0) status = "downloading";
|
||||
else if (dlQueue > 0) status = "queued";
|
||||
else if (dlSlot > 0) status = "waiting";
|
||||
else if (dlComplete > 0) status = "completed";
|
||||
else status = "completed";
|
||||
|
||||
downloadStatus.Add((item.Key.Name, progress, status));
|
||||
|
||||
@@ -217,6 +217,7 @@ public class DrawEntityFactory
|
||||
entry.PairStatus,
|
||||
handler?.LastAppliedDataBytes ?? -1,
|
||||
handler?.LastAppliedDataTris ?? -1,
|
||||
handler?.LastAppliedApproximateEffectiveTris ?? -1,
|
||||
handler?.LastAppliedApproximateVRAMBytes ?? -1,
|
||||
handler?.LastAppliedApproximateEffectiveVRAMBytes ?? -1,
|
||||
handler);
|
||||
|
||||
@@ -415,7 +415,9 @@ public class IdDisplayHandler
|
||||
var vramBytes = pair.LastAppliedApproximateEffectiveVRAMBytes >= 0
|
||||
? pair.LastAppliedApproximateEffectiveVRAMBytes
|
||||
: pair.LastAppliedApproximateVRAMBytes;
|
||||
var triangleCount = pair.LastAppliedDataTris;
|
||||
var triangleCount = pair.LastAppliedApproximateEffectiveTris >= 0
|
||||
? pair.LastAppliedApproximateEffectiveTris
|
||||
: pair.LastAppliedDataTris;
|
||||
if (vramBytes < 0 && triangleCount < 0)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed record PairUiEntry(
|
||||
IndividualPairStatus? PairStatus,
|
||||
long LastAppliedDataBytes,
|
||||
long LastAppliedDataTris,
|
||||
long LastAppliedApproximateEffectiveTris,
|
||||
long LastAppliedApproximateVramBytes,
|
||||
long LastAppliedApproximateEffectiveVramBytes,
|
||||
IPairHandlerAdapter? Handler)
|
||||
|
||||
@@ -7,4 +7,5 @@ public enum VisiblePairSortMode
|
||||
EffectiveVramUsage = 2,
|
||||
TriangleCount = 3,
|
||||
PreferredDirectPairs = 4,
|
||||
EffectiveTriangleCount = 5,
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly CacheMonitor _cacheMonitor;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly UiThemeConfigService _themeConfigService;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, IReadOnlyDictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
@@ -577,6 +577,94 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTriangleDecimationCounters()
|
||||
{
|
||||
HashSet<Pair> trackedPairs = new();
|
||||
|
||||
var snapshot = _pairUiService.GetSnapshot();
|
||||
|
||||
foreach (var pair in snapshot.DirectPairs)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
|
||||
foreach (var group in snapshot.GroupPairs.Values)
|
||||
{
|
||||
foreach (var pair in group)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
long totalOriginalTris = 0;
|
||||
long totalEffectiveTris = 0;
|
||||
var hasData = false;
|
||||
|
||||
foreach (var pair in trackedPairs)
|
||||
{
|
||||
if (!pair.IsVisible)
|
||||
continue;
|
||||
|
||||
var original = pair.LastAppliedDataTris;
|
||||
var effective = pair.LastAppliedApproximateEffectiveTris;
|
||||
|
||||
if (original >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalOriginalTris += original;
|
||||
}
|
||||
|
||||
if (effective >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalEffectiveTris += effective;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData)
|
||||
{
|
||||
ImGui.TextDisabled("Triangle usage has not been calculated yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
var savedTris = Math.Max(0L, totalOriginalTris - totalEffectiveTris);
|
||||
var originalText = FormatTriangleCount(totalOriginalTris);
|
||||
var effectiveText = FormatTriangleCount(totalEffectiveTris);
|
||||
var savedText = FormatTriangleCount(savedTris);
|
||||
|
||||
ImGui.TextUnformatted($"Total triangle usage (original): {originalText}");
|
||||
ImGui.TextUnformatted($"Total triangle usage (effective): {effectiveText}");
|
||||
|
||||
if (savedTris > 0)
|
||||
{
|
||||
UiSharedService.ColorText($"Triangles saved by decimation: {savedText}", UIColors.Get("LightlessGreen"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted($"Triangles saved by decimation: {savedText}");
|
||||
}
|
||||
|
||||
static string FormatTriangleCount(long triangleCount)
|
||||
{
|
||||
if (triangleCount < 0)
|
||||
{
|
||||
return "n/a";
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000_000d:0.#}m tris");
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000d:0.#}k tris");
|
||||
}
|
||||
|
||||
return $"{triangleCount} tris";
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawThemeVectorRow(MainStyle.StyleVector2Option option)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
@@ -866,10 +954,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
|
||||
_uiShared.DrawHelpText(
|
||||
$"The download window will show the current progress of outstanding downloads.{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"What do W/Q/P/D stand for?{Environment.NewLine}W = Waiting for Slot (see Maximum Parallel Downloads){Environment.NewLine}" +
|
||||
$"What do W/Q/P/D/C stand for?{Environment.NewLine}W = Waiting for Slot (see Maximum Parallel Downloads){Environment.NewLine}" +
|
||||
$"Q = Queued on Server, waiting for queue ready signal{Environment.NewLine}" +
|
||||
$"P = Processing download (aka downloading){Environment.NewLine}" +
|
||||
$"D = Decompressing download");
|
||||
$"D = Decompressing download{Environment.NewLine}" +
|
||||
$"C = Completed download");
|
||||
if (!_configService.Current.ShowTransferWindow) ImGui.BeginDisabled();
|
||||
ImGui.Indent();
|
||||
|
||||
@@ -1497,6 +1586,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
DrawPairPropertyRow("Approx. VRAM", FormatBytes(pair.LastAppliedApproximateVRAMBytes));
|
||||
DrawPairPropertyRow("Effective VRAM", FormatBytes(pair.LastAppliedApproximateEffectiveVRAMBytes));
|
||||
DrawPairPropertyRow("Last Triangles", pair.LastAppliedDataTris < 0 ? "n/a" : pair.LastAppliedDataTris.ToString(CultureInfo.InvariantCulture));
|
||||
DrawPairPropertyRow("Effective Triangles", pair.LastAppliedApproximateEffectiveTris < 0 ? "n/a" : pair.LastAppliedApproximateEffectiveTris.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
@@ -3634,6 +3724,112 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (_uiShared.MediumTreeNode("Model Optimization", UIColors.Get("DimRed")))
|
||||
{
|
||||
_uiShared.MediumText("Warning", UIColors.Get("DimRed"));
|
||||
_uiShared.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Model decimation is a "),
|
||||
new SeStringUtils.RichTextEntry("destructive", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" process and may cause broken or incorrect character appearances."));
|
||||
|
||||
_uiShared.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("this shit is placeholder still owo"));
|
||||
|
||||
var performanceConfig = _playerPerformanceConfigService.Current;
|
||||
var enableDecimation = performanceConfig.EnableModelDecimation;
|
||||
if (ImGui.Checkbox("Enable model decimation", ref enableDecimation))
|
||||
{
|
||||
performanceConfig.EnableModelDecimation = enableDecimation;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText("When enabled, Lightless generates a decimated copy of given model after download.");
|
||||
|
||||
var keepOriginalModels = performanceConfig.KeepOriginalModelFiles;
|
||||
if (ImGui.Checkbox("Keep original model files", ref keepOriginalModels))
|
||||
{
|
||||
performanceConfig.KeepOriginalModelFiles = keepOriginalModels;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText("When disabled, Lightless removes the original model after a decimated copy is created.");
|
||||
ImGui.SameLine();
|
||||
_uiShared.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("If disabled, saved + effective triangle usage information will not work.", UIColors.Get("LightlessYellow")));
|
||||
|
||||
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
|
||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.SliderInt("Decimate models above", ref triangleThreshold, 10_000, 100_000))
|
||||
{
|
||||
performanceConfig.ModelDecimationTriangleThreshold = Math.Clamp(triangleThreshold, 10_000, 100_000);
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.Text("triangles");
|
||||
_uiShared.DrawHelpText($"Models below this triangle count are left untouched.{UiSharedService.TooltipSeparator}Default: 50,000");
|
||||
|
||||
var targetPercent = (float)(performanceConfig.ModelDecimationTargetRatio * 100.0);
|
||||
var clampedPercent = Math.Clamp(targetPercent, 70f, 99f);
|
||||
if (Math.Abs(clampedPercent - targetPercent) > float.Epsilon)
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = clampedPercent / 100.0;
|
||||
_playerPerformanceConfigService.Save();
|
||||
targetPercent = clampedPercent;
|
||||
}
|
||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.SliderFloat("Target triangle ratio", ref targetPercent, 70f, 99f, "%.0f%%"))
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = Math.Clamp(targetPercent / 100f, 0.7f, 0.99f);
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText($"Target ratio relative to original triangle count (70% keeps 70% of triangles).{UiSharedService.TooltipSeparator}Default: 70%");
|
||||
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
ImGui.TextUnformatted("Decimation targets");
|
||||
_uiShared.DrawHelpText("Hair mods are always excluded from decimation.");
|
||||
|
||||
var allowBody = performanceConfig.ModelDecimationAllowBody;
|
||||
if (ImGui.Checkbox("Body", ref allowBody))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowBody = allowBody;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowFaceHead = performanceConfig.ModelDecimationAllowFaceHead;
|
||||
if (ImGui.Checkbox("Face/head", ref allowFaceHead))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowFaceHead = allowFaceHead;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowTail = performanceConfig.ModelDecimationAllowTail;
|
||||
if (ImGui.Checkbox("Tails/Ears", ref allowTail))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowTail = allowTail;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowClothing = performanceConfig.ModelDecimationAllowClothing;
|
||||
if (ImGui.Checkbox("Clothing (body/legs/shoes/gloves/hats)", ref allowClothing))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowClothing = allowClothing;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowAccessories = performanceConfig.ModelDecimationAllowAccessories;
|
||||
if (ImGui.Checkbox("Accessories (earring/rings/bracelet/necklace)", ref allowAccessories))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowAccessories = allowAccessories;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
DrawTriangleDecimationCounters();
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(new Vector2(10));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user