diff --git a/CUE4Parse b/CUE4Parse index c99a1d6d..83200c61 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit c99a1d6dfc269281c2a7bf4dc36f8664dd8791e6 +Subproject commit 83200c61e66048ef5e17d7ffdd5092a1f53cac03 diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index cd57824c..5b79b834 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -32,6 +32,7 @@ public class BaseIcon : UCreator // rarity if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series); else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer); + else if (Object.TryGetValue(out EFortRarity hackRarity, "HackRarity")) GetRarity(hackRarity); else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList")) @@ -48,7 +49,7 @@ public class BaseIcon : UCreator Preview = preview; else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition")) Preview = Utils.GetBitmap(itemDefinition); - else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon")) + else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon", "CartridgeIcon", "EffectIcon")) Preview = Utils.GetBitmap(largePreview); else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s)) Preview = Utils.GetBitmap(s); @@ -66,9 +67,9 @@ public class BaseIcon : UCreator } // text - if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle")) + if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle", "KeyName", "HackDisplayName")) DisplayName = displayName.Text; - if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription")) + if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription", "KeyDescription", "HackDescription")) Description = description.Text; else if (Object.TryGetValue(out FText[] descriptions, "Description")) Description = string.Join('\n', descriptions.Select(x => x.Text)); diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 82f81427..aea95e5f 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -65,6 +65,7 @@ public class CreatorPackage : IDisposable case "AthenaEmojiItemDefinition": case "AthenaItemWrapDefinition": case "AthenaToyItemDefinition": + case "FortHackData": case "FortHeroType": case "FortTokenType": case "FortAbilityKit": @@ -72,7 +73,9 @@ public class CreatorPackage : IDisposable case "FortMissionInfo": case "RewardGraphToken": case "JunoKnowledgeBundle": + case "FortHackDataMinimal": case "FortBannerTokenType": + case "FortHackKeyCoreData": case "FortVariantTokenType": case "FortDecoItemDefinition": case "FortStatItemDefinition": @@ -88,6 +91,7 @@ public class CreatorPackage : IDisposable case "SparksBassItemDefinition": case "FortGadgetItemDefinition": case "AthenaCharmItemDefinition": + case "FortHackKeyItemDefinition": case "FortPlaysetItemDefinition": case "FortGiftBoxItemDefinition": case "FortOutpostItemDefinition": @@ -124,6 +128,7 @@ public class CreatorPackage : IDisposable case "JunoRecipeBundleItemDefinition": case "FortHomebaseNodeItemDefinition": case "FortNeverPersistItemDefinition": + case "FortHackCartridgeItemDefinition": case "FortPlayerAugmentItemDefinition": case "FortSmartBuildingItemDefinition": case "FortGiftBoxUnlockItemDefinition": diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 528e01fd..6740a50d 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -69,6 +69,7 @@ using CUE4Parse.UE4.Wwise; using CUE4Parse.Utils; using CUE4Parse_Conversion.Exporters; using CUE4Parse_Conversion.Sounds; +using CUE4Parse.UE4.Assets.Exports.GeometryCollection; using EpicManifestParser; using EpicManifestParser.UE; using FModel.Creator; @@ -1561,6 +1562,7 @@ public class CUE4ParseViewModel : ViewModel // }: case UPaperSprite when isNone && UserSettings.Default.PreviewMaterials: case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes: + case UGeometryCollection when isNone && UserSettings.Default.PreviewStaticMeshes: case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh: case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial && @@ -1587,6 +1589,7 @@ public class CUE4ParseViewModel : ViewModel return true; } case UStaticMesh when HasFlag(bulk, EBulkType.Meshes): + case UGeometryCollection when HasFlag(bulk, EBulkType.Meshes): case USkeletalMesh when HasFlag(bulk, EBulkType.Meshes): case USkeleton when UserSettings.Default.SaveSkeletonAsMesh && HasFlag(bulk, EBulkType.Meshes): // case UMaterialInterface when HasFlag(bulk, EBulkType.Materials): diff --git a/FModel/ViewModels/GameFileViewModel.cs b/FModel/ViewModels/GameFileViewModel.cs index 57a9456c..361fa995 100644 --- a/FModel/ViewModels/GameFileViewModel.cs +++ b/FModel/ViewModels/GameFileViewModel.cs @@ -219,7 +219,7 @@ public class GameFileViewModel(GameFile asset) : ViewModel USkeletalMesh => (EAssetCategory.SkeletalMesh, EBulkType.Meshes), UCustomizableObject => (EAssetCategory.CustomizableObject, EBulkType.None), UNaniteDisplacedMesh => (EAssetCategory.NaniteDisplacedMesh, EBulkType.None), - UGeometryCollection => (EAssetCategory.GeometryCollection, EBulkType.None), + UGeometryCollection => (EAssetCategory.GeometryCollection, EBulkType.Meshes), UTexture => (EAssetCategory.Texture, EBulkType.Textures), diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index 6447e2d7..d49d77f4 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -196,8 +196,8 @@ public class GameSelectorViewModel : ViewModel .OrderBy(value => ((int)value & 0xFF) == 0); private IEnumerable EnumerateDetectedGames() { - yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks", EGame.GAME_UE5_8); - yield return DirectorySettings.Default("Fortnite [LIVE]", Constants._FN_LIVE_TRIGGER, ue: EGame.GAME_UE5_8); + yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks", EGame.GAME_UE6_0); + yield return DirectorySettings.Default("Fortnite [LIVE]", Constants._FN_LIVE_TRIGGER, ue: EGame.GAME_UE6_0); yield return GetUnrealEngineGame("Pewee", "\\RogueCompany\\Content\\Paks", EGame.GAME_RogueCompany); yield return GetUnrealEngineGame("Rosemallow", "\\Indiana\\Content\\Paks", EGame.GAME_UE4_21); yield return GetUnrealEngineGame("Catnip", "\\OakGame\\Content\\Paks", EGame.GAME_Borderlands3); diff --git a/FModel/Views/Snooper/Models/StaticModel.cs b/FModel/Views/Snooper/Models/StaticModel.cs index e32e655c..d0fba195 100644 --- a/FModel/Views/Snooper/Models/StaticModel.cs +++ b/FModel/Views/Snooper/Models/StaticModel.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using CUE4Parse_Conversion.Dto; +using CUE4Parse.UE4.Assets.Exports.GeometryCollection; using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.StaticMesh; using CUE4Parse.UE4.Assets.Exports.Texture; @@ -143,6 +144,12 @@ public class StaticModel : UModel } } + public StaticModel(UGeometryCollection export, StaticMeshDto staticMesh, Transform transform = null) + : base(export, staticMesh.LODs[LodLevel], export.Materials, staticMesh.LODs[LodLevel].Vertices, staticMesh.LODs.Count, transform) + { + Box = staticMesh.Bounds * Constants.SCALE_DOWN_RATIO; + } + public override void RenderCollision(Shader shader) { base.RenderCollision(shader); diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 9bd26581..f49b2edd 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Threading; using System.Windows; using CUE4Parse_Conversion.Animations; +using CUE4Parse_Conversion.Dto; using CUE4Parse_Conversion.Meshes; using CUE4Parse_Conversion.Options; using CUE4Parse.UE4.Assets.Exports; @@ -29,6 +30,7 @@ using FModel.Views.Snooper.Lights; using FModel.Views.Snooper.Models; using FModel.Views.Snooper.Shading; using OpenTK.Windowing.GraphicsLibraryFramework; +using Serilog; namespace FModel.Views.Snooper; @@ -88,6 +90,9 @@ public class Renderer : IDisposable case UStaticMesh when export.Value is UStaticMesh st: LoadStaticMesh(st, UserSettings.Default.NaniteMeshExportFormat); break; + case UGeometryCollection when export.Value is UGeometryCollection gc: + LoadStaticMesh(gc, UserSettings.Default.NaniteMeshExportFormat); + break; case USkeletalMesh when export.Value is USkeletalMesh sk: LoadSkeletalMesh(sk); break; @@ -132,7 +137,7 @@ public class Renderer : IDisposable { // do nothing, selected model has the correct skeleton for this animation } - else */if (animBase.Skeleton.TryLoad(out USkeleton skeleton)) + else */if (animBase.Skeleton?.TryLoad(out USkeleton skeleton) == true) { LoadSkeleton(skeleton); } @@ -144,7 +149,7 @@ public class Renderer : IDisposable } private void Animate(UObject anim, FGuid guid) { - if (anim is not UAnimSequenceBase animBase || !animBase.Skeleton.TryLoad(out USkeleton skeleton) || + if (anim is not UAnimSequenceBase animBase || animBase.Skeleton == null || !animBase.Skeleton.TryLoad(out USkeleton skeleton) || !Options.TryGetModel(guid, out var m) || m is not SkeletalModel model) return; @@ -361,6 +366,31 @@ public class Renderer : IDisposable Options.SelectModel(guid); } + private void LoadStaticMesh(UGeometryCollection original, ENaniteMeshFormat naniteFormat = ENaniteMeshFormat.NoNanite) + { + var guid = new FGuid((uint) original.GetFullName().GetHashCode()); + if (Options.TryGetModel(guid, out var model)) + { + model.AddInstance(Transform.Identity); + Application.Current.Dispatcher.Invoke(() => model.SetupInstances()); + return; + } + + StaticMeshDto mesh; + try + { + mesh = new StaticMeshDto(original, naniteFormat); + } + catch (Exception e) + { + Log.Error(e, "Failed to convert geometry collection"); + return; + } + + Options.Models[guid] = new StaticModel(original, mesh); + Options.SelectModel(guid); + } + private void LoadSkeletalMesh(USkeletalMesh original) { var guid = new FGuid((uint) original.GetFullName().GetHashCode());