scuffed but working model viewer

This commit is contained in:
iAmAsval 2021-07-13 16:55:26 +02:00
parent 08050aee78
commit 983d9da845
7 changed files with 236 additions and 2 deletions

@ -1 +1 @@
Subproject commit 291277cdfe645f8c9759961f0851366bffa2f233
Subproject commit e5861dac2390041a9b70c6623a99646c1503d4e5

View File

@ -108,6 +108,7 @@
<PackageReference Include="CSCore" Version="1.2.1.2" />
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="EpicManifestParser" Version="1.2.0" />
<PackageReference Include="HelixToolkit.SharpDX.Core.Wpf" Version="2.17.0" />
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NVorbis" Version="0.10.1" />

View File

@ -160,6 +160,8 @@ namespace FModel.ViewModels.ApiEndpoints
{
var request = new RestRequest(args.ChangelogURL, Method.GET);
var response = await _client.ExecuteAsync(request).ConfigureAwait(false);
if (string.IsNullOrEmpty(response.Content)) return;
_applicationView.CUE4Parse.TabControl.AddTab($"Release Notes: {args.CurrentVersion}");
_applicationView.CUE4Parse.TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("changelog");
_applicationView.CUE4Parse.TabControl.SelectedTab.SetDocumentText(response.Content, false);

View File

@ -590,7 +590,10 @@ namespace FModel.ViewModels
}
else
{
// preview
Application.Current.Dispatcher.Invoke(delegate
{
Helper.OpenWindow<AdonisWindow>("Model Viewer", () => new ModelViewer(export).Show());
});
}
return false;
}

View File

@ -0,0 +1,146 @@
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
using FModel.Framework;
using HelixToolkit.SharpDX.Core;
using HelixToolkit.Wpf.SharpDX;
using SharpDX;
namespace FModel.ViewModels
{
public class ModelViewerViewModel : ViewModel
{
private EffectsManager _effectManager;
public EffectsManager EffectManager
{
get => _effectManager;
set => SetProperty(ref _effectManager, value);
}
private Camera _cam;
public Camera Cam
{
get => _cam;
set => SetProperty(ref _cam, value);
}
private Geometry3D _cubeMesh;
public Geometry3D CubeMesh
{
get => _cubeMesh;
set => SetProperty(ref _cubeMesh, value);
}
private Material _red;
public Material Red
{
get => _red;
set => SetProperty(ref _red, value);
}
public ModelViewerViewModel(UStaticMesh? mesh)
{
EffectManager = new DefaultEffectsManager();
Cam = new PerspectiveCamera();
if (mesh?.RenderData == null || mesh.RenderData.LODs.Length < 1) return;
var builder = new MeshBuilder();
for (var i = 0; i < mesh.RenderData.LODs.Length; i++)
{
if (mesh.RenderData.LODs[i] is not
{
VertexBuffer: not null,
PositionVertexBuffer: not null,
ColorVertexBuffer: not null,
IndexBuffer: not null
} srcLod) continue;
var numVerts = srcLod.PositionVertexBuffer.Verts.Length;
for (var j = 0; j < numVerts; j++)
{
var suv = srcLod.VertexBuffer.UV[j];
builder.Positions.Add(new Vector3(srcLod.PositionVertexBuffer.Verts[j].X, srcLod.PositionVertexBuffer.Verts[j].Y, srcLod.PositionVertexBuffer.Verts[j].Z));
builder.Normals.Add(new Vector3(suv.Normal[2].X, suv.Normal[2].Y, suv.Normal[2].Z));
}
for (var j = 0; j < srcLod.IndexBuffer.Indices16.Length; j++)
{
builder.TriangleIndices.Add(srcLod.IndexBuffer[j]);
}
break;
}
builder.Scale(0.05, 0.05, 0.05);
CubeMesh = builder.ToMesh();
Red = PhongMaterials.Red;
}
public ModelViewerViewModel(USkeletalMesh? mesh)
{
EffectManager = new DefaultEffectsManager();
Cam = new PerspectiveCamera();
if (mesh == null || mesh.LODModels?.Length < 1) return;
var builder = new MeshBuilder();
for (var i = 0; i < mesh.LODModels.Length; i++)
{
if (mesh.LODModels[i] is not { } srcLod) continue;
var bUseVerticesFromSections = false;
var vertexCount = srcLod.VertexBufferGPUSkin.GetVertexCount();
if (vertexCount == 0 && srcLod.Sections.Length > 0 && srcLod.Sections[0].SoftVertices.Length > 0)
{
bUseVerticesFromSections = true;
for (var j = 0; j < srcLod.Sections.Length; j++)
{
vertexCount += srcLod.Sections[i].SoftVertices.Length;
}
}
var chunkIndex = -1;
var chunkVertexIndex = 0;
long lastChunkVertex = -1;
var vertBuffer = srcLod.VertexBufferGPUSkin;
for (var j = 0; j < vertexCount; j++)
{
while (j >= lastChunkVertex) // this will fix any issues with empty chunks or sections
{
// proceed to next chunk or section
if (srcLod.Chunks.Length > 0)
{
// pre-UE4.13 code: chunks
var c = srcLod.Chunks[++chunkIndex];
lastChunkVertex = c.BaseVertexIndex + c.NumRigidVertices + c.NumSoftVertices;
}
else
{
// UE4.13+ code: chunk information migrated to sections
var s = srcLod.Sections[++chunkIndex];
lastChunkVertex = s.BaseVertexIndex + s.NumVertices;
}
chunkVertexIndex = 0;
}
FSkelMeshVertexBase v;
if (bUseVerticesFromSections)
v = srcLod.Sections[chunkIndex].SoftVertices[chunkVertexIndex++];
else if (!vertBuffer.bUseFullPrecisionUVs)
v = vertBuffer.VertsHalf[j];
else
v = vertBuffer.VertsFloat[j];
builder.Positions.Add(new Vector3(v.Pos.X, v.Pos.Y, v.Pos.Z));
builder.Normals.Add(new Vector3(v.Normal[2].X, v.Normal[2].Y, v.Normal[2].Z));
}
for (var j = 0; j < srcLod.Indices.Indices16.Length; j++)
{
builder.TriangleIndices.Add(srcLod.Indices.Indices16[j]);
}
break;
}
builder.Scale(0.05, 0.05, 0.05);
CubeMesh = builder.ToMesh();
Red = PhongMaterials.Red;
}
}
}

View File

@ -0,0 +1,59 @@
<adonisControls:AdonisWindow x:Class="FModel.Views.ModelViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FModel"
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
xmlns:helix="http://helix-toolkit.org/wpf/SharpDX"
WindowStartupLocation="CenterScreen" ResizeMode="CanResize" IconVisibility="Collapsed"
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.65'}">
<adonisControls:AdonisWindow.Style>
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
<Setter Property="Title" Value="Model Viewer" />
</Style>
</adonisControls:AdonisWindow.Style>
<adonisControls:AdonisWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</adonisControls:AdonisWindow.Resources>
<Grid>
<helix:Viewport3DX Title="{Binding Title}"
ShowCoordinateSystem="True"
IsCoordinateSystemMoverEnabled="False"
CoordinateSystemAxisXColor="#FC3854"
CoordinateSystemAxisYColor="#85CB22"
CoordinateSystemAxisZColor="#388EED"
IsChangeFieldOfViewEnabled="False"
ShowViewCube="False"
ShowCameraTarget="False"
Camera="{Binding Cam}"
IsShadowMappingEnabled="True"
EnableSSAO="True"
MSAA="Maximum"
FXAALevel="Ultra"
SSAOQuality="High"
UseDefaultGestures="False"
EffectsManager="{Binding EffectManager}"
BackgroundColor="#262630">
<helix:Viewport3DX.InputBindings>
<MouseBinding Command="helix:ViewportCommands.Rotate" Gesture="MiddleClick" />
<MouseBinding Command="helix:ViewportCommands.Pan" Gesture="Shift+MiddleClick"></MouseBinding>
</helix:Viewport3DX.InputBindings>
<helix:GroupModel3D>
<helix:DirectionalLight3D Direction="0, 0, -1" Color="White" />
<helix:DirectionalLight3D Direction="0, -1, 0" Color="White"/>
</helix:GroupModel3D>
<helix:GroupModel3D>
<helix:SpotLight3D Direction="0, 1, 0" Color="Red" Position="0, -2, 0"/>
<helix:PointLight3D Position="3, 0, 0" Color="Green"/>
</helix:GroupModel3D>
<helix:MeshGeometryModel3D Geometry="{Binding CubeMesh}" Material="{Binding Red}" />
</helix:Viewport3DX>
</Grid>
</adonisControls:AdonisWindow>

View File

@ -0,0 +1,23 @@
using System;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
using FModel.ViewModels;
namespace FModel.Views
{
public partial class ModelViewer
{
public ModelViewer(UObject export)
{
DataContext = export switch
{
UStaticMesh st => new ModelViewerViewModel(st),
USkeletalMesh sk => new ModelViewerViewModel(sk),
_ => throw new NotImplementedException()
};
InitializeComponent();
}
}
}