mirror of
https://github.com/4sval/FModel.git
synced 2026-04-21 09:07:45 -05:00
model viewer kinda done
This commit is contained in:
parent
8a0a51867e
commit
c12a393f22
|
|
@ -1 +1 @@
|
|||
Subproject commit 8abb7f35bd92a2c1087df690124493a3484f83ec
|
||||
Subproject commit 1c5654417460d291f7b80c056f1c54412c292bc8
|
||||
|
|
@ -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 & 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 & 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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 & 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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user