mirror of
https://github.com/4sval/FModel.git
synced 2026-04-13 13:06:38 -05:00
refactored model viewer
This commit is contained in:
parent
22d1a32651
commit
8a0a51867e
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media.Media3D;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
|
|
@ -60,50 +62,48 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _zAxis, value);
|
||||
}
|
||||
|
||||
private bool _appendModeEnabled;
|
||||
public bool AppendModeEnabled
|
||||
private ModelAndCam _selectedModel; // selected mesh
|
||||
public ModelAndCam SelectedModel
|
||||
{
|
||||
get => _appendModeEnabled;
|
||||
set => SetProperty(ref _appendModeEnabled, value);
|
||||
}
|
||||
|
||||
private MeshGeometryModel3D _selectedGeometry;
|
||||
public MeshGeometryModel3D SelectedGeometry
|
||||
{
|
||||
get => _selectedGeometry;
|
||||
get => _selectedModel;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedGeometry, value);
|
||||
if (_selectedGeometry == null || !_geometries.TryGetValue(_selectedGeometry, out var camAxis)) return;
|
||||
SetProperty(ref _selectedModel, value);
|
||||
if (_selectedModel == null) return;
|
||||
|
||||
XAxis = camAxis.XAxis;
|
||||
YAxis = camAxis.YAxis;
|
||||
ZAxis = camAxis.ZAxis;
|
||||
XAxis = _selectedModel.XAxis;
|
||||
YAxis = _selectedModel.YAxis;
|
||||
ZAxis = _selectedModel.ZAxis;
|
||||
Cam.UpDirection = new Vector3D(0, 1, 0);
|
||||
Cam.Position = _selectedModel.Position;
|
||||
Cam.LookDirection = _selectedModel.LookDirection;
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableElement3DCollection _group3d;
|
||||
public ObservableElement3DCollection Group3d
|
||||
private readonly ObservableCollection<ModelAndCam> _loadedModels; // mesh list
|
||||
public ICollectionView LoadedModelsView { get; }
|
||||
|
||||
private bool _appendMode;
|
||||
public bool AppendMode
|
||||
{
|
||||
get => _group3d;
|
||||
set => SetProperty(ref _group3d, value);
|
||||
get => _appendMode;
|
||||
set => SetProperty(ref _appendMode, value);
|
||||
}
|
||||
|
||||
public bool CanAppend => SelectedModel != null;
|
||||
|
||||
public TextureModel HDRi { get; private set; }
|
||||
|
||||
private readonly FGame _game;
|
||||
private readonly int[] _facesIndex = { 1, 0, 2 };
|
||||
private readonly List<UObject> _meshes;
|
||||
private readonly Dictionary<MeshGeometryModel3D, CamAxisHolder> _geometries;
|
||||
|
||||
public ModelViewerViewModel(FGame game)
|
||||
{
|
||||
_game = game;
|
||||
_meshes = new List<UObject>();
|
||||
_geometries = new Dictionary<MeshGeometryModel3D, CamAxisHolder>();
|
||||
_loadedModels = new ObservableCollection<ModelAndCam>();
|
||||
|
||||
EffectManager = new DefaultEffectsManager();
|
||||
Group3d = new ObservableElement3DCollection();
|
||||
LoadedModelsView = new ListCollectionView(_loadedModels);
|
||||
Cam = new PerspectiveCamera { NearPlaneDistance = 0.1, FarPlaneDistance = double.PositiveInfinity, FieldOfView = 90 };
|
||||
LoadHDRi();
|
||||
}
|
||||
|
|
@ -119,46 +119,32 @@ namespace FModel.ViewModels
|
|||
#if DEBUG
|
||||
LoadHDRi();
|
||||
#endif
|
||||
if (!AppendModeEnabled) Clear();
|
||||
|
||||
_meshes.Add(export);
|
||||
switch (export)
|
||||
ModelAndCam p;
|
||||
if (AppendMode && CanAppend)
|
||||
p = SelectedModel;
|
||||
else
|
||||
{
|
||||
case UStaticMesh st:
|
||||
LoadStaticMesh(st);
|
||||
break;
|
||||
case USkeletalMesh sk:
|
||||
LoadSkeletalMesh(sk);
|
||||
break;
|
||||
case UMaterialInstance mi:
|
||||
LoadMaterialInstance(mi);
|
||||
break;
|
||||
default: // idiot
|
||||
throw new ArgumentOutOfRangeException();
|
||||
p = new ModelAndCam(export);
|
||||
_loadedModels.Add(p);
|
||||
}
|
||||
|
||||
if (_geometries.Count < 1) return;
|
||||
foreach (var geometry in _geometries.Keys)
|
||||
bool valid = export switch
|
||||
{
|
||||
// Tag is used as a flag to tell if the geometry is already in Group3d
|
||||
if (geometry.Tag is not (bool and false)) continue;
|
||||
UStaticMesh st => TryLoadStaticMesh(st, ref p),
|
||||
USkeletalMesh sk => TryLoadSkeletalMesh(sk, ref p),
|
||||
UMaterialInstance mi => TryLoadMaterialInstance(mi, ref p),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(export))
|
||||
};
|
||||
|
||||
geometry.Tag = true;
|
||||
Group3d.Add(geometry);
|
||||
}
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
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;
|
||||
SelectedModel = p;
|
||||
}
|
||||
|
||||
public void RenderingToggle()
|
||||
{
|
||||
foreach (var g in Group3d)
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
if (g is not MeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
|
@ -166,10 +152,10 @@ namespace FModel.ViewModels
|
|||
geometryModel.IsRendering = !geometryModel.IsRendering;
|
||||
}
|
||||
}
|
||||
|
||||
public void WirefreameToggle()
|
||||
{
|
||||
foreach (var g in Group3d)
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
if (g is not MeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
|
@ -177,88 +163,90 @@ namespace FModel.ViewModels
|
|||
geometryModel.RenderWireframe = !geometryModel.RenderWireframe;
|
||||
}
|
||||
}
|
||||
|
||||
public void DiffuseOnlyToggle()
|
||||
{
|
||||
foreach (var g in Group3d)
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
if (g is not MeshGeometryModel3D geometryModel)
|
||||
if (g is not MeshGeometryModel3D { Material: PBRMaterial mat })
|
||||
continue;
|
||||
|
||||
if (geometryModel.Material is PBRMaterial mat)
|
||||
{
|
||||
//mat.RenderAmbientOcclusionMap = !mat.RenderAmbientOcclusionMap;
|
||||
mat.RenderDisplacementMap = !mat.RenderDisplacementMap;
|
||||
//mat.RenderEmissiveMap = !mat.RenderEmissiveMap;
|
||||
mat.RenderEnvironmentMap = !mat.RenderEnvironmentMap;
|
||||
mat.RenderIrradianceMap = !mat.RenderIrradianceMap;
|
||||
mat.RenderRoughnessMetallicMap = !mat.RenderRoughnessMetallicMap;
|
||||
mat.RenderShadowMap = !mat.RenderShadowMap;
|
||||
mat.RenderNormalMap = !mat.RenderNormalMap;
|
||||
}
|
||||
//mat.RenderAmbientOcclusionMap = !mat.RenderAmbientOcclusionMap;
|
||||
mat.RenderDisplacementMap = !mat.RenderDisplacementMap;
|
||||
//mat.RenderEmissiveMap = !mat.RenderEmissiveMap;
|
||||
mat.RenderEnvironmentMap = !mat.RenderEnvironmentMap;
|
||||
mat.RenderIrradianceMap = !mat.RenderIrradianceMap;
|
||||
mat.RenderRoughnessMetallicMap = !mat.RenderRoughnessMetallicMap;
|
||||
mat.RenderShadowMap = !mat.RenderShadowMap;
|
||||
mat.RenderNormalMap = !mat.RenderNormalMap;
|
||||
}
|
||||
}
|
||||
|
||||
public void FocusOnSelectedGeometry()
|
||||
public void FocusOnSelectedMesh()
|
||||
{
|
||||
if (!_geometries.TryGetValue(_selectedGeometry, out var camAxis)) return;
|
||||
Cam.AnimateTo(camAxis.Position, camAxis.LookDirection, new Vector3D(0, 1, 0), 500);
|
||||
Cam.AnimateTo(SelectedModel.Position, SelectedModel.LookDirection, new Vector3D(0, 1, 0), 500);
|
||||
}
|
||||
|
||||
private void LoadMaterialInstance(UMaterialInstance materialInstance)
|
||||
private bool TryLoadMaterialInstance(UMaterialInstance materialInstance, ref ModelAndCam cam)
|
||||
{
|
||||
var builder = new MeshBuilder();
|
||||
builder.AddSphere(Vector3.Zero, 10);
|
||||
|
||||
var camAxis = SetupCameraAndAxis(new FBox(new FVector(-15), new FVector(15)));
|
||||
var (m, isRendering, _) = LoadMaterial(materialInstance);
|
||||
SetupCameraAndAxis(new FBox(new FVector(-15), new FVector(15)), ref cam);
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(materialInstance);
|
||||
|
||||
_geometries.Add(new MeshGeometryModel3D
|
||||
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, IsRendering = isRendering, Tag = false // flag
|
||||
}, camAxis);
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LoadStaticMesh(UStaticMesh mesh)
|
||||
private bool TryLoadStaticMesh(UStaticMesh mesh, ref ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
cam = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var camAxis = SetupCameraAndAxis(convertedMesh.BoundingBox);
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, ref cam);
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, camAxis);
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, ref cam);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LoadSkeletalMesh(USkeletalMesh mesh)
|
||||
private bool TryLoadSkeletalMesh(USkeletalMesh mesh, ref ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
cam = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var camAxis = SetupCameraAndAxis(convertedMesh.BoundingBox);
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, ref cam);
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, camAxis);
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, ref cam);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, CamAxisHolder camAxis)
|
||||
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, ref ModelAndCam cam)
|
||||
{
|
||||
foreach (var section in sections) // each section is a mesh part with its own material
|
||||
{
|
||||
var builder = new MeshBuilder();
|
||||
// NumFaces * 3 (triangle) = next section FirstIndex
|
||||
cam.TriangleCount += section.NumFaces; // NumFaces * 3 (triangle) = next section FirstIndex
|
||||
for (var j = 0; j < section.NumFaces; j++) // draw a triangle for each face
|
||||
{
|
||||
foreach (var t in _facesIndex) // triangle face 1 then 0 then 2
|
||||
|
|
@ -278,11 +266,11 @@ namespace FModel.ViewModels
|
|||
continue;
|
||||
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(unrealMaterial);
|
||||
_geometries.Add(new MeshGeometryModel3D
|
||||
cam.Group3d.Add(new MeshGeometryModel3D
|
||||
{
|
||||
Name = FixName(unrealMaterial.Name), Geometry = builder.ToMeshGeometry3D(),
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering, Tag = false // flag
|
||||
}, camAxis);
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -330,9 +318,24 @@ namespace FModel.ViewModels
|
|||
}
|
||||
case FGame.ShooterGame:
|
||||
{
|
||||
// Valorant's Specular Texture Channels
|
||||
// R Metallic
|
||||
// G Specular
|
||||
// B Roughness
|
||||
unsafe
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset], d[offset+2]) = (d[offset+2], d[offset]); // swap R and B
|
||||
(d[offset], d[offset+1]) = (d[offset+1], d[offset]); // swap B and G
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
parameters.RoughnessValue = 1;
|
||||
parameters.MetallicValue = 1;
|
||||
goto case FGame.Gameface;
|
||||
break;
|
||||
}
|
||||
case FGame.Gameface:
|
||||
{
|
||||
|
|
@ -378,26 +381,25 @@ namespace FModel.ViewModels
|
|||
return (m, isRendering, parameters.IsTransparent);
|
||||
}
|
||||
|
||||
private CamAxisHolder SetupCameraAndAxis(FBox box)
|
||||
private void SetupCameraAndAxis(FBox box, ref ModelAndCam cam)
|
||||
{
|
||||
var ret = new CamAxisHolder();
|
||||
if (AppendMode && CanAppend) return;
|
||||
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));
|
||||
ret.XAxis = lineBuilder.ToLineGeometry3D();
|
||||
cam.XAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(meanX, box.Min.Z, meanY), new Vector3(meanX, box.Max.Z, meanY));
|
||||
ret.YAxis = lineBuilder.ToLineGeometry3D();
|
||||
cam.YAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(meanX, meanZ, box.Min.Y), new Vector3(meanX, meanZ, box.Max.Y));
|
||||
ret.ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
cam.ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
|
||||
ret.Position = new Point3D(box.Max.X + meanX * 2, meanZ, box.Min.Y + meanY * 2);
|
||||
ret.LookDirection = new Vector3D(-ret.Position.X + meanX, 0, -ret.Position.Z + meanY);
|
||||
return ret;
|
||||
cam.Position = new Point3D(box.Max.X + meanX * 2, meanZ, box.Min.Y + meanY * 2);
|
||||
cam.LookDirection = new Vector3D(-cam.Position.X + meanX, 0, -cam.Position.Z + meanY);
|
||||
}
|
||||
|
||||
private string FixName(string input)
|
||||
|
|
@ -411,10 +413,51 @@ namespace FModel.ViewModels
|
|||
return input.Replace('-', '_');
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
public void Clear()
|
||||
{
|
||||
_meshes.Clear();
|
||||
_geometries.Clear();
|
||||
foreach (var g in _loadedModels.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
_loadedModels.Remove(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelAndCam : ViewModel
|
||||
{
|
||||
public UObject Export { get; }
|
||||
public Point3D Position { get; set; }
|
||||
public Vector3D LookDirection { get; set; }
|
||||
public Geometry3D XAxis { get; set; }
|
||||
public Geometry3D YAxis { get; set; }
|
||||
public Geometry3D ZAxis { get; set; }
|
||||
public int TriangleCount { get; set; }
|
||||
|
||||
private MeshGeometryModel3D _selectedGeometry; // selected material
|
||||
public MeshGeometryModel3D SelectedGeometry
|
||||
{
|
||||
get => _selectedGeometry;
|
||||
set => SetProperty(ref _selectedGeometry, value);
|
||||
}
|
||||
|
||||
private ObservableElement3DCollection _group3d; // material list
|
||||
public ObservableElement3DCollection Group3d
|
||||
{
|
||||
get => _group3d;
|
||||
set => SetProperty(ref _group3d, value);
|
||||
}
|
||||
|
||||
public ModelAndCam(UObject export)
|
||||
{
|
||||
Export = export;
|
||||
TriangleCount = 0;
|
||||
Group3d = new ObservableElement3DCollection();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TriangleCount = 0;
|
||||
SelectedGeometry = null;
|
||||
foreach (var g in Group3d.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
|
|
@ -422,13 +465,4 @@ namespace FModel.ViewModels
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CamAxisHolder
|
||||
{
|
||||
public Point3D Position;
|
||||
public Vector3D LookDirection;
|
||||
public Geometry3D XAxis;
|
||||
public Geometry3D YAxis;
|
||||
public Geometry3D ZAxis;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,21 +23,89 @@
|
|||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="250" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="350" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="4*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<GroupBox Grid.Column="0" Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
<DockPanel Margin="10">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Models" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding ModelViewer.LoadedModelsView, IsAsync=True}"
|
||||
SelectedItem="{Binding ModelViewer.SelectedModel, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Export.Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Triangles" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding ModelViewer.SelectedModel.TriangleCount, FallbackValue=0, StringFormat={}{0:### ### ### ###}}" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Materials" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="2" Text="{Binding ModelViewer.SelectedModel.Group3d.Count, FallbackValue=0, StringFormat={}{0:### ###}}" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
|
||||
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<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="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}" />
|
||||
</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.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource CopyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Change Material">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SwapIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GridSplitter Grid.Column="1" ResizeDirection="Columns" Width="4" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}" />
|
||||
|
|
@ -53,13 +121,14 @@
|
|||
</helix:Viewport3DX.InputBindings>
|
||||
|
||||
<helix:EnvironmentMap3D Texture="{Binding ModelViewer.HDRi}" />
|
||||
<helix:DirectionalLight3D Direction="{Binding ModelViewer.Cam.LookDirection}" Color="White" />
|
||||
<helix:DirectionalLight3D Color="White" Direction="{Binding Camera.LookDirection,
|
||||
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type helix:Viewport3DX}}}" />
|
||||
|
||||
<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:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.XAxis}" Color="#FC3854" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.YAxis}" Color="#85CB22" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.ZAxis}" Color="#388EED" />
|
||||
|
||||
<helix:GroupModel3D x:Name="MyAntiCrashGroup" ItemsSource="{Binding ModelViewer.Group3d}" Mouse3DDown="OnMouse3DDown" />
|
||||
<helix:GroupModel3D x:Name="MyAntiCrashGroup" ItemsSource="{Binding ModelViewer.SelectedModel.Group3d}" />
|
||||
</helix:Viewport3DX>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
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
|
||||
{
|
||||
|
|
@ -21,7 +19,8 @@ namespace FModel.Views
|
|||
public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export);
|
||||
private void OnClosing(object sender, CancelEventArgs e)
|
||||
{
|
||||
_applicationView.ModelViewer.AppendModeEnabled = false;
|
||||
_applicationView.ModelViewer.Clear();
|
||||
_applicationView.ModelViewer.AppendMode = false;
|
||||
MyAntiCrashGroup.ItemsSource = null; // <3
|
||||
}
|
||||
|
||||
|
|
@ -40,20 +39,5 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
<Geometry x:Key="NotVisibleIcon">M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM2.71 3.16c-.39.39-.39 1.02 0 1.41l1.97 1.97C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l2.72 2.72c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.13 3.16c-.39-.39-1.03-.39-1.42 0zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33c-.15-1.4-1.25-2.49-2.64-2.64l2.64 2.64z</Geometry>
|
||||
<Geometry x:Key="WireframeIcon">M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM8 17h8c.55 0 1-.45 1-1V8c0-.55-.45-1-1-1H8c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1zm1-8h6v6H9V9z</Geometry>
|
||||
<Geometry x:Key="NotWireframeIcon">M3,13h2v-2H3V13z M7,21h2v-2H7V21z M13,3h-2v2h2V3z M19,3v2h2C21,3.9,20.1,3,19,3z M5,21v-2H3C3,20.1,3.9,21,5,21z M3,17h2 v-2H3V17z M11,21h2v-2h-2V21z M19,13h2v-2h-2V13z M19,9h2V7h-2V9z M15,5h2V3h-2V5z M7.83,5L7,4.17V3h2v2H7.83z M19.83,17L19,16.17 V15h2v2H19.83z M9,15v-3.17L12.17,15H9z M2.1,3.51c-0.39,0.39-0.39,1.02,0,1.41L4.17,7H3v2h2V7.83l2,2V16c0,0.55,0.45,1,1,1h6.17 l2,2H15v2h2v-1.17l2.07,2.07c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L3.51,3.51C3.12,3.12,2.49,3.12,2.1,3.51z M17,8c0-0.55-0.45-1-1-1H9.83l2,2H15v3.17l2,2V8z</Geometry>
|
||||
<Geometry x:Key="SwapIcon">M16 17.01V11c0-.55-.45-1-1-1s-1 .45-1 1v6.01h-1.79c-.45 0-.67.54-.35.85l2.79 2.78c.2.19.51.19.71 0l2.79-2.78c.32-.31.09-.85-.35-.85H16zM8.65 3.35L5.86 6.14c-.32.31-.1.85.35.85H8V13c0 .55.45 1 1 1s1-.45 1-1V6.99h1.79c.45 0 .67-.54.35-.85L9.35 3.35c-.19-.19-.51-.19-.7 0z</Geometry>
|
||||
|
||||
<Style x:Key="TabItemFillSpace" TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="Width">
|
||||
|
|
@ -618,13 +619,11 @@
|
|||
</Style>
|
||||
|
||||
<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="ItemsSource" Value="{Binding ModelViewer.SelectedModel.Group3d, IsAsync=True}" />
|
||||
<Setter Property="SelectedItem" Value="{Binding ModelViewer.SelectedModel.SelectedGeometry}" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="NeverExpand"/>
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/>
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.HorizontalScrollBarExpansionMode" Value="NeverExpand"/>
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.HorizontalScrollBarPlacement" Value="Docked"/>
|
||||
<Setter Property="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
|
|
@ -684,7 +683,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ModelViewer.Group3d.Count, FallbackValue=0}" Value="0">
|
||||
<DataTrigger Binding="{Binding ModelViewer.SelectedModel.Group3d.Count, FallbackValue=0}" Value="0">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user