refactored model viewer to allow cool things

This commit is contained in:
iAmAsval 2021-11-19 00:44:55 +01:00
parent 5742f713e3
commit 0ce3b47ad3
5 changed files with 102 additions and 37 deletions

View File

@ -92,7 +92,7 @@ namespace FModel.ViewModels
AesManager = new AesManagerViewModel(CUE4Parse);
MapViewer = new MapViewerViewModel(CUE4Parse);
AudioPlayer = new AudioPlayerViewModel();
ModelViewer = new ModelViewerViewModel();
ModelViewer = new ModelViewerViewModel(CUE4Parse.Game);
Status = EStatusKind.Ready;
}

View File

@ -1,10 +1,6 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Media.Media3D;
using CUE4Parse.UE4.Assets.Exports;
@ -17,11 +13,8 @@ using CUE4Parse_Conversion.Meshes;
using CUE4Parse_Conversion.Meshes.PSK;
using CUE4Parse_Conversion.Textures;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using HelixToolkit.SharpDX.Core;
using HelixToolkit.Wpf.SharpDX;
using RestSharp;
using SharpDX;
using SkiaSharp;
using Camera = HelixToolkit.Wpf.SharpDX.Camera;
@ -74,6 +67,21 @@ namespace FModel.ViewModels
set => SetProperty(ref _appendModeEnabled, value);
}
private MeshGeometryModel3D _selectedGeometry;
public MeshGeometryModel3D SelectedGeometry
{
get => _selectedGeometry;
set
{
SetProperty(ref _selectedGeometry, value);
if (!_geometries.TryGetValue(_selectedGeometry, out var camAxis)) return;
XAxis = camAxis.XAxis;
YAxis = camAxis.YAxis;
ZAxis = camAxis.ZAxis;
}
}
private ObservableElement3DCollection _group3d;
public ObservableElement3DCollection Group3d
{
@ -83,12 +91,17 @@ namespace FModel.ViewModels
public TextureModel HDRi { get; private set; }
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
private readonly FGame _game;
private readonly int[] _facesIndex = { 1, 0, 2 };
private readonly List<UObject> _meshes;
private readonly Dictionary<MeshGeometryModel3D, CamAxisHolder> _geometries;
public ModelViewerViewModel()
public ModelViewerViewModel(FGame game)
{
_game = game;
_meshes = new List<UObject>();
_geometries = new Dictionary<MeshGeometryModel3D, CamAxisHolder>();
EffectManager = new DefaultEffectsManager();
Group3d = new ObservableElement3DCollection();
Cam = new PerspectiveCamera { NearPlaneDistance = 0.1, FarPlaneDistance = double.PositiveInfinity, FieldOfView = 90 };
@ -107,6 +120,8 @@ namespace FModel.ViewModels
LoadHDRi();
#endif
if (!AppendModeEnabled) Clear();
_meshes.Add(export);
switch (export)
{
case UStaticMesh st:
@ -115,9 +130,27 @@ namespace FModel.ViewModels
case USkeletalMesh sk:
LoadSkeletalMesh(sk);
break;
default:
default: // idiot
throw new ArgumentOutOfRangeException();
}
if (_geometries.Count < 1) return;
foreach (var geometry in _geometries.Keys)
{
// Tag is used as a flag to tell if the geometry is already in Group3d
if (geometry.Tag is not (bool and false)) continue;
geometry.Tag = true;
Group3d.Add(geometry);
}
if (AppendModeEnabled || Group3d[0] is not MeshGeometryModel3D selected ||
!_geometries.TryGetValue(selected, out var camAxis)) return;
SelectedGeometry = selected;
Cam.UpDirection = new Vector3D(0, 1, 0);
Cam.Position = camAxis.Position;
Cam.LookDirection = camAxis.LookDirection;
}
public void RenderingToggle()
@ -163,6 +196,12 @@ namespace FModel.ViewModels
}
}
public void FocusOnSelectedGeometry()
{
if (!_geometries.TryGetValue(_selectedGeometry, out var camAxis)) return;
Cam.AnimateTo(camAxis.Position, camAxis.LookDirection, new Vector3D(0, 1, 0), 500);
}
private void LoadStaticMesh(UStaticMesh mesh)
{
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
@ -170,12 +209,11 @@ namespace FModel.ViewModels
return;
}
if (!AppendModeEnabled) SetupCameraAndAxis(convertedMesh.BoundingBox);
var camAxis = SetupCameraAndAxis(convertedMesh.BoundingBox);
foreach (var lod in convertedMesh.LODs)
{
if (lod.SkipLod) continue;
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value);
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, camAxis);
break;
}
}
@ -187,17 +225,16 @@ namespace FModel.ViewModels
return;
}
if (!AppendModeEnabled) SetupCameraAndAxis(convertedMesh.BoundingBox);
var camAxis = SetupCameraAndAxis(convertedMesh.BoundingBox);
foreach (var lod in convertedMesh.LODs)
{
if (lod.SkipLod) continue;
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value);
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, camAxis);
break;
}
}
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices)
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, CamAxisHolder camAxis)
{
foreach (var section in sections) // each section is a mesh part with its own material
{
@ -233,7 +270,7 @@ namespace FModel.ViewModels
if (parameters.Normal is UTexture2D normal)
m.NormalMap = new TextureModel(normal.Decode()?.Encode().AsStream());
if (_applicationView.CUE4Parse.Game == FGame.FortniteGame)
if (_game == FGame.FortniteGame)
{
// Fortnite's Specular Texture Channels
// R Specular
@ -293,39 +330,40 @@ namespace FModel.ViewModels
m = new PBRMaterial { AlbedoColor = new Color4(1, 0, 0, 1) }; //PhongMaterials.Red;
}
Group3d.Add(new MeshGeometryModel3D
_geometries.Add(new MeshGeometryModel3D
{
Name = unrealMaterial.Name.Replace('-', '_'),
Geometry = builder.ToMeshGeometry3D(),
Material = m,
IsRendering = isRendering
});
Name = unrealMaterial.Name.Replace('-', '_'), Geometry = builder.ToMeshGeometry3D(),
Material = m, IsRendering = isRendering, Tag = false // flag
}, camAxis);
}
}
private void SetupCameraAndAxis(FBox box)
private CamAxisHolder SetupCameraAndAxis(FBox box)
{
var ret = new CamAxisHolder();
var meanX = (box.Max.X + box.Min.X) / 2;
var meanY = (box.Max.Y + box.Min.Y) / 2;
var meanZ = (box.Max.Z + box.Min.Z) / 2;
var lineBuilder = new LineBuilder();
lineBuilder.AddLine(new Vector3(box.Min.X, meanZ, -meanY), new Vector3(box.Max.X, meanZ, -meanY));
XAxis = lineBuilder.ToLineGeometry3D();
ret.XAxis = lineBuilder.ToLineGeometry3D();
lineBuilder = new LineBuilder();
lineBuilder.AddLine(new Vector3(meanX, box.Min.Z, -meanY), new Vector3(meanX, box.Max.Z, -meanY));
YAxis = lineBuilder.ToLineGeometry3D();
ret.YAxis = lineBuilder.ToLineGeometry3D();
lineBuilder = new LineBuilder();
lineBuilder.AddLine(new Vector3(meanX, meanZ, -box.Min.Y), new Vector3(meanX, meanZ, -box.Max.Y));
ZAxis = lineBuilder.ToLineGeometry3D();
ret.ZAxis = lineBuilder.ToLineGeometry3D();
Cam.UpDirection = new Vector3D(0, 1, 0);
Cam.Position = new Point3D(box.Max.X + meanX * 2, meanZ * 1.25, -box.Min.Y -meanY * 2);
Cam.LookDirection = new Vector3D(-Cam.Position.X, 0, -Cam.Position.Z - meanY);
ret.Position = new Point3D(box.Max.X + meanX * 2, meanZ, -box.Min.Y - meanY * 2);
ret.LookDirection = new Vector3D(-ret.Position.X, 0, -ret.Position.Z - meanY);
return ret;
}
private void Clear()
{
_meshes.Clear();
_geometries.Clear();
foreach (var g in Group3d.ToList())
{
g.Dispose();
@ -333,4 +371,13 @@ namespace FModel.ViewModels
}
}
}
public class CamAxisHolder
{
public Point3D Position;
public Vector3D LookDirection;
public Geometry3D XAxis;
public Geometry3D YAxis;
public Geometry3D ZAxis;
}
}

View File

@ -34,7 +34,7 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Style="{StaticResource MaterialsListBox}" />
<ListBox Grid.Row="0" Style="{StaticResource MaterialsListBox}" MouseDoubleClick="OnMouseDoubleClick" />
<CheckBox Content="Append Mode" VerticalAlignment="Stretch" HorizontalAlignment="Center" Margin="0 0 0 5"
Grid.Row="1" IsChecked="{Binding ModelViewer.AppendModeEnabled}" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
</Grid>
@ -52,14 +52,14 @@
<MouseBinding Command="helix:ViewportCommands.Pan" Gesture="MiddleClick" />
</helix:Viewport3DX.InputBindings>
<helix:EnvironmentMap3D Texture="{Binding ModelViewer.HDRi}"/>
<helix:EnvironmentMap3D Texture="{Binding ModelViewer.HDRi}" />
<helix:DirectionalLight3D Direction="{Binding ModelViewer.Cam.LookDirection}" Color="White" />
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.XAxis}" Color="#FC3854" />
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.YAxis}" Color="#85CB22" />
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.ZAxis}" Color="#388EED" />
<helix:GroupModel3D x:Name="MyAntiCrashGroup" ItemsSource="{Binding ModelViewer.Group3d}" />
<helix:GroupModel3D x:Name="MyAntiCrashGroup" ItemsSource="{Binding ModelViewer.Group3d}" Mouse3DDown="OnMouse3DDown" />
</helix:Viewport3DX>
</Grid>
</adonisControls:AdonisWindow>

View File

@ -1,8 +1,10 @@
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using CUE4Parse.UE4.Assets.Exports;
using FModel.Services;
using FModel.ViewModels;
using HelixToolkit.Wpf.SharpDX;
namespace FModel.Views
{
@ -38,5 +40,20 @@ namespace FModel.Views
break;
}
}
private void OnMouse3DDown(object sender, MouseDown3DEventArgs e)
{
if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || e.HitTestResult.ModelHit is not MeshGeometryModel3D m) return;
_applicationView.ModelViewer.SelectedGeometry = m;
}
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is not TextBlock or Image)
return;
if (!_applicationView.IsReady || sender is not ListBox { SelectedItem: MeshGeometryModel3D }) return;
_applicationView.ModelViewer.FocusOnSelectedGeometry();
}
}
}

View File

@ -619,6 +619,7 @@
<Style x:Key="MaterialsListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
<Setter Property="ItemsSource" Value="{Binding ModelViewer.Group3d, IsAsync=True}" />
<Setter Property="SelectedItem" Value="{Binding ModelViewer.SelectedGeometry}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="NeverExpand"/>
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/>