diff --git a/CUE4Parse b/CUE4Parse index ce78d925..20b76090 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit ce78d9257da0adaf6c4c151c6fc42b045c170466 +Subproject commit 20b76090cb5309be5623082eaef801791ef8ae18 diff --git a/FModel/Creator/Bases/FN/BaseIconStats.cs b/FModel/Creator/Bases/FN/BaseIconStats.cs index 706d0dd0..a1f15a80 100644 --- a/FModel/Creator/Bases/FN/BaseIconStats.cs +++ b/FModel/Creator/Bases/FN/BaseIconStats.cs @@ -142,10 +142,10 @@ namespace FModel.Creator.Bases.FN if (property.TryGetValue(out FStructFallback curve, "Curve") && curve.TryGetValue(out FName rowName, "RowName") && curve.TryGetValue(out UCurveTable curveTable, "CurveTable") && - curveTable.TryGetCurveTableRow(rowName.Text, StringComparison.OrdinalIgnoreCase, out var rowValue) && - rowValue.TryGetValue(out FSimpleCurveKey[] keys, "Keys") && keys.Length > 0) + curveTable.TryFindCurve(rowName, out var rowValue) && + rowValue is FSimpleCurve s && s.Keys.Length > 0) { - statValue = keys[0].Value; + statValue = s.Keys[0].Value; return true; } diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 1c9671dd..f383972c 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -259,33 +259,6 @@ namespace FModel.Settings set => SetProperty(ref _overridedGame, value); } - private IDictionary _overridedUEVersion = new Dictionary - { - {FGame.Unknown, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.FortniteGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.ShooterGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.DeadByDaylight, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.OakGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.Dungeons, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.WorldExplorers, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.g3, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.StateOfDecay2, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.Prospect, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.Indiana, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.RogueCompany, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.SwGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.Platform, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.BendGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.TslGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.PortalWars, UE4Version.VER_UE4_DETERMINE_BY_GAME}, - {FGame.Gameface, UE4Version.VER_UE4_DETERMINE_BY_GAME} - }; - public IDictionary OverridedUEVersion - { - get => _overridedUEVersion; - set => SetProperty(ref _overridedUEVersion, value); - } - private IDictionary> _overridedCustomVersions = new Dictionary> { {FGame.Unknown, null}, diff --git a/FModel/ViewModels/AssetsFolderViewModel.cs b/FModel/ViewModels/AssetsFolderViewModel.cs index 76ece9d0..763759cc 100644 --- a/FModel/ViewModels/AssetsFolderViewModel.cs +++ b/FModel/ViewModels/AssetsFolderViewModel.cs @@ -49,8 +49,8 @@ namespace FModel.ViewModels private set => SetProperty(ref _mountPoint, value); } - private UE4Version _version; - public UE4Version Version + private int _version; + public int Version { get => _version; private set => SetProperty(ref _version, value); @@ -61,7 +61,7 @@ namespace FModel.ViewModels public RangeObservableCollection Folders { get; } public ICollectionView FoldersView { get; } - public TreeItem(string header, string package, string mountPoint, UE4Version version, string pathHere) + public TreeItem(string header, string package, string mountPoint, int version, string pathHere) { Header = header; Package = package; @@ -129,7 +129,7 @@ namespace FModel.ViewModels if (lastNode == null) { var nodePath = builder.ToString(); - lastNode = new TreeItem(folder, item.Package, entry.Vfs.MountPoint, entry.Vfs.Ver, nodePath[..^1]); + lastNode = new TreeItem(folder, item.Package, entry.Vfs.MountPoint, entry.Vfs.Ver.Value, nodePath[..^1]); lastNode.Folders.SetSuppressionState(true); lastNode.AssetsList.Assets.SetSuppressionState(true); parentNode.Add(lastNode); @@ -168,4 +168,4 @@ namespace FModel.ViewModels }); } } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 894e7ff1..994ef42f 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -85,9 +85,8 @@ namespace FModel.ViewModels Provider = new StreamedFileProvider("FortniteLive", true, new VersionContainer( UserSettings.Default.OverridedGame[Game], - UserSettings.Default.OverridedUEVersion[Game], - UserSettings.Default.OverridedCustomVersions[Game], - UserSettings.Default.OverridedOptions[Game])); + customVersions: UserSettings.Default.OverridedCustomVersions[Game], + optionOverrides: UserSettings.Default.OverridedOptions[Game])); break; } case Constants._VAL_LIVE_TRIGGER: @@ -96,17 +95,16 @@ namespace FModel.ViewModels Provider = new StreamedFileProvider("ValorantLive", true, new VersionContainer( UserSettings.Default.OverridedGame[Game], - UserSettings.Default.OverridedUEVersion[Game], - UserSettings.Default.OverridedCustomVersions[Game], - UserSettings.Default.OverridedOptions[Game])); + customVersions: UserSettings.Default.OverridedCustomVersions[Game], + optionOverrides: UserSettings.Default.OverridedOptions[Game])); break; } default: { Game = gameDirectory.SubstringBeforeLast("\\Content").SubstringAfterLast("\\").ToEnum(FGame.Unknown); - var versions = new VersionContainer( - UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedUEVersion[Game], - UserSettings.Default.OverridedCustomVersions[Game], UserSettings.Default.OverridedOptions[Game]); + var versions = new VersionContainer(UserSettings.Default.OverridedGame[Game], + customVersions: UserSettings.Default.OverridedCustomVersions[Game], + optionOverrides: UserSettings.Default.OverridedOptions[Game]); if (Game == FGame.StateOfDecay2) Provider = new DefaultFileProvider(new DirectoryInfo(gameDirectory), new List @@ -395,7 +393,7 @@ namespace FModel.ViewModels if (VirtualPathCount > 0) return; await _threadWorkerView.Begin(cancellationToken => { - VirtualPathCount = Provider.LoadVirtualPaths(UserSettings.Default.OverridedUEVersion[Game], cancellationToken); + VirtualPathCount = Provider.LoadVirtualPaths(UserSettings.Default.OverridedGame[Game].GetVersion(), cancellationToken); if (VirtualPathCount > 0) { FLogger.AppendInformation(); diff --git a/FModel/ViewModels/ModelViewerViewModel.cs b/FModel/ViewModels/ModelViewerViewModel.cs index f7630aaa..ca1b1608 100644 --- a/FModel/ViewModels/ModelViewerViewModel.cs +++ b/FModel/ViewModels/ModelViewerViewModel.cs @@ -51,43 +51,11 @@ namespace FModel.ViewModels set => SetProperty(ref _cam, value); } - private Geometry3D _xAxis; - public Geometry3D XAxis - { - get => _xAxis; - set => SetProperty(ref _xAxis, value); - } - - private Geometry3D _yAxis; - public Geometry3D YAxis - { - get => _yAxis; - set => SetProperty(ref _yAxis, value); - } - - private Geometry3D _zAxis; - public Geometry3D ZAxis - { - get => _zAxis; - set => SetProperty(ref _zAxis, value); - } - private ModelAndCam _selectedModel; // selected mesh public ModelAndCam SelectedModel { get => _selectedModel; - set - { - SetProperty(ref _selectedModel, value); - if (_selectedModel == null) return; - - XAxis = _selectedModel.XAxis; - YAxis = _selectedModel.YAxis; - ZAxis = _selectedModel.ZAxis; - Cam.UpDirection = new Vector3D(0, 1, 0); - Cam.Position = _selectedModel.Position; - Cam.LookDirection = _selectedModel.LookDirection; - } + set => SetProperty(ref _selectedModel, value); } private readonly ObservableCollection _loadedModels; // mesh list @@ -107,6 +75,8 @@ namespace FModel.ViewModels private readonly FGame _game; private readonly int[] _facesIndex = { 1, 0, 2 }; + private readonly float[] _table = { 255 * 0.9f, 25 * 3.0f, 255 * 0.6f, 255 * 0.0f }; + private readonly int[] _table2 = { 0, 1, 2, 4, 7, 3, 5, 6 }; public ModelViewerViewModel(FGame game) { @@ -155,7 +125,11 @@ namespace FModel.ViewModels }; }); if (!valid) return; + SelectedModel = p; + Cam.UpDirection = new Vector3D(0, 1, 0); + Cam.Position = p.Position; + Cam.LookDirection = p.LookDirection; } #region PUBLIC METHODS @@ -183,6 +157,12 @@ namespace FModel.ViewModels } } + public void MaterialColorToggle() + { + if (SelectedModel == null) return; + SelectedModel.ShowMaterialColor = !SelectedModel.ShowMaterialColor; + } + public void DiffuseOnlyToggle() { if (SelectedModel == null) return; @@ -317,10 +297,12 @@ namespace FModel.ViewModels private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, ModelAndCam cam) { - foreach (var section in sections) // each section is a mesh part with its own material + for (int i = 0; i < sections.Length; i++) // each section is a mesh part with its own material { + var section = sections[i]; var builder = new MeshBuilder(); cam.TriangleCount += section.NumFaces; // NumFaces * 3 (triangle) = next section FirstIndex + for (var j = 0; j < section.NumFaces; j++) // draw a triangle for each face { foreach (var t in _facesIndex) // triangle face 1 then 0 then 2 @@ -339,18 +321,26 @@ namespace FModel.ViewModels if (section.Material == null || !section.Material.TryLoad(out var unrealMaterial)) continue; + var index = (i & 0xFFF8) | _table2[i & 7] ^ 7; var (m, isRendering, isTransparent) = LoadMaterial(unrealMaterial); + Application.Current.Dispatcher.Invoke(() => { cam.Group3d.Add(new MeshGeometryModel3D { Name = FixName(unrealMaterial.Name), Geometry = builder.ToMeshGeometry3D(), - Material = m, IsTransparent = isTransparent, IsRendering = isRendering + Material = m, IsTransparent = isTransparent, IsRendering = isRendering, + Tag = new PBRMaterial + { + AlbedoColor = new Color4(_table[C(index)] / 255, _table[C(index >> 1)] / 255, _table[C(index >> 2)] / 255, 1) + } }); }); } } + private int C(int x) => (x & 1) | ((x >> 2) & 2); + private (PBRMaterial material, bool isRendering, bool isTransparent) LoadMaterial(UMaterialInterface unrealMaterial) { var m = new PBRMaterial { RenderShadowMap = true, EnableAutoTangent = true, RenderEnvironmentMap = true }; @@ -539,6 +529,23 @@ namespace FModel.ViewModels set => SetProperty(ref _isVisible, value); } + private bool _showMaterialColor; + public bool ShowMaterialColor + { + get => _showMaterialColor; + set + { + SetProperty(ref _showMaterialColor, value); + foreach (var g in Group3d) + { + if (g is not MeshGeometryModel3D geometryModel) + continue; + + (geometryModel.Material, geometryModel.Tag) = ((PBRMaterial)geometryModel.Tag, geometryModel.Material); + } + } + } + private MeshGeometryModel3D _selectedGeometry; // selected material public MeshGeometryModel3D SelectedGeometry { diff --git a/FModel/ViewModels/SettingsViewModel.cs b/FModel/ViewModels/SettingsViewModel.cs index e047a7e1..efc59e74 100644 --- a/FModel/ViewModels/SettingsViewModel.cs +++ b/FModel/ViewModels/SettingsViewModel.cs @@ -47,13 +47,6 @@ namespace FModel.ViewModels set => SetProperty(ref _selectedUeGame, value); } - private UE4Version _selectedUeVersion; - public UE4Version SelectedUeVersion - { - get => _selectedUeVersion; - set => SetProperty(ref _selectedUeVersion, value); - } - private List _selectedCustomVersions; public List SelectedCustomVersions { @@ -127,7 +120,6 @@ namespace FModel.ViewModels public ReadOnlyObservableCollection UpdateModes { get; private set; } public ObservableCollection Presets { get; private set; } public ReadOnlyObservableCollection UeGames { get; private set; } - public ReadOnlyObservableCollection UeVersions { get; private set; } public ReadOnlyObservableCollection AssetLanguages { get; private set; } public ReadOnlyObservableCollection AesReloads { get; private set; } public ReadOnlyObservableCollection DiscordRpcs { get; private set; } @@ -146,7 +138,6 @@ namespace FModel.ViewModels private EUpdateMode _updateModeSnapshot; private string _presetSnapshot; private EGame _ueGameSnapshot; - private UE4Version _ueVersionSnapshot; private List _customVersionsSnapshot; private Dictionary _optionsSnapshot; private ELanguage _assetLanguageSnapshot; @@ -168,7 +159,6 @@ namespace FModel.ViewModels _updateModeSnapshot = UserSettings.Default.UpdateMode; _presetSnapshot = UserSettings.Default.Presets[_game]; _ueGameSnapshot = UserSettings.Default.OverridedGame[_game]; - _ueVersionSnapshot = UserSettings.Default.OverridedUEVersion[_game]; _customVersionsSnapshot = UserSettings.Default.OverridedCustomVersions[_game]; _optionsSnapshot = UserSettings.Default.OverridedOptions[_game]; _assetLanguageSnapshot = UserSettings.Default.AssetLanguage; @@ -181,7 +171,6 @@ namespace FModel.ViewModels SelectedUpdateMode = _updateModeSnapshot; SelectedPreset = _presetSnapshot; SelectedUeGame = _ueGameSnapshot; - SelectedUeVersion = _ueVersionSnapshot; SelectedCustomVersions = _customVersionsSnapshot; SelectedOptions = _optionsSnapshot; SelectedAssetLanguage = _assetLanguageSnapshot; @@ -196,7 +185,6 @@ namespace FModel.ViewModels UpdateModes = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateUpdateModes())); Presets = new ObservableCollection(EnumeratePresets()); UeGames = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateUeGames())); - UeVersions = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateUeVersions())); AssetLanguages = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateAssetLanguages())); AesReloads = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateAesReloads())); DiscordRpcs = new ReadOnlyObservableCollection(new ObservableCollection(EnumerateDiscordRpcs())); @@ -226,7 +214,6 @@ namespace FModel.ViewModels { if (_gamePreset?.Versions == null || !_gamePreset.Versions.TryGetValue(key, out var version)) return; SelectedUeGame = version.GameEnum.ToEnum(EGame.GAME_UE4_LATEST); - SelectedUeVersion = (UE4Version)version.UeVer; SelectedCustomVersions = new List(); foreach (var (guid, v) in version.CustomVersions) @@ -244,7 +231,6 @@ namespace FModel.ViewModels public void ResetPreset() { SelectedUeGame = _ueGameSnapshot; - SelectedUeVersion = _ueVersionSnapshot; SelectedCustomVersions = _customVersionsSnapshot; SelectedOptions = _optionsSnapshot; } @@ -253,7 +239,7 @@ namespace FModel.ViewModels { var ret = SettingsOut.Nothing; - if (_ueGameSnapshot != SelectedUeGame || _ueVersionSnapshot != SelectedUeVersion || // comboboxes + if (_ueGameSnapshot != SelectedUeGame || // combobox _customVersionsSnapshot != SelectedCustomVersions || _optionsSnapshot != SelectedOptions || _outputSnapshot != UserSettings.Default.OutputDirectory || // textbox _gameSnapshot != UserSettings.Default.GameDirectory) // textbox @@ -265,7 +251,6 @@ namespace FModel.ViewModels UserSettings.Default.UpdateMode = SelectedUpdateMode; UserSettings.Default.Presets[_game] = SelectedPreset; UserSettings.Default.OverridedGame[_game] = SelectedUeGame; - UserSettings.Default.OverridedUEVersion[_game] = SelectedUeVersion; UserSettings.Default.OverridedCustomVersions[_game] = SelectedCustomVersions; UserSettings.Default.OverridedOptions[_game] = SelectedOptions; UserSettings.Default.AssetLanguage = SelectedAssetLanguage; @@ -289,7 +274,6 @@ namespace FModel.ViewModels yield return Constants._NO_PRESET_TRIGGER; } private IEnumerable EnumerateUeGames() => Enum.GetValues(SelectedUeGame.GetType()).Cast(); - private IEnumerable EnumerateUeVersions() => Enum.GetValues(SelectedUeVersion.GetType()).Cast(); private IEnumerable EnumerateAssetLanguages() => Enum.GetValues(SelectedAssetLanguage.GetType()).Cast(); private IEnumerable EnumerateAesReloads() => Enum.GetValues(SelectedAesReload.GetType()).Cast(); private IEnumerable EnumerateDiscordRpcs() => Enum.GetValues(SelectedDiscordRpc.GetType()).Cast(); diff --git a/FModel/Views/ModelViewer.xaml b/FModel/Views/ModelViewer.xaml index 73ae17d5..0654382a 100644 --- a/FModel/Views/ModelViewer.xaml +++ b/FModel/Views/ModelViewer.xaml @@ -126,7 +126,7 @@ - + diff --git a/FModel/Views/ModelViewer.xaml.cs b/FModel/Views/ModelViewer.xaml.cs index 55c729ce..bed3f6ce 100644 --- a/FModel/Views/ModelViewer.xaml.cs +++ b/FModel/Views/ModelViewer.xaml.cs @@ -6,6 +6,7 @@ using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Exports.Material; using FModel.Services; using FModel.ViewModels; +using HelixToolkit.Wpf.SharpDX; using MessageBox = AdonisUI.Controls.MessageBox; using MessageBoxImage = AdonisUI.Controls.MessageBoxImage; @@ -64,12 +65,21 @@ namespace FModel.Views // case Key.D: // _applicationView.ModelViewer.DiffuseOnlyToggle(); // break; + case Key.M: + _applicationView.ModelViewer.MaterialColorToggle(); + break; case Key.Decimal: _applicationView.ModelViewer.FocusOnSelectedMesh(); break; } } + private void OnMouse3DDown(object sender, MouseDown3DEventArgs e) + { + if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || e.HitTestResult.ModelHit is not MeshGeometryModel3D m) return; + _applicationView.ModelViewer.SelectedModel.SelectedGeometry = m; + } + private void OnFocusClick(object sender, RoutedEventArgs e) => _applicationView.ModelViewer.FocusOnSelectedMesh(); diff --git a/FModel/Views/Resources/Converters/TagToColorConverter.cs b/FModel/Views/Resources/Converters/TagToColorConverter.cs new file mode 100644 index 00000000..9b7012ea --- /dev/null +++ b/FModel/Views/Resources/Converters/TagToColorConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using HelixToolkit.Wpf.SharpDX; + +namespace FModel.Views.Resources.Converters +{ + public class TagToColorConverter : IValueConverter + { + public static readonly TagToColorConverter Instance = new(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not PBRMaterial material) + return new SolidColorBrush(Colors.Red); + + return new SolidColorBrush(Color.FromScRgb( + material.AlbedoColor.Alpha, material.AlbedoColor.Red, + material.AlbedoColor.Green, material.AlbedoColor.Blue)); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 060dcf2a..cb74a243 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -653,6 +653,7 @@ + @@ -671,6 +672,10 @@ + diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index 7831875d..defe4685 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -37,7 +37,6 @@ - @@ -92,7 +91,7 @@ DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" SelectionChanged="OnSelectionChanged" Margin="0 0 0 5"> - + @@ -103,19 +102,8 @@ - - - - - - - - - - - + + @@ -126,13 +114,13 @@