model viewer kinda done

This commit is contained in:
iAmAsval 2021-11-21 01:53:18 +01:00
parent 8a0a51867e
commit c12a393f22
9 changed files with 221 additions and 162 deletions

@ -1 +1 @@
Subproject commit 8abb7f35bd92a2c1087df690124493a3484f83ec
Subproject commit 1c5654417460d291f7b80c056f1c54412c292bc8

View File

@ -156,19 +156,13 @@
<MenuItem Header="Save Textures" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Save Materials" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Save Meshes" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Save Animations" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
<MenuItem Header="Open Model Viewer" IsCheckable="True" StaysOpenOnClick="True"
<MenuItem Header="Open Meshes &amp; Materials" IsCheckable="True" StaysOpenOnClick="True"
InputGestureText="{Binding AutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
IsChecked="{Binding IsAutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
</MenuItem>
@ -847,32 +841,6 @@
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="TEX" />
</StatusBarItem>
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Materials Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MAT" />
</StatusBarItem>
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Meshes Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MSH" />
</StatusBarItem>
<StatusBarItem Width="35" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Animations Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
@ -899,7 +867,7 @@
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="SND" />
</StatusBarItem>
<StatusBarItem Width="33" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Meshes Enabled">
<StatusBarItem Width="35" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Meshes &amp; Materials Enabled">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
@ -909,7 +877,7 @@
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="OMV" />
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MSH" />
</StatusBarItem>
</StackPanel>
</StatusBarItem>

View File

@ -33,10 +33,6 @@ namespace FModel
{new KeyGesture(UserSettings.Default.AutoSaveProps.Key, UserSettings.Default.AutoSaveProps.Modifiers)}), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveTextures", typeof(MainWindow), new InputGestureCollection
{new KeyGesture(UserSettings.Default.AutoSaveTextures.Key, UserSettings.Default.AutoSaveTextures.Modifiers)}), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveMaterials", typeof(MainWindow), new InputGestureCollection
{new KeyGesture(UserSettings.Default.AutoSaveMaterials.Key, UserSettings.Default.AutoSaveMaterials.Modifiers)}), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveMeshes", typeof(MainWindow), new InputGestureCollection
{new KeyGesture(UserSettings.Default.AutoSaveMeshes.Key, UserSettings.Default.AutoSaveMeshes.Modifiers)}), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveAnimations", typeof(MainWindow), new InputGestureCollection
{new KeyGesture(UserSettings.Default.AutoSaveAnimations.Key, UserSettings.Default.AutoSaveAnimations.Modifiers)}), OnAutoTriggerExecuted));
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoOpenSounds", typeof(MainWindow), new InputGestureCollection
@ -150,12 +146,6 @@ namespace FModel
case "AutoSaveTextures":
UserSettings.Default.IsAutoSaveTextures = !UserSettings.Default.IsAutoSaveTextures;
break;
case "AutoSaveMaterials":
UserSettings.Default.IsAutoSaveMaterials = !UserSettings.Default.IsAutoSaveMaterials;
break;
case "AutoSaveMeshes":
UserSettings.Default.IsAutoSaveMeshes = !UserSettings.Default.IsAutoSaveMeshes;
break;
case "AutoSaveAnimations":
UserSettings.Default.IsAutoSaveAnimations = !UserSettings.Default.IsAutoSaveAnimations;
break;

View File

@ -86,20 +86,6 @@ namespace FModel.Settings
set => SetProperty(ref _isAutoSaveTextures, value);
}
private bool _isAutoSaveMaterials;
public bool IsAutoSaveMaterials
{
get => _isAutoSaveMaterials;
set => SetProperty(ref _isAutoSaveMaterials, value);
}
private bool _isAutoSaveMeshes;
public bool IsAutoSaveMeshes
{
get => _isAutoSaveMeshes;
set => SetProperty(ref _isAutoSaveMeshes, value);
}
private bool _isAutoSaveAnimations;
public bool IsAutoSaveAnimations
{
@ -497,35 +483,21 @@ namespace FModel.Settings
set => SetProperty(ref _autoSaveTextures, value);
}
private Hotkey _autoSaveMaterials = new(Key.F4);
public Hotkey AutoSaveMaterials
{
get => _autoSaveMaterials;
set => SetProperty(ref _autoSaveMaterials, value);
}
private Hotkey _autoSaveMeshes = new(Key.F5);
public Hotkey AutoSaveMeshes
{
get => _autoSaveMeshes;
set => SetProperty(ref _autoSaveMeshes, value);
}
private Hotkey _autoSaveAnimations = new(Key.F6);
private Hotkey _autoSaveAnimations = new(Key.F4);
public Hotkey AutoSaveAnimations
{
get => _autoSaveAnimations;
set => SetProperty(ref _autoSaveAnimations, value);
}
private Hotkey _autoOpenSounds = new(Key.F7);
private Hotkey _autoOpenSounds = new(Key.F5);
public Hotkey AutoOpenSounds
{
get => _autoOpenSounds;
set => SetProperty(ref _autoOpenSounds, value);
}
private Hotkey _autoOpenMeshes = new(Key.F8);
private Hotkey _autoOpenMeshes = new(Key.F6);
public Hotkey AutoOpenMeshes
{
get => _autoOpenMeshes;

View File

@ -59,6 +59,13 @@ namespace FModel.ViewModels
set => SetProperty(ref _game, value);
}
private bool _modelIsSwappingMaterial;
public bool ModelIsSwappingMaterial
{
get => _modelIsSwappingMaterial;
set => SetProperty(ref _modelIsSwappingMaterial, value);
}
public AbstractVfsFileProvider Provider { get; }
public GameDirectoryViewModel GameDirectory { get; }
public AssetsFolderViewModel AssetsFolder { get; }
@ -665,42 +672,35 @@ namespace FModel.ViewModels
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
return false;
}
case UStaticMesh:
case USkeletalMesh:
{
if (UserSettings.Default.IsAutoOpenMeshes)
{
Application.Current.Dispatcher.Invoke(() =>
{
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
modelViewer.Load(export);
});
}
if (UserSettings.Default.IsAutoSaveMeshes)
{
SaveExport(export);
}
return true;
}
case USkeleton when UserSettings.Default.IsAutoSaveMeshes && UserSettings.Default.SaveSkeletonAsMesh:
case UMaterialInterface when UserSettings.Default.IsAutoSaveMaterials:
case UAnimSequence when UserSettings.Default.IsAutoSaveAnimations:
{
SaveExport(export);
return true;
}
case UMaterialInstance when UserSettings.Default.IsAutoOpenMeshes &&
case UStaticMesh when UserSettings.Default.IsAutoOpenMeshes:
case USkeletalMesh when UserSettings.Default.IsAutoOpenMeshes:
case UMaterialInstance when UserSettings.Default.IsAutoOpenMeshes && !ModelIsSwappingMaterial &&
!(Game == FGame.FortniteGame && export.Owner != null && (export.Owner.Name.EndsWith($"/MI_OfferImages/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))):
export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))):
{
Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.InvokeAsync(() =>
{
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
modelViewer.Load(export);
});
return true;
}
case UMaterialInstance m when ModelIsSwappingMaterial:
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
modelViewer.Swap(m);
});
return true;
}
case USkeleton when UserSettings.Default.SaveSkeletonAsMesh:
case UAnimSequence when UserSettings.Default.IsAutoSaveAnimations:
{
SaveExport(export);
return true;
}
default:
{
using var package = new CreatorPackage(export, UserSettings.Default.CosmeticStyle);

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Media3D;
@ -15,8 +17,13 @@ using CUE4Parse_Conversion.Meshes;
using CUE4Parse_Conversion.Meshes.PSK;
using CUE4Parse_Conversion.Textures;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using FModel.Views.Resources.Controls;
using HelixToolkit.SharpDX.Core;
using HelixToolkit.Wpf.SharpDX;
using Ookii.Dialogs.Wpf;
using Serilog;
using SharpDX;
using SkiaSharp;
using Camera = HelixToolkit.Wpf.SharpDX.Camera;
@ -27,6 +34,9 @@ namespace FModel.ViewModels
{
public class ModelViewerViewModel : ViewModel
{
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
#region BINDINGS
private EffectsManager _effectManager;
public EffectsManager EffectManager
{
@ -93,6 +103,7 @@ namespace FModel.ViewModels
public bool CanAppend => SelectedModel != null;
public TextureModel HDRi { get; private set; }
#endregion
private readonly FGame _game;
private readonly int[] _facesIndex = { 1, 0, 2 };
@ -114,11 +125,12 @@ namespace FModel.ViewModels
HDRi = TextureModel.Create(cubeMap?.Stream);
}
public void LoadExport(UObject export)
public async Task LoadExport(UObject export)
{
#if DEBUG
LoadHDRi();
#endif
ModelAndCam p;
if (AppendMode && CanAppend)
p = SelectedModel;
@ -127,20 +139,23 @@ namespace FModel.ViewModels
p = new ModelAndCam(export);
_loadedModels.Add(p);
}
bool valid = export switch
bool valid = false;
await _threadWorkerView.Begin(_ =>
{
UStaticMesh st => TryLoadStaticMesh(st, ref p),
USkeletalMesh sk => TryLoadSkeletalMesh(sk, ref p),
UMaterialInstance mi => TryLoadMaterialInstance(mi, ref p),
_ => throw new ArgumentOutOfRangeException(nameof(export))
};
if (!valid)
return;
valid = export switch
{
UStaticMesh st => TryLoadStaticMesh(st, p),
USkeletalMesh sk => TryLoadSkeletalMesh(sk, p),
UMaterialInstance mi => TryLoadMaterialInstance(mi, p),
_ => throw new ArgumentOutOfRangeException(nameof(export))
};
});
if (!valid) return;
SelectedModel = p;
}
#region PUBLIC METHODS
public void RenderingToggle()
{
if (SelectedModel == null) return;
@ -152,6 +167,7 @@ namespace FModel.ViewModels
geometryModel.IsRendering = !geometryModel.IsRendering;
}
}
public void WirefreameToggle()
{
if (SelectedModel == null) return;
@ -163,6 +179,7 @@ namespace FModel.ViewModels
geometryModel.RenderWireframe = !geometryModel.RenderWireframe;
}
}
public void DiffuseOnlyToggle()
{
if (SelectedModel == null) return;
@ -181,67 +198,121 @@ namespace FModel.ViewModels
mat.RenderNormalMap = !mat.RenderNormalMap;
}
}
public void FocusOnSelectedMesh()
{
Cam.AnimateTo(SelectedModel.Position, SelectedModel.LookDirection, new Vector3D(0, 1, 0), 500);
}
private bool TryLoadMaterialInstance(UMaterialInstance materialInstance, ref ModelAndCam cam)
public void SaveLoadedModels()
{
if (_loadedModels.Count < 1) return;
var folderBrowser = new VistaFolderBrowserDialog {ShowNewFolderButton = true};
if (folderBrowser.ShowDialog() == false) return;
foreach (var model in _loadedModels)
{
var toSave = new CUE4Parse_Conversion.Exporter(model.Export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat, UserSettings.Default.MeshExportFormat);
if (toSave.TryWriteToDir(new DirectoryInfo(folderBrowser.SelectedPath), out var savedFileName))
{
Log.Information("Successfully saved {FileName}", savedFileName);
FLogger.AppendInformation();
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
}
else
{
Log.Error("{FileName} could not be saved", savedFileName);
FLogger.AppendError();
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
}
}
}
public void CopySelectedMaterialName()
{
if (SelectedModel is not { } m || m.SelectedGeometry is null)
return;
Clipboard.SetText(m.SelectedGeometry.Name.TrimEnd());
}
public async Task<bool> TryChangeSelectedMaterial(UMaterialInstance materialInstance)
{
if (SelectedModel is not { } model || model.SelectedGeometry is null)
return false;
PBRMaterial m = null;
await _threadWorkerView.Begin(_ =>
{
var (material, _, _) = LoadMaterial(materialInstance);
m = material;
});
if (m == null) return false;
model.SelectedGeometry.Material = m;
return true;
}
#endregion
private bool TryLoadMaterialInstance(UMaterialInstance materialInstance, ModelAndCam cam)
{
var builder = new MeshBuilder();
builder.AddSphere(Vector3.Zero, 10);
cam.TriangleCount = 1984; // no need to count
SetupCameraAndAxis(new FBox(new FVector(-15), new FVector(15)), ref cam);
SetupCameraAndAxis(new FBox(new FVector(-11), new FVector(11)), cam);
var (m, isRendering, isTransparent) = LoadMaterial(materialInstance);
cam.Group3d.Add(new MeshGeometryModel3D
Application.Current.Dispatcher.Invoke(() =>
{
Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1,0,0), -90)),
Name = FixName(materialInstance.Name), Geometry = builder.ToMeshGeometry3D(),
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
cam.Group3d.Add(new MeshGeometryModel3D
{
Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1,0,0), -90)),
Name = FixName(materialInstance.Name), Geometry = builder.ToMeshGeometry3D(),
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
});
});
return true;
}
private bool TryLoadStaticMesh(UStaticMesh mesh, ref ModelAndCam cam)
private bool TryLoadStaticMesh(UStaticMesh mesh, ModelAndCam cam)
{
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
{
cam = null;
return false;
}
SetupCameraAndAxis(convertedMesh.BoundingBox, ref cam);
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
foreach (var lod in convertedMesh.LODs)
{
if (lod.SkipLod) continue;
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, ref cam);
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
break;
}
return true;
}
private bool TryLoadSkeletalMesh(USkeletalMesh mesh, ref ModelAndCam cam)
private bool TryLoadSkeletalMesh(USkeletalMesh mesh, ModelAndCam cam)
{
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
{
cam = null;
return false;
}
SetupCameraAndAxis(convertedMesh.BoundingBox, ref cam);
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
foreach (var lod in convertedMesh.LODs)
{
if (lod.SkipLod) continue;
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, ref cam);
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
break;
}
return true;
}
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, ref ModelAndCam cam)
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
{
@ -266,10 +337,13 @@ namespace FModel.ViewModels
continue;
var (m, isRendering, isTransparent) = LoadMaterial(unrealMaterial);
cam.Group3d.Add(new MeshGeometryModel3D
Application.Current.Dispatcher.Invoke(() =>
{
Name = FixName(unrealMaterial.Name), Geometry = builder.ToMeshGeometry3D(),
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
cam.Group3d.Add(new MeshGeometryModel3D
{
Name = FixName(unrealMaterial.Name), Geometry = builder.ToMeshGeometry3D(),
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
});
});
}
}
@ -381,7 +455,7 @@ namespace FModel.ViewModels
return (m, isRendering, parameters.IsTransparent);
}
private void SetupCameraAndAxis(FBox box, ref ModelAndCam cam)
private void SetupCameraAndAxis(FBox box, ModelAndCam cam)
{
if (AppendMode && CanAppend) return;
var meanX = (box.Max.X + box.Min.X) / 2;

View File

@ -71,19 +71,23 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="Focus" />
<ToggleButton Grid.Row="0" Grid.Column="2" IsChecked="{Binding ModelViewer.AppendMode}" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}"
Content="{Binding ModelViewer.AppendMode, Converter={x:Static converters:BoolToToggleConverter.Instance}, StringFormat='{}Append {0}'}" />
<Button Grid.Row="0" Grid.Column="0" Content="Focus" Click="OnFocusClick" />
<ToggleButton Grid.Row="0" Grid.Column="2" IsChecked="{Binding ModelViewer.AppendMode}" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
<TextBlock Text="{Binding IsChecked, Converter={x:Static converters:BoolToToggleConverter.Instance},
StringFormat={}Append {0}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ToggleButton}}}" />
</ToggleButton>
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}"
ContentStringFormat="{}Save All Loaded Models ({0})" Content="{Binding ModelViewer.LoadedModelsView.Count}" />
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Click="Save"
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}">
<TextBlock Text="{Binding ModelViewer.LoadedModelsView.Count, StringFormat={}Save All Loaded Models ({0})}" />
</Button>
</Grid>
</Grid>
<Separator DockPanel.Dock="Top" Tag="MATERIALS" Style="{StaticResource CustomSeparator}" />
<ListBox DockPanel.Dock="Top" Style="{StaticResource MaterialsListBox}">
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Copy Name">
<MenuItem Header="Copy Name" Click="OnCopyClick">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
@ -92,7 +96,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Change Material">
<MenuItem Header="Change Material" Click="OnChangeMaterialClick">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">

View File

@ -1,13 +1,20 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using AdonisUI.Controls;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Material;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels;
using MessageBox = AdonisUI.Controls.MessageBox;
using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
namespace FModel.Views
{
public partial class ModelViewer
{
private bool _messageShown;
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
public ModelViewer()
@ -16,7 +23,27 @@ namespace FModel.Views
InitializeComponent();
}
public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export);
public async void Load(UObject export) => await _applicationView.ModelViewer.LoadExport(export);
public async void Swap(UMaterialInstance materialInstance)
{
var sucess = await _applicationView.ModelViewer.TryChangeSelectedMaterial(materialInstance);
if (sucess)
{
_applicationView.CUE4Parse.ModelIsSwappingMaterial = false;
}
else
{
MessageBox.Show(new MessageBoxModel
{
Text = "An attempt to load a material failed.",
Caption = "Error",
Icon = MessageBoxImage.Error,
Buttons = MessageBoxButtons.OkCancel(),
IsSoundEnabled = false
});
}
}
private void OnClosing(object sender, CancelEventArgs e)
{
_applicationView.ModelViewer.Clear();
@ -39,5 +66,33 @@ namespace FModel.Views
break;
}
}
private void OnFocusClick(object sender, RoutedEventArgs e)
=> _applicationView.ModelViewer.FocusOnSelectedMesh();
private void OnCopyClick(object sender, RoutedEventArgs e)
=> _applicationView.ModelViewer.CopySelectedMaterialName();
private void Save(object sender, RoutedEventArgs e)
=> _applicationView.ModelViewer.SaveLoadedModels();
private void OnChangeMaterialClick(object sender, RoutedEventArgs e)
{
_applicationView.CUE4Parse.ModelIsSwappingMaterial = true;
if (!_messageShown)
{
MessageBox.Show(new MessageBoxModel
{
Text = "Simply extract a material once FModel will be brought to the foreground. This message will be shown once per Model Viewer's lifetime, close it to begin.",
Caption = "How To Change Material?",
Icon = MessageBoxImage.Information,
IsSoundEnabled = false
});
_messageShown = true;
}
MainWindow.YesWeCats.Activate();
}
}
}

View File

@ -320,7 +320,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -361,32 +360,29 @@
<TextBlock Grid.Row="10" Grid.Column="0" Text="Auto Save Textures *" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Materials *" VerticalAlignment="Center" Margin="0 0 0 5" />
<TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Animations *" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Save Meshes *" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Save Animations *" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="14" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
<controls:HotkeyTextBox Grid.Row="14" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
HotKey="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Open Meshes &amp; Materials *" VerticalAlignment="Center" />
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
HotKey="{Binding AutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<Separator Grid.Row="15" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
<Separator Grid.Row="14" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
<TextBlock Grid.Row="16" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="15" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="15" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="17" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="17" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="16" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="18" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="17" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="17" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
<TextBlock Grid.Row="19" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="19" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
<TextBlock Grid.Row="18" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
</Grid>
</DataTemplate>