diff --git a/FModel/Creator/Bases/FN/BaseBundle.cs b/FModel/Creator/Bases/FN/BaseBundle.cs index ea85a775..90c9de2b 100644 --- a/FModel/Creator/Bases/FN/BaseBundle.cs +++ b/FModel/Creator/Bases/FN/BaseBundle.cs @@ -59,7 +59,7 @@ public class BaseBundle : UCreator foreach (var reward in rewards) { if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition")) continue; - quest.AddCompletionRequest(itemDefinition); + quest.AddCompletionReward(itemDefinition); } _quests.Add(quest); } diff --git a/FModel/Creator/Bases/FN/BaseCommunity.cs b/FModel/Creator/Bases/FN/BaseCommunity.cs index 8ac06a9d..2492426d 100644 --- a/FModel/Creator/Bases/FN/BaseCommunity.cs +++ b/FModel/Creator/Bases/FN/BaseCommunity.cs @@ -124,7 +124,14 @@ public class BaseCommunity : BaseIcon if (!bShort) return base.GetCosmeticSeason(seasonNumber); var s = seasonNumber["Cosmetics.Filter.Season.".Length..]; (int chapterIdx, int seasonIdx) = GetInternalSID(int.Parse(s)); - return $"C{chapterIdx} S{seasonIdx}"; + return s switch + { + "10" => $"C{chapterIdx} SX", + "27" => $"Fortnite: OG", + "32" => $"Fortnite: Remix", + "35" => $"C{chapterIdx} MS1", + _ => $"C{chapterIdx} S{seasonIdx}" + }; } private new void DrawBackground(SKCanvas c) diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index fc2fc211..e131f358 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -257,12 +257,19 @@ public class BaseIcon : UCreator var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}"); var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}."); + if (s == "10") return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, "X"))); if (initial <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s))); var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}"); var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}"); var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx)); - return Utils.RemoveHtmlTags(string.Format(introduced, d)); + return s switch + { + "27" => Utils.RemoveHtmlTags(string.Format(introduced, string.Format("Fortnite: OG"))), + "32" => Utils.RemoveHtmlTags(string.Format(introduced, string.Format("Fortnite: Remix"))), + "35" => Utils.RemoveHtmlTags(string.Format(introduced, string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format("MS1")))), + _ => Utils.RemoveHtmlTags(string.Format(introduced, d)) + }; } protected void CheckGameplayTags(FInstancedStruct[] dataList) diff --git a/FModel/Creator/Bases/FN/BaseIconStats.cs b/FModel/Creator/Bases/FN/BaseIconStats.cs index 5db374fb..a297b0e0 100644 --- a/FModel/Creator/Bases/FN/BaseIconStats.cs +++ b/FModel/Creator/Bases/FN/BaseIconStats.cs @@ -84,44 +84,75 @@ public class BaseIconStats : BaseIcon weaponStatHandle.TryGetValue(out UDataTable dataTable, "DataTable") && dataTable.TryGetDataTableRow(weaponRowName.Text, StringComparison.OrdinalIgnoreCase, out var weaponRowValue)) { - if (weaponRowValue.TryGetValue(out int bpc, "BulletsPerCartridge")) + weaponRowValue.TryGetValue(out float dmgPb, "DmgPB"); //Damage at point blank + weaponRowValue.TryGetValue(out float mdpc, "MaxDamagePerCartridge"); //Max damage a weapon can do in a single hit, usually used for shotguns + weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"); //Headshot multiplier + weaponRowValue.TryGetValue(out int clipSize, "ClipSize"); //Item magazine size + weaponRowValue.TryGetValue(out float firingRate, "FiringRate"); //Item firing rate, value is shots per second + weaponRowValue.TryGetValue(out float armTime, "ArmTime"); //Time it takes for traps to be able to be set off + weaponRowValue.TryGetValue(out float reloadTime, "ReloadTime"); //Time it takes for a weapon to reload + weaponRowValue.TryGetValue(out int bpc, "BulletsPerCartridge"); //Amount of pellets shot by a weapon at once, usually for shotguns + weaponRowValue.TryGetValue(out float heatMax, "OverheatingMaxValue"); //Maximum heat overheating weapons can hold before they need to cool off + weaponRowValue.TryGetValue(out float heatPerShot, "OverheatHeatingValue"); //Heat generated per shot on overheat weapons + weaponRowValue.TryGetValue(out float overheatCooldown, "OverheatedCooldownDelay"); //Cooldown after a weapon reaches its maximum heat capacity + weaponRowValue.TryGetValue(out int cartridgePerFire, "CartridgePerFire"); //Amount of bullets shot after pressing the fire button once + weaponRowValue.TryGetValue(out float burstFiringRate, "BurstFiringRate"); //Item firing rate during a burst, value is shots per second { var multiplier = bpc != 0f ? bpc : 1; - if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f) + if (dmgPb != 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 200)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 160)); } - if (weaponRowValue.TryGetValue(out float mdpc, "MaxDamagePerCartridge") && mdpc >= 0f) + if (mdpc > 0f && dmgPb * dmgCritical * multiplier > mdpc) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), mdpc, 200)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), mdpc, 160)); } - else if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical")) + + else if (dmgCritical != 0f && dmgCritical != 1f && dmgPb != 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 200)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 160)); } } - - if (weaponRowValue.TryGetValue(out int clipSize, "ClipSize") && clipSize != 0) + if (clipSize > 999f || clipSize == 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), clipSize, 50)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), Utils.GetLocalizedResource("", "0FAE8E5445029F2AA209ADB0FE49B23C", "Infinite"), -1)); } - if (weaponRowValue.TryGetValue(out float firingRate, "FiringRate") && firingRate != 0f) + else if (clipSize != 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), firingRate, 15)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), clipSize, 40)); } - if (weaponRowValue.TryGetValue(out float armTime, "ArmTime") && armTime != 0f) + var burstEquation = cartridgePerFire / (((cartridgePerFire - 1f) / burstFiringRate) + (1f / firingRate)); + if (burstEquation != 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "3BFEB8BD41A677CC5F45B9A90D6EAD6F", "Arming Delay"), armTime, 125)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), burstEquation, 11)); + } + else if (firingRate != 0f) + { + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), firingRate, 11)); } - if (weaponRowValue.TryGetValue(out float reloadTime, "ReloadTime") && reloadTime != 0f) + if (armTime != 0f) { - _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time"), reloadTime, 15)); + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "3BFEB8BD41A677CC5F45B9A90D6EAD6F", "Arming Delay"), armTime, 5)); } + if (reloadTime != 0f && clipSize < 999f && clipSize != 0f) + { + _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time"), reloadTime, 10)); + } + + if (overheatCooldown != 0f && clipSize > 999f || overheatCooldown != 0f && clipSize == 0f) + { + _statistics.Add(new IconStat("Overheat Cooldown", overheatCooldown, 5)); + } + + if (heatMax != 0f && heatPerShot != 0f && clipSize > 999f || heatMax != 0f && heatPerShot != 0f && clipSize == 0f) + { + _statistics.Add(new IconStat("Shots to Overheat", Math.Ceiling(heatMax / heatPerShot), 80)); + } if ((Object.ExportType.Equals("FortContextTrapItemDefinition", StringComparison.OrdinalIgnoreCase) || Object.ExportType.Equals("FortTrapItemDefinition", StringComparison.OrdinalIgnoreCase)) && weaponRowValue.TryGetValue(out UDataTable durabilityTable, "Durability") && @@ -285,6 +316,10 @@ public class IconStat _statPaint.Color = SKColors.White; c.DrawText(_value.ToString(), new SKPoint(width - 50, y + 10), _statPaint); + if (_maxValue == -1) //fill bar if max value is set to -1, for things that don't return a number here but should still be represented as the maximum value + { + c.DrawRect(new SKRect(height * 2, y, Math.Min(width - height, sliderRight), y + 5), _statPaint); + } if (_maxValue < 1 || !float.TryParse(_value.ToString(), out var floatValue)) return; if (floatValue < 0) floatValue = 0; diff --git a/FModel/Creator/Bases/FN/BaseQuest.cs b/FModel/Creator/Bases/FN/BaseQuest.cs index 48984aff..ef3159aa 100644 --- a/FModel/Creator/Bases/FN/BaseQuest.cs +++ b/FModel/Creator/Bases/FN/BaseQuest.cs @@ -44,12 +44,12 @@ public class BaseQuest : BaseIcon DisplayName = ReformatString(description, completionCount.ToString(), completionCount < 0); } - public void AddCompletionRequest(FSoftObjectPath itemDefinition) + public void AddCompletionReward(FSoftObjectPath itemDefinition) { _rewards.Add(itemDefinition.TryLoad(out UObject uObject) ? new Reward(uObject) : new Reward()); } - public void AddCompletionRequest(int quantity, string reward) + public void AddCompletionReward(int quantity, string reward) { _rewards.Add(new Reward(quantity, reward)); } diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index ad80b17b..0a0ad68c 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -49,7 +49,8 @@ public class CreatorPackage : IDisposable case "CosmeticShoesItemDefinition": case "AthenaPickaxeItemDefinition": case "AthenaGadgetItemDefinition": - case "AthenaGliderItemDefinition": + case "AthenaGliderItemDefinition": + case "AthenaHatItemDefinition": case "AthenaSprayItemDefinition": case "AthenaDanceItemDefinition": case "AthenaEmojiItemDefinition": diff --git a/FModel/Enums.cs b/FModel/Enums.cs index 1412b013..59700533 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -60,7 +60,9 @@ public enum ELoadingMode [Description("All (New)")] AllButNew, [Description("All (Modified)")] - AllButModified + AllButModified, + [Description("All (Except Patched Assets)")] + AllButPatched, } // public enum EUpdateMode diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 2f915233..e1d71bc3 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -63,7 +63,6 @@ public partial class MainWindow await ApplicationViewModel.InitOodle(); await ApplicationViewModel.InitZlib(); - await ApplicationViewModel.InitDetex(); await _applicationView.CUE4Parse.Initialize(); await _applicationView.AesManager.InitAes(); await _applicationView.UpdateProvider(true); @@ -74,6 +73,7 @@ public partial class MainWindow _applicationView.CUE4Parse.VerifyConsoleVariables(), _applicationView.CUE4Parse.VerifyOnDemandArchives(), _applicationView.CUE4Parse.InitMappings(), + ApplicationViewModel.InitDetex(), ApplicationViewModel.InitVgmStream(), ApplicationViewModel.InitImGuiSettings(newOrUpdated), Task.Run(() => diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 01418ff1..66c126bf 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -10,6 +10,7 @@ using CUE4Parse_Conversion.Meshes; using CUE4Parse_Conversion.Textures; using CUE4Parse_Conversion.UEFormat.Enums; using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.Nanite; using FModel.Framework; using FModel.ViewModels; using FModel.ViewModels.ApiEndpoints.Models; @@ -60,6 +61,7 @@ namespace FModel.Settings { LodFormat = Default.LodExportFormat, MeshFormat = Default.MeshExportFormat, + NaniteMeshFormat = Default.NaniteMeshExportFormat, AnimFormat = Default.MeshExportFormat switch { EMeshFormat.UEFormat => EAnimFormat.UEFormat, @@ -367,6 +369,13 @@ namespace FModel.Settings set => SetProperty(ref _meshExportFormat, value); } + private ENaniteMeshFormat _naniteMeshExportFormat = ENaniteMeshFormat.OnlyNaniteLOD; + public ENaniteMeshFormat NaniteMeshExportFormat + { + get => _naniteMeshExportFormat; + set => SetProperty(ref _naniteMeshExportFormat, value); + } + private EMaterialFormat _materialExportFormat = EMaterialFormat.FirstLayer; public EMaterialFormat MaterialExportFormat { diff --git a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs index aeaf42b9..47b7bd55 100644 --- a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs @@ -106,7 +106,11 @@ public class FModelApiEndpoint : AbstractApiProvider public void CheckForUpdates(bool launch = false) { - if (DateTime.Now < UserSettings.Default.NextUpdateCheck) return; + if (DateTime.Now < UserSettings.Default.NextUpdateCheck) + { + Log.Warning("Updates have been silenced until {DateTime}", UserSettings.Default.NextUpdateCheck); + return; + } if (launch) { @@ -140,7 +144,8 @@ public class FModelApiEndpoint : AbstractApiProvider { UserSettings.Default.LastUpdateCheck = DateTime.Now; - if (((CustomMandatory)args.Mandatory).CommitHash == Constants.APP_COMMIT_ID) + var targetHash = ((CustomMandatory) args.Mandatory).CommitHash; + if (targetHash == Constants.APP_COMMIT_ID) { if (UserSettings.Default.ShowChangelog) ShowChangelog(args); @@ -152,6 +157,7 @@ public class FModelApiEndpoint : AbstractApiProvider UserSettings.Default.ShowChangelog = currentVersion != args.InstalledVersion; const string message = "A new update is available!"; + Log.Warning("{message} Version {CurrentVersion} ({Hash})", message, currentVersion, targetHash); Helper.OpenWindow(message, () => new UpdateView { Title = message, ResizeMode = ResizeMode.NoResize }.ShowDialog()); } else diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index e3faa892..dd0806c2 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -260,7 +260,7 @@ public class ApplicationViewModel : ViewModel ZlibHelper.Initialize(zlibPath); } - public static async ValueTask InitDetex() + public static async Task InitDetex() { var detexPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", DetexHelper.DLL_NAME); if (File.Exists(DetexHelper.DLL_NAME)) diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 9b468d16..fa23482b 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -39,6 +39,8 @@ using CUE4Parse_Conversion; using CUE4Parse_Conversion.Sounds; using CUE4Parse.FileProvider.Objects; using CUE4Parse.UE4.Assets; +using CUE4Parse.UE4.BinaryConfig; +using CUE4Parse.UE4.Objects.UObject; using CUE4Parse.UE4.Objects.UObject; using CUE4Parse.Utils; using EpicManifestParser; @@ -165,6 +167,7 @@ public class CUE4ParseViewModel : ViewModel Provider.ReadScriptData = UserSettings.Default.ReadScriptData; Provider.ReadShaderMaps = UserSettings.Default.ReadShaderMaps; + Provider.ReadNaniteData = true; GameDirectory = new GameDirectoryViewModel(); AssetsFolder = new AssetsFolderViewModel(); @@ -195,7 +198,7 @@ public class CUE4ParseViewModel : ViewModel { ChunkCacheDirectory = cacheDir, ManifestCacheDirectory = cacheDir, - ChunkBaseUrl = "http://epicgames-download1.akamaized.net/Builds/Fortnite/CloudDir/", + ChunkBaseUrl = "http://download.epicgames.com/Builds/Fortnite/CloudDir/", Decompressor = ManifestZlibngDotNetDecompressor.Decompress, DecompressorState = ZlibHelper.Instance, CacheChunksAsIs = false @@ -208,7 +211,7 @@ public class CUE4ParseViewModel : ViewModel { (manifest, _) = manifestInfo.DownloadAndParseAsync(manifestOptions, cancellationToken: cancellationToken, - elementManifestPredicate: static x => x.Uri.Host != "cloudflare.epicgamescdn.com" + elementManifestPredicate: static x => x.Uri.Host == "download.epicgames.com" ).GetAwaiter().GetResult(); } catch (HttpRequestException ex) @@ -582,6 +585,16 @@ public class CUE4ParseViewModel : ViewModel break; } + case "ini" when entry.Name.Contains("BinaryConfig"): + { + var ar = entry.CreateReader(); + var configCache = new FConfigCacheIni(ar); + + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json"); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(configCache, Formatting.Indented), saveProperties, updateUi); + + break; + } case "upluginmanifest": case "uproject": case "manifest": diff --git a/FModel/ViewModels/Commands/LoadCommand.cs b/FModel/ViewModels/Commands/LoadCommand.cs index 0a2683bb..4a4ea0b6 100644 --- a/FModel/ViewModels/Commands/LoadCommand.cs +++ b/FModel/ViewModels/Commands/LoadCommand.cs @@ -87,6 +87,11 @@ public class LoadCommand : ViewModelCommand FilterNewOrModifiedFilesToDisplay(cancellationToken); break; } + case ELoadingMode.AllButPatched: + { + FilterPacthedFilesToDisplay(cancellationToken); + break; + } default: throw new ArgumentOutOfRangeException(); } @@ -273,4 +278,26 @@ public class LoadCommand : ViewModelCommand entries.Add(asset); } + + private void FilterPacthedFilesToDisplay(CancellationToken cancellationToken) + { + var loaded = new Dictionary(_applicationView.CUE4Parse.Provider.PathComparer); + + foreach (var (key, asset) in _applicationView.CUE4Parse.Provider.Files) + { + cancellationToken.ThrowIfCancellationRequested(); // cancel if needed + if (asset.IsUePackagePayload) continue; + + if (asset is VfsEntry entry && loaded.TryGetValue(key, out var file) && + file is VfsEntry existingEntry && entry.Vfs.ReadOrder < existingEntry.Vfs.ReadOrder) + { + continue; + } + + loaded[key] = asset; + } + + _applicationView.Status.UpdateStatusLabel($"{loaded.Count:### ### ###} Packages"); + _applicationView.CUE4Parse.AssetsFolder.BulkPopulate(loaded.Values); + } } diff --git a/FModel/ViewModels/SettingsViewModel.cs b/FModel/ViewModels/SettingsViewModel.cs index 15d558ea..b733fc61 100644 --- a/FModel/ViewModels/SettingsViewModel.cs +++ b/FModel/ViewModels/SettingsViewModel.cs @@ -9,6 +9,7 @@ using CUE4Parse_Conversion.Meshes; using CUE4Parse_Conversion.Textures; using CUE4Parse_Conversion.UEFormat.Enums; using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.Nanite; using FModel.Framework; using FModel.Services; using FModel.Settings; @@ -143,6 +144,13 @@ public class SettingsViewModel : ViewModel set => SetProperty(ref _selectedLodExportFormat, value); } + private ENaniteMeshFormat _selectedNaniteMeshExportFormat; + public ENaniteMeshFormat SelectedNaniteMeshExportFormat + { + get => _selectedNaniteMeshExportFormat; + set => SetProperty(ref _selectedNaniteMeshExportFormat, value); + } + private EMaterialFormat _selectedMaterialExportFormat; public EMaterialFormat SelectedMaterialExportFormat { @@ -170,6 +178,7 @@ public class SettingsViewModel : ViewModel public ReadOnlyObservableCollection SocketExportFormats { get; private set; } public ReadOnlyObservableCollection CompressionFormats { get; private set; } public ReadOnlyObservableCollection LodExportFormats { get; private set; } + public ReadOnlyObservableCollection NaniteMeshExportFormats { get; private set; } public ReadOnlyObservableCollection MaterialExportFormats { get; private set; } public ReadOnlyObservableCollection TextureExportFormats { get; private set; } public ReadOnlyObservableCollection Platforms { get; private set; } @@ -193,6 +202,7 @@ public class SettingsViewModel : ViewModel private ESocketFormat _socketExportFormatSnapshot; private EFileCompressionFormat _compressionFormatSnapshot; private ELodFormat _lodExportFormatSnapshot; + private ENaniteMeshFormat _naniteMeshExportFormatSnapshot; private EMaterialFormat _materialExportFormatSnapshot; private ETextureFormat _textureExportFormatSnapshot; @@ -233,6 +243,7 @@ public class SettingsViewModel : ViewModel _socketExportFormatSnapshot = UserSettings.Default.SocketExportFormat; _compressionFormatSnapshot = UserSettings.Default.CompressionFormat; _lodExportFormatSnapshot = UserSettings.Default.LodExportFormat; + _naniteMeshExportFormatSnapshot = UserSettings.Default.NaniteMeshExportFormat; _materialExportFormatSnapshot = UserSettings.Default.MaterialExportFormat; _textureExportFormatSnapshot = UserSettings.Default.TextureExportFormat; @@ -248,6 +259,7 @@ public class SettingsViewModel : ViewModel SelectedSocketExportFormat = _socketExportFormatSnapshot; SelectedCompressionFormat = _selectedCompressionFormat; SelectedLodExportFormat = _lodExportFormatSnapshot; + SelectedNaniteMeshExportFormat = _naniteMeshExportFormatSnapshot; SelectedMaterialExportFormat = _materialExportFormatSnapshot; SelectedTextureExportFormat = _textureExportFormatSnapshot; SelectedAesReload = UserSettings.Default.AesReload; @@ -263,6 +275,7 @@ public class SettingsViewModel : ViewModel SocketExportFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateSocketExportFormat())); CompressionFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateCompressionFormat())); LodExportFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateLodExportFormat())); + NaniteMeshExportFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateNaniteMeshExportFormat())); MaterialExportFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateMaterialExportFormat())); TextureExportFormats = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateTextureExportFormat())); Platforms = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateUePlatforms())); @@ -303,6 +316,7 @@ public class SettingsViewModel : ViewModel UserSettings.Default.SocketExportFormat = SelectedSocketExportFormat; UserSettings.Default.CompressionFormat = SelectedCompressionFormat; UserSettings.Default.LodExportFormat = SelectedLodExportFormat; + UserSettings.Default.NaniteMeshExportFormat = SelectedNaniteMeshExportFormat; UserSettings.Default.MaterialExportFormat = SelectedMaterialExportFormat; UserSettings.Default.TextureExportFormat = SelectedTextureExportFormat; UserSettings.Default.AesReload = SelectedAesReload; @@ -328,6 +342,7 @@ public class SettingsViewModel : ViewModel private IEnumerable EnumerateSocketExportFormat() => Enum.GetValues(); private IEnumerable EnumerateCompressionFormat() => Enum.GetValues(); private IEnumerable EnumerateLodExportFormat() => Enum.GetValues(); + private IEnumerable EnumerateNaniteMeshExportFormat() => Enum.GetValues(); private IEnumerable EnumerateMaterialExportFormat() => Enum.GetValues(); private IEnumerable EnumerateTextureExportFormat() => Enum.GetValues(); private IEnumerable EnumerateUePlatforms() => Enum.GetValues(); diff --git a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs index d8dbccfa..0c63a759 100644 --- a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs +++ b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs @@ -6,6 +6,7 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Threading; namespace FModel.Views.Resources.Controls; @@ -54,7 +55,7 @@ public class FLogger : ITextFormatter } job(); - }); + }, DispatcherPriority.Background); } public static void Text(string message, string color, bool newLine = false) diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index 7ff12b81..d8a79e24 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -316,6 +316,7 @@ + @@ -459,8 +460,8 @@ - - + @@ -469,8 +470,18 @@ - - + + + + + + + + + + diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index dfa91ad3..a2317032 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -12,6 +12,7 @@ using CUE4Parse.UE4.Assets.Exports.Component.SplineMesh; using CUE4Parse.UE4.Assets.Exports.Component.StaticMesh; using CUE4Parse.UE4.Assets.Exports.GeometryCollection; using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.Nanite; using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; using CUE4Parse.UE4.Assets.Exports.StaticMesh; using CUE4Parse.UE4.Assets.Exports.Texture; @@ -86,7 +87,7 @@ public class Renderer : IDisposable switch (dummy) { case UStaticMesh when export.Value is UStaticMesh st: - LoadStaticMesh(st); + LoadStaticMesh(st, UserSettings.Default.NaniteMeshExportFormat); break; case USkeletalMesh when export.Value is USkeletalMesh sk: LoadSkeletalMesh(sk); @@ -343,7 +344,7 @@ public class Renderer : IDisposable wnd.WindowShouldClose(true, true); } - private void LoadStaticMesh(UStaticMesh original) + private void LoadStaticMesh(UStaticMesh original, ENaniteMeshFormat naniteFormat = ENaniteMeshFormat.OnlyNormalLODs) { var guid = original.LightingGuid; if (Options.TryGetModel(guid, out var model)) @@ -353,7 +354,7 @@ public class Renderer : IDisposable return; } - if (!original.TryConvert(out var mesh)) + if (!original.TryConvert(out var mesh, naniteFormat)) return; Options.Models[guid] = new StaticModel(original, mesh); diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index 729aa766..e9f7cfa4 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -130,7 +130,8 @@ public class Material : IDisposable "EmissiveUVs_RG_UpperLeftCorner_BA_LowerRightCorner", "Emissive Texture UVs RG_TopLeft BA_BottomRight", "Emissive 2 UV Positioning (RG)UpperLeft (BA)LowerRight", - "EmissiveUVPositioning (RG)UpperLeft (BA)LowerRight")) + "EmissiveUVPositioning (RG)UpperLeft (BA)LowerRight", + "Emissive_CH")) EmissiveRegion = new Vector4(EmissiveUVs.R, EmissiveUVs.G, EmissiveUVs.B, EmissiveUVs.A); if ((Parameters.TryGetSwitch(out var swizzleRoughnessToGreen, "SwizzleRoughnessToGreen") && swizzleRoughnessToGreen) ||