mirror of
https://github.com/4sval/FModel.git
synced 2026-04-23 01:57:48 -05:00
refactored model viewer to allow cool things
This commit is contained in:
parent
5742f713e3
commit
0ce3b47ad3
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user