mirror of
https://github.com/4sval/FModel.git
synced 2026-03-22 01:34:37 -05:00
object outlining v0.annoying
This commit is contained in:
parent
a7885b1dbc
commit
a45ad49414
|
|
@ -1 +1 @@
|
|||
Subproject commit cebbff9035b292e81493f8b94c19d39394be2618
|
||||
Subproject commit 35f6d437eb0f03855d30265a6214794810868e6a
|
||||
|
|
@ -101,8 +101,8 @@
|
|||
<None Remove="Resources\skybox.vert" />
|
||||
<None Remove="Resources\framebuffer.frag" />
|
||||
<None Remove="Resources\framebuffer.vert" />
|
||||
<None Remove="Resources\imgui.frag" />
|
||||
<None Remove="Resources\imgui.vert" />
|
||||
<None Remove="Resources\outline.frag" />
|
||||
<None Remove="Resources\outline.vert" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -120,8 +120,8 @@
|
|||
<EmbeddedResource Include="Resources\skybox.vert" />
|
||||
<EmbeddedResource Include="Resources\framebuffer.frag" />
|
||||
<EmbeddedResource Include="Resources\framebuffer.vert" />
|
||||
<EmbeddedResource Include="Resources\imgui.frag" />
|
||||
<EmbeddedResource Include="Resources\imgui.vert" />
|
||||
<EmbeddedResource Include="Resources\outline.frag" />
|
||||
<EmbeddedResource Include="Resources\outline.vert" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -132,7 +132,6 @@
|
|||
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.70-temp" />
|
||||
<PackageReference Include="HelixToolkit.SharpDX.Core.Wpf" Version="2.21.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.16" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.4" />
|
||||
|
|
@ -238,4 +237,10 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="deps\glfw3.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#version 330
|
||||
|
||||
layout (location = 0) out vec4 Out_Color;
|
||||
|
||||
in vec2 Frag_UV;
|
||||
in vec4 Frag_Color;
|
||||
|
||||
uniform sampler2D Texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#version 330
|
||||
|
||||
layout (location = 0) in vec2 Position;
|
||||
layout (location = 1) in vec2 UV;
|
||||
layout (location = 2) in vec4 Color;
|
||||
|
||||
uniform mat4 ProjMtx;
|
||||
|
||||
out vec2 Frag_UV;
|
||||
out vec4 Frag_Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
Frag_UV = UV;
|
||||
Frag_Color = Color;
|
||||
gl_Position = ProjMtx * vec4(Position.xy,0,1);
|
||||
}
|
||||
8
FModel/Resources/outline.frag
Normal file
8
FModel/Resources/outline.frag
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#version 330
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = vec4(0.929, 0.588, 0.196, 0.2);
|
||||
}
|
||||
15
FModel/Resources/outline.vert
Normal file
15
FModel/Resources/outline.vert
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vPos;
|
||||
layout (location = 1) in vec3 vNormal;
|
||||
layout (location = 6) in mat4 vInstanceMatrix;
|
||||
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
|
||||
void main()
|
||||
{
|
||||
float scaleFactor = 0.005;
|
||||
vec3 scaleVertex = vPos + vNormal * scaleFactor;
|
||||
gl_Position = uProjection * uView * vInstanceMatrix * vec4(scaleVertex, 1.0);
|
||||
}
|
||||
|
|
@ -72,7 +72,6 @@ public class ApplicationViewModel : ViewModel
|
|||
public AesManagerViewModel AesManager { get; }
|
||||
public AudioPlayerViewModel AudioPlayer { get; }
|
||||
public MapViewerViewModel MapViewer { get; }
|
||||
public ModelViewerViewModel ModelViewer { get; }
|
||||
private OodleCompressor _oodle;
|
||||
|
||||
public ApplicationViewModel()
|
||||
|
|
@ -94,7 +93,6 @@ public class ApplicationViewModel : ViewModel
|
|||
AesManager = new AesManagerViewModel(CUE4Parse);
|
||||
MapViewer = new MapViewerViewModel(CUE4Parse);
|
||||
AudioPlayer = new AudioPlayerViewModel();
|
||||
ModelViewer = new ModelViewerViewModel(CUE4Parse.Game);
|
||||
Status = EStatusKind.Ready;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -763,12 +763,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
case UMaterialInstance m when ModelIsOverwritingMaterial:
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
|
||||
modelViewer.Overwrite(m);
|
||||
});
|
||||
return true;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
case UStaticMesh when UserSettings.Default.SaveStaticMeshes:
|
||||
case USkeletalMesh when UserSettings.Default.SaveSkeletalMeshes:
|
||||
|
|
|
|||
|
|
@ -1,767 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media.Media3D;
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.Utils;
|
||||
using CUE4Parse_Conversion.Materials;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Meshes.glTF;
|
||||
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 SharpGLTF.Geometry;
|
||||
using SharpGLTF.Geometry.VertexTypes;
|
||||
using SharpGLTF.Scenes;
|
||||
using SharpGLTF.Schema2;
|
||||
using SharpGLTF.Transforms;
|
||||
using SkiaSharp;
|
||||
using Camera = HelixToolkit.Wpf.SharpDX.Camera;
|
||||
using Exporter = CUE4Parse_Conversion.Exporter;
|
||||
using Geometry3D = HelixToolkit.SharpDX.Core.Geometry3D;
|
||||
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
|
||||
using Vector2 = SharpDX.Vector2;
|
||||
using Vector3 = SharpDX.Vector3;
|
||||
using VERTEX = SharpGLTF.Geometry.VertexTypes.VertexPositionNormalTangent;
|
||||
|
||||
namespace FModel.ViewModels;
|
||||
|
||||
public class ModelViewerViewModel : ViewModel
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
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 ModelAndCam _selectedModel; // selected mesh
|
||||
public ModelAndCam SelectedModel
|
||||
{
|
||||
get => _selectedModel;
|
||||
set => SetProperty(ref _selectedModel, value);
|
||||
}
|
||||
|
||||
private readonly ObservableCollection<ModelAndCam> _loadedModels; // mesh list
|
||||
public ICollectionView LoadedModelsView { get; }
|
||||
|
||||
private bool _appendMode;
|
||||
public bool AppendMode
|
||||
{
|
||||
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 };
|
||||
|
||||
public ModelViewerViewModel(FGame game)
|
||||
{
|
||||
_game = game;
|
||||
_loadedModels = new ObservableCollection<ModelAndCam>();
|
||||
|
||||
EffectManager = new DefaultEffectsManager();
|
||||
LoadedModelsView = new ListCollectionView(_loadedModels);
|
||||
Cam = new PerspectiveCamera { NearPlaneDistance = 0.1, FarPlaneDistance = double.PositiveInfinity, FieldOfView = 90 };
|
||||
LoadHDRi();
|
||||
}
|
||||
|
||||
private void LoadHDRi()
|
||||
{
|
||||
var cubeMap = Application.GetResourceStream(new Uri("/FModel;component/Resources/approaching_storm_cubemap.dds", UriKind.Relative));
|
||||
HDRi = TextureModel.Create(cubeMap?.Stream);
|
||||
}
|
||||
|
||||
public void LoadExport(UObject export)
|
||||
{
|
||||
#if DEBUG
|
||||
LoadHDRi();
|
||||
#endif
|
||||
|
||||
ModelAndCam p;
|
||||
if (AppendMode && CanAppend)
|
||||
{
|
||||
p = SelectedModel;
|
||||
_loadedModels.Add(new ModelAndCam(export) { IsVisible = false });
|
||||
}
|
||||
else
|
||||
{
|
||||
p = new ModelAndCam(export);
|
||||
_loadedModels.Add(p);
|
||||
}
|
||||
|
||||
switch (export)
|
||||
{
|
||||
case UStaticMesh st:
|
||||
LoadStaticMesh(st, p);
|
||||
break;
|
||||
case USkeletalMesh sk:
|
||||
LoadSkeletalMesh(sk, p);
|
||||
break;
|
||||
case UMaterialInstance mi:
|
||||
LoadMaterialInstance(mi, p);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(export));
|
||||
}
|
||||
|
||||
if (AppendMode && CanAppend) return;
|
||||
SelectedModel = p;
|
||||
Cam.UpDirection = new Vector3D(0, 1, 0);
|
||||
Cam.Position = p.Position;
|
||||
Cam.LookDirection = p.LookDirection;
|
||||
}
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public void RenderingToggle()
|
||||
{
|
||||
if (SelectedModel == null) return;
|
||||
SelectedModel.RenderingToggle = !SelectedModel.RenderingToggle;
|
||||
}
|
||||
|
||||
public void WirefreameToggle()
|
||||
{
|
||||
if (SelectedModel == null) return;
|
||||
SelectedModel.WireframeToggle = !SelectedModel.WireframeToggle;
|
||||
}
|
||||
|
||||
public void MaterialColorToggle()
|
||||
{
|
||||
if (SelectedModel == null) return;
|
||||
SelectedModel.ShowMaterialColor = !SelectedModel.ShowMaterialColor;
|
||||
}
|
||||
|
||||
public void DiffuseOnlyToggle()
|
||||
{
|
||||
if (SelectedModel == null) return;
|
||||
SelectedModel.DiffuseOnlyToggle = !SelectedModel.DiffuseOnlyToggle;
|
||||
}
|
||||
|
||||
public void FocusOnSelectedMesh()
|
||||
{
|
||||
Cam.AnimateTo(SelectedModel.Position, SelectedModel.LookDirection, new Vector3D(0, 1, 0), 500);
|
||||
}
|
||||
|
||||
public async Task SaveLoadedModels()
|
||||
{
|
||||
if (_loadedModels.Count < 1) return;
|
||||
|
||||
var folderBrowser = new VistaFolderBrowserDialog { ShowNewFolderButton = true };
|
||||
if (folderBrowser.ShowDialog() == false) return;
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
var exportOptions = new ExporterOptions
|
||||
{
|
||||
TextureFormat = UserSettings.Default.TextureExportFormat,
|
||||
LodFormat = UserSettings.Default.LodExportFormat,
|
||||
MeshFormat = UserSettings.Default.MeshExportFormat,
|
||||
Platform = UserSettings.Default.OverridedPlatform,
|
||||
ExportMorphTargets = UserSettings.Default.SaveMorphTargets
|
||||
};
|
||||
foreach (var model in _loadedModels)
|
||||
{
|
||||
var toSave = new Exporter(model.Export, exportOptions);
|
||||
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 SaveAsScene()
|
||||
{
|
||||
if (_loadedModels.Count < 1) return;
|
||||
|
||||
var fileBrowser = new VistaSaveFileDialog
|
||||
{
|
||||
Title = "Save Loaded Models As...",
|
||||
DefaultExt = ".glb",
|
||||
Filter = "glTF Binary File (*.glb)|*.glb|glTF ASCII File (*.gltf)|*.gltf|All Files(*.*)|*.*",
|
||||
AddExtension = true,
|
||||
OverwritePrompt = true,
|
||||
CheckPathExists = true
|
||||
};
|
||||
|
||||
if (fileBrowser.ShowDialog() == false || string.IsNullOrEmpty(fileBrowser.FileName)) return;
|
||||
|
||||
var sceneBuilder = new SceneBuilder();
|
||||
var materialExports = new List<MaterialExporter>();
|
||||
foreach (var model in _loadedModels)
|
||||
{
|
||||
switch (model.Export)
|
||||
{
|
||||
case UStaticMesh sm:
|
||||
{
|
||||
var mesh = new MeshBuilder<VERTEX, VertexColorXTextureX, VertexEmpty>(sm.Name);
|
||||
if (sm.TryConvert(out var convertedMesh) && convertedMesh.LODs.Count > 0)
|
||||
{
|
||||
var lod = convertedMesh.LODs.First();
|
||||
for (var i = 0; i < lod.Sections.Value.Length; i++)
|
||||
{
|
||||
Gltf.ExportStaticMeshSections(i, lod, lod.Sections.Value[i], materialExports, mesh);
|
||||
}
|
||||
|
||||
sceneBuilder.AddRigidMesh(mesh, AffineTransform.Identity);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case USkeletalMesh sk:
|
||||
{
|
||||
var mesh = new MeshBuilder<VERTEX, VertexColorXTextureX, VertexJoints4>(sk.Name);
|
||||
|
||||
if (sk.TryConvert(out var convertedMesh) && convertedMesh.LODs.Count > 0)
|
||||
{
|
||||
var lod = convertedMesh.LODs.First();
|
||||
for (var i = 0; i < lod.Sections.Value.Length; i++)
|
||||
{
|
||||
Gltf.ExportSkelMeshSections(i, lod, lod.Sections.Value[i], materialExports, mesh);
|
||||
}
|
||||
|
||||
var armatureNodeBuilder = new NodeBuilder(sk.Name + ".ao");
|
||||
var armature = Gltf.CreateGltfSkeleton(convertedMesh.RefSkeleton, armatureNodeBuilder);
|
||||
sceneBuilder.AddSkinnedMesh(mesh, Matrix4x4.Identity, armature);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var scene = sceneBuilder.ToGltf2();
|
||||
var fileName = fileBrowser.FileName;
|
||||
if (fileName.EndsWith(".glb", StringComparison.OrdinalIgnoreCase))
|
||||
scene.SaveGLB(fileName);
|
||||
else if (fileName.EndsWith(".gltf", StringComparison.OrdinalIgnoreCase))
|
||||
scene.SaveGLTF(fileName);
|
||||
else if (fileName.EndsWith(".obj", StringComparison.OrdinalIgnoreCase))
|
||||
scene.SaveAsWavefront(fileName);
|
||||
else
|
||||
throw new ArgumentOutOfRangeException(nameof(fileName), $@"Unknown file format {fileName.SubstringAfterWithLast('.')}");
|
||||
|
||||
if (!CheckIfSaved(fileName)) return;
|
||||
foreach (var materialExport in materialExports)
|
||||
{
|
||||
materialExport.TryWriteToDir(new DirectoryInfo(StringUtils.SubstringBeforeWithLast(fileName, '\\')), out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopySelectedMaterialName()
|
||||
{
|
||||
if (SelectedModel is not { } m || m.SelectedGeometry is null)
|
||||
return;
|
||||
|
||||
Clipboard.SetText(m.SelectedGeometry.DisplayName.TrimEnd());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool TryOverwriteMaterial(UMaterialInstance materialInstance)
|
||||
{
|
||||
if (SelectedModel?.SelectedGeometry == null || _loadedModels.Count < 1) return false;
|
||||
|
||||
var (m, _, _) = LoadMaterial(materialInstance);
|
||||
|
||||
var obj = new ResolvedLoadedObject(materialInstance);
|
||||
switch (_loadedModels[SelectedModel.SelectedGeometry.ExportIndex].Export)
|
||||
{
|
||||
case UStaticMesh { Materials: { } } st:
|
||||
st.Materials[SelectedModel.SelectedGeometry.MaterialIndex] = obj;
|
||||
break;
|
||||
case USkeletalMesh sk:
|
||||
sk.Materials[SelectedModel.SelectedGeometry.MaterialIndex].Material = obj;
|
||||
break;
|
||||
case UMaterialInstance:
|
||||
SelectedModel.SwapExport(materialInstance);
|
||||
break;
|
||||
}
|
||||
|
||||
SelectedModel.SelectedGeometry.Material = m;
|
||||
return m != null;
|
||||
}
|
||||
|
||||
private void LoadMaterialInstance(UMaterialInstance materialInstance, ModelAndCam cam)
|
||||
{
|
||||
var builder = new MeshBuilder();
|
||||
builder.AddBox(Vector3.Zero, 10, 10, 10);
|
||||
cam.TriangleCount = 12; // no need to count
|
||||
|
||||
SetupCameraAndAxis(new FBox(new FVector(-8), new FVector(8)), cam);
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(materialInstance);
|
||||
|
||||
cam.Group3d.Add(new CustomMeshGeometryModel3D
|
||||
{
|
||||
Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), -45)),
|
||||
DisplayName = materialInstance.Name, Geometry = builder.ToMeshGeometry3D(), MaterialIndex = 0,
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering, ExportIndex = _loadedModels.Count - 1
|
||||
});
|
||||
}
|
||||
|
||||
private void LoadStaticMesh(UStaticMesh mesh, ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
|
||||
foreach (var lod in convertedMesh.LODs.Where(lod => !lod.SkipLod))
|
||||
{
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSkeletalMesh(USkeletalMesh mesh, ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
|
||||
foreach (var lod in convertedMesh.LODs.Where(lod => !lod.SkipLod))
|
||||
{
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, ModelAndCam cam)
|
||||
{
|
||||
for (var i = 0; i < sections.Length; i++) // each section is a mesh part with its own material
|
||||
{
|
||||
var section = sections[i];
|
||||
var builder = new MeshBuilder();
|
||||
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
|
||||
{
|
||||
var id = section.FirstIndex + j * 3 + t;
|
||||
var vert = verts[indices[id]];
|
||||
var p = new Vector3(vert.Position.X, vert.Position.Z, vert.Position.Y); // up direction is Y
|
||||
var n = new Vector3(vert.Normal.X, vert.Normal.Z, vert.Normal.Y);
|
||||
n.Normalize();
|
||||
|
||||
builder.AddNode(p, n, new Vector2(vert.UV.U, vert.UV.V));
|
||||
builder.TriangleIndices.Add(j * 3 + t); // one mesh part is "j * 3 + t" use "id" if you're building the full mesh
|
||||
}
|
||||
}
|
||||
|
||||
if (section.Material == null || !section.Material.TryLoad(out var o) || o is not UMaterialInterface material)
|
||||
{
|
||||
cam.Group3d.Add(new CustomMeshGeometryModel3D
|
||||
{
|
||||
DisplayName = section.Material?.Name.ToString() ?? $"material_{section.MaterialIndex}", MaterialIndex = section.MaterialIndex,
|
||||
Geometry = builder.ToMeshGeometry3D(), Material = new PBRMaterial(), IsTransparent = false,
|
||||
IsRendering = true, ExportIndex = _loadedModels.Count - 1
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(material);
|
||||
cam.Group3d.Add(new CustomMeshGeometryModel3D
|
||||
{
|
||||
DisplayName = material.Name, MaterialIndex = section.MaterialIndex,
|
||||
Geometry = builder.ToMeshGeometry3D(), Material = m, IsTransparent = isTransparent,
|
||||
IsRendering = isRendering, ExportIndex = _loadedModels.Count - 1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (PBRMaterial material, bool isRendering, bool isTransparent) LoadMaterial(UMaterialInterface unrealMaterial)
|
||||
{
|
||||
var m = new PBRMaterial { RenderShadowMap = true, EnableAutoTangent = true, RenderEnvironmentMap = true };
|
||||
var parameters = new CMaterialParams();
|
||||
unrealMaterial.GetParams(parameters);
|
||||
|
||||
var isRendering = !parameters.IsNull;
|
||||
if (isRendering)
|
||||
{
|
||||
if (!parameters.HasTopDiffuseTexture && parameters.DiffuseColor is { A: > 0 } diffuseColor)
|
||||
{
|
||||
m.AlbedoColor = new Color4(diffuseColor.R, diffuseColor.G, diffuseColor.B, diffuseColor.A);
|
||||
}
|
||||
else if (parameters.Diffuse is UTexture2D diffuse)
|
||||
{
|
||||
m.AlbedoMap = new TextureModel(diffuse.Decode(UserSettings.Default.OverridedPlatform)?.Encode(SKEncodedImageFormat.Png, 100).AsStream());
|
||||
}
|
||||
|
||||
if (parameters.Normal is UTexture2D normal)
|
||||
{
|
||||
m.NormalMap = new TextureModel(normal.Decode(UserSettings.Default.OverridedPlatform)?.Encode(SKEncodedImageFormat.Png, 100).AsStream());
|
||||
}
|
||||
|
||||
if (parameters.Specular is UTexture2D specular)
|
||||
{
|
||||
var mip = specular.GetFirstMip();
|
||||
var platform = UserSettings.Default.OverridedPlatform;
|
||||
TextureDecoder.DecodeTexture(mip, specular.Format, specular.isNormalMap, platform, out var data, out var colorType);
|
||||
|
||||
switch (_game)
|
||||
{
|
||||
case FGame.FortniteGame:
|
||||
{
|
||||
// Fortnite's Specular Texture Channels
|
||||
// R Specular
|
||||
// G Metallic
|
||||
// B Roughness
|
||||
unsafe
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
{
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
d[offset] = 0;
|
||||
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // swap G and B
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameters.RoughnessValue = 1;
|
||||
parameters.MetallicValue = 1;
|
||||
break;
|
||||
}
|
||||
case FGame.ShooterGame:
|
||||
{
|
||||
var packedPBRType = specular.Name[(specular.Name.LastIndexOf('_') + 1)..];
|
||||
switch (packedPBRType)
|
||||
{
|
||||
case "MRAE": // R: Metallic, G: AO (0-127) & Emissive (128-255), B: Roughness (Character PBR)
|
||||
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 R and G
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "MRAS": // R: Metallic, B: Roughness, B: AO, A: Specular (Legacy PBR)
|
||||
case "MRA": // R: Metallic, B: Roughness, B: AO (Environment PBR)
|
||||
case "MRS": // R: Metallic, G: Roughness, B: Specular (Weapon PBR)
|
||||
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
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
parameters.RoughnessValue = 1;
|
||||
parameters.MetallicValue = 1;
|
||||
break;
|
||||
}
|
||||
case FGame.Gameface:
|
||||
{
|
||||
// GTA's Specular Texture Channels
|
||||
// R Metallic
|
||||
// G Roughness
|
||||
// B Specular
|
||||
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
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(mip.SizeX, mip.SizeY, colorType, SKAlphaType.Unpremul));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = data)
|
||||
{
|
||||
bitmap.SetPixels(new IntPtr(p));
|
||||
}
|
||||
}
|
||||
|
||||
// R -> AO G -> Roughness B -> Metallic
|
||||
m.RoughnessMetallicMap = new TextureModel(bitmap.Encode(SKEncodedImageFormat.Png, 100).AsStream());
|
||||
m.RoughnessFactor = parameters.RoughnessValue;
|
||||
m.MetallicFactor = parameters.MetallicValue;
|
||||
m.RenderAmbientOcclusionMap = parameters.SpecularValue > 0;
|
||||
}
|
||||
|
||||
if (parameters.HasTopEmissiveTexture && parameters.Emissive is UTexture2D emissive && parameters.EmissiveColor is { A: > 0 } emissiveColor)
|
||||
{
|
||||
m.EmissiveColor = new Color4(emissiveColor.R, emissiveColor.G, emissiveColor.B, emissiveColor.A);
|
||||
m.EmissiveMap = new TextureModel(emissive.Decode(UserSettings.Default.OverridedPlatform)?.Encode(SKEncodedImageFormat.Png, 100).AsStream());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m.AlbedoColor = new Color4(1, 0, 0, 1);
|
||||
}
|
||||
|
||||
return (m, isRendering, parameters.IsTransparent);
|
||||
}
|
||||
|
||||
private void SetupCameraAndAxis(FBox box, ModelAndCam cam)
|
||||
{
|
||||
if (AppendMode && CanAppend) return;
|
||||
var center = box.GetCenter();
|
||||
|
||||
var lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(box.Min.X, center.Z, center.Y), new Vector3(box.Max.X, center.Z, center.Y));
|
||||
cam.XAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(center.X, box.Min.Z, center.Y), new Vector3(center.X, box.Max.Z, center.Y));
|
||||
cam.YAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(center.X, center.Z, box.Min.Y), new Vector3(center.X, center.Z, box.Max.Y));
|
||||
cam.ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
|
||||
cam.Position = new Point3D(box.Max.X + center.X * 2, center.Z, box.Min.Y + center.Y * 2);
|
||||
cam.LookDirection = new Vector3D(-cam.Position.X + center.X, 0, -cam.Position.Z + center.Y);
|
||||
}
|
||||
|
||||
private bool CheckIfSaved(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", path);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {path}", Constants.WHITE, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.Error("{FileName} could not be saved", path);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{path}'", Constants.WHITE, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var g in _loadedModels.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
_loadedModels.Remove(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelAndCam : ViewModel
|
||||
{
|
||||
public UObject Export { get; private set; }
|
||||
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 bool _isVisible = true;
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
set => SetProperty(ref _isVisible, value);
|
||||
}
|
||||
|
||||
private bool _renderingToggle;
|
||||
public bool RenderingToggle
|
||||
{
|
||||
get => _renderingToggle;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _renderingToggle, value);
|
||||
foreach (var g in Group3d)
|
||||
{
|
||||
if (g is not CustomMeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
||||
geometryModel.IsRendering = !geometryModel.IsRendering;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _wireframeToggle;
|
||||
public bool WireframeToggle
|
||||
{
|
||||
get => _wireframeToggle;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _wireframeToggle, value);
|
||||
foreach (var g in Group3d)
|
||||
{
|
||||
if (g is not CustomMeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
||||
geometryModel.RenderWireframe = !geometryModel.RenderWireframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _showMaterialColor;
|
||||
public bool ShowMaterialColor
|
||||
{
|
||||
get => _showMaterialColor;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _showMaterialColor, value);
|
||||
for (var i = 0; i < Group3d.Count; i++)
|
||||
{
|
||||
if (Group3d[i] is not CustomMeshGeometryModel3D { Material: PBRMaterial material } m)
|
||||
continue;
|
||||
|
||||
var index = B(i);
|
||||
material.RenderAlbedoMap = !_showMaterialColor;
|
||||
|
||||
if (_showMaterialColor)
|
||||
{
|
||||
m.Tag = material.AlbedoColor;
|
||||
material.AlbedoColor = new Color4(_table[C(index)] / 255, _table[C(index >> 1)] / 255, _table[C(index >> 2)] / 255, 1);
|
||||
}
|
||||
else material.AlbedoColor = (Color4) m.Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _diffuseOnlyToggle;
|
||||
public bool DiffuseOnlyToggle
|
||||
{
|
||||
get => _diffuseOnlyToggle;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _diffuseOnlyToggle, value);
|
||||
foreach (var g in Group3d)
|
||||
{
|
||||
if (g is not CustomMeshGeometryModel3D { Material: PBRMaterial material })
|
||||
continue;
|
||||
|
||||
material.RenderAmbientOcclusionMap = !material.RenderAmbientOcclusionMap;
|
||||
material.RenderDisplacementMap = !material.RenderDisplacementMap;
|
||||
// material.RenderEmissiveMap = !material.RenderEmissiveMap;
|
||||
// material.RenderEnvironmentMap = !material.RenderEnvironmentMap;
|
||||
material.RenderIrradianceMap = !material.RenderIrradianceMap;
|
||||
material.RenderRoughnessMetallicMap = !material.RenderRoughnessMetallicMap;
|
||||
material.RenderShadowMap = !material.RenderShadowMap;
|
||||
material.RenderNormalMap = !material.RenderNormalMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CustomMeshGeometryModel3D _selectedGeometry; // selected material
|
||||
public CustomMeshGeometryModel3D SelectedGeometry
|
||||
{
|
||||
get => _selectedGeometry;
|
||||
set => SetProperty(ref _selectedGeometry, value);
|
||||
}
|
||||
|
||||
private ObservableElement3DCollection _group3d; // material list
|
||||
public ObservableElement3DCollection Group3d
|
||||
{
|
||||
get => _group3d;
|
||||
set => SetProperty(ref _group3d, value);
|
||||
}
|
||||
|
||||
private readonly float[] _table = { 255 * 0.9f, 25 * 3.0f, 255 * 0.6f, 255 * 0.0f };
|
||||
private readonly int[] _table2 = { 0, 1, 2, 4, 7, 3, 5, 6 };
|
||||
|
||||
public ModelAndCam(UObject export)
|
||||
{
|
||||
Export = export;
|
||||
TriangleCount = 0;
|
||||
Group3d = new ObservableElement3DCollection();
|
||||
}
|
||||
|
||||
private int B(int x) => (x & 0xFFF8) | _table2[x & 7] ^ 7;
|
||||
private int C(int x) => (x & 1) | ((x >> 2) & 2);
|
||||
|
||||
public void SwapExport(UObject e)
|
||||
{
|
||||
Export = e;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TriangleCount = 0;
|
||||
SelectedGeometry = null;
|
||||
foreach (var g in Group3d.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
Group3d.Remove(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomMeshGeometryModel3D : MeshGeometryModel3D
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public int MaterialIndex { get; set; }
|
||||
public int ExportIndex { get; set; }
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
<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:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:helix="http://helix-toolkit.org/wpf/SharpDX"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanResize" IconVisibility="Collapsed"
|
||||
PreviewKeyDown="OnWindowKeyDown" Closing="OnClosing"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.60'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.60'}">
|
||||
<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>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="350" MinWidth="250" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="4*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Models" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" Style="{StaticResource ModelsComboBox}" IsEnabled="{Binding IsReady}" />
|
||||
|
||||
<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" Click="OnFocusClick" IsEnabled="{Binding IsReady}" />
|
||||
<ToggleButton Grid.Row="0" Grid.Column="2" IsChecked="{Binding ModelViewer.AppendMode}" IsEnabled="{Binding IsReady}"
|
||||
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" Click="Save" IsEnabled="{Binding IsReady}"
|
||||
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 x:Name="MaterialsListName" DockPanel.Dock="Top" Style="{StaticResource MaterialsListBox}" IsEnabled="{Binding IsReady}">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Copy Name" Click="OnCopyClick">
|
||||
<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="Overwrite Material" Click="OnOverwriteMaterialClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource OverwriteIcon}" />
|
||||
</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}}" />
|
||||
|
||||
<TextBlock Grid.Column="2" Text="Model is loading, please wait..." HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<helix:Viewport3DX Grid.Column="2" EffectsManager="{Binding ModelViewer.EffectManager}" Camera="{Binding ModelViewer.Cam}"
|
||||
ShowViewCube="False" IsChangeFieldOfViewEnabled="False" IsMoveEnabled="False" UseDefaultGestures="False"
|
||||
ShowCameraTarget="False" FXAALevel="Ultra" MSAA="Maximum" BackgroundColor="#2A2B34"
|
||||
EnableSSAO="True" SSAOIntensity="1" EnableSwapChainRendering="True">
|
||||
<helix:Viewport3DX.InputBindings>
|
||||
<MouseBinding Command="helix:ViewportCommands.Rotate" Gesture="LeftClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Zoom" Gesture="RightClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Pan" Gesture="MiddleClick" />
|
||||
<KeyBinding Command="helix:ViewportCommands.ZoomExtents" Modifiers="Shift" Key="C"/>
|
||||
</helix:Viewport3DX.InputBindings>
|
||||
|
||||
<helix:EnvironmentMap3D Texture="{Binding ModelViewer.HDRi}" />
|
||||
<helix:DirectionalLight3D Color="White" Direction="{Binding Camera.LookDirection,
|
||||
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type helix:Viewport3DX}}}" />
|
||||
|
||||
<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" Mouse3DDown="OnMouse3DDown"
|
||||
ItemsSource="{Binding ModelViewer.SelectedModel.Group3d, IsAsync=True}"/>
|
||||
</helix:Viewport3DX>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
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.ViewModels;
|
||||
using HelixToolkit.Wpf.SharpDX;
|
||||
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()
|
||||
{
|
||||
DataContext = _applicationView;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export);
|
||||
public void Overwrite(UMaterialInstance materialInstance)
|
||||
{
|
||||
if (_applicationView.ModelViewer.TryOverwriteMaterial(materialInstance))
|
||||
{
|
||||
_applicationView.CUE4Parse.ModelIsOverwritingMaterial = 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();
|
||||
_applicationView.ModelViewer.AppendMode = false;
|
||||
_applicationView.CUE4Parse.ModelIsOverwritingMaterial = false;
|
||||
MyAntiCrashGroup.ItemsSource = null; // <3
|
||||
}
|
||||
|
||||
private async void OnWindowKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.W:
|
||||
_applicationView.ModelViewer.WirefreameToggle();
|
||||
break;
|
||||
case Key.H:
|
||||
_applicationView.ModelViewer.RenderingToggle();
|
||||
break;
|
||||
case Key.D:
|
||||
_applicationView.ModelViewer.DiffuseOnlyToggle();
|
||||
break;
|
||||
case Key.M:
|
||||
_applicationView.ModelViewer.MaterialColorToggle();
|
||||
break;
|
||||
case Key.Decimal:
|
||||
_applicationView.ModelViewer.FocusOnSelectedMesh();
|
||||
break;
|
||||
case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift):
|
||||
_applicationView.ModelViewer.SaveAsScene();
|
||||
break;
|
||||
case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control):
|
||||
await _applicationView.ModelViewer.SaveLoadedModels();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouse3DDown(object sender, MouseDown3DEventArgs e)
|
||||
{
|
||||
if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || e.HitTestResult.ModelHit is not CustomMeshGeometryModel3D m) return;
|
||||
_applicationView.ModelViewer.SelectedModel.SelectedGeometry = m;
|
||||
MaterialsListName.ScrollIntoView(m);
|
||||
}
|
||||
|
||||
private void OnFocusClick(object sender, RoutedEventArgs e)
|
||||
=> _applicationView.ModelViewer.FocusOnSelectedMesh();
|
||||
|
||||
private void OnCopyClick(object sender, RoutedEventArgs e)
|
||||
=> _applicationView.ModelViewer.CopySelectedMaterialName();
|
||||
|
||||
private async void Save(object sender, RoutedEventArgs e)
|
||||
=> await _applicationView.ModelViewer.SaveLoadedModels();
|
||||
|
||||
private void OnOverwriteMaterialClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_applicationView.CUE4Parse.ModelIsOverwritingMaterial = 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 Overwrite Material?",
|
||||
Icon = MessageBoxImage.Information,
|
||||
IsSoundEnabled = false
|
||||
});
|
||||
_messageShown = true;
|
||||
}
|
||||
|
||||
MainWindow.YesWeCats.Activate();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using SharpDX.Direct3D11;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
public class BoolToFillModeConverter : IValueConverter
|
||||
{
|
||||
public static readonly BoolToFillModeConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
FillMode.Solid => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
true => FillMode.Solid,
|
||||
_ => FillMode.Wireframe
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
public class DateTimeToStringConverter : IValueConverter
|
||||
{
|
||||
public static readonly DateTimeToStringConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value is not DateTime dateTime ? value : $"{dateTime.ToLongDateString()}, {dateTime.ToShortTimeString()}";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using HelixToolkit.Wpf.SharpDX;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
public class TagToColorConverter : IValueConverter
|
||||
{
|
||||
public static readonly TagToColorConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not PBRMaterial material)
|
||||
return new SolidColorBrush(Colors.Red);
|
||||
|
||||
return new SolidColorBrush(Color.FromScRgb(
|
||||
material.AlbedoColor.Alpha, material.AlbedoColor.Red,
|
||||
material.AlbedoColor.Green, material.AlbedoColor.Blue));
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
xmlns:system="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
|
||||
xmlns:soundOut="clr-namespace:CSCore.SoundOut;assembly=CSCore"
|
||||
xmlns:sharpDx="clr-namespace:SharpDX.Direct3D11;assembly=SharpDX.Direct3D11"
|
||||
xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
|
|
@ -638,91 +637,6 @@
|
|||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MaterialsListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
|
||||
<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="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="25" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/FModel;component/Resources/materialicon.png" Width="16" Height="16" Margin="5 0" HorizontalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding DisplayName}" TextTrimming="CharacterEllipsis" />
|
||||
<ToggleButton Grid.Column="3" IsChecked="{Binding IsRendering}" Padding="3" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path x:Name="SvgIcon1" Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource VisibleIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
<ToggleButton Grid.Column="4" IsChecked="{Binding FillMode, Converter={x:Static converters:BoolToFillModeConverter.Instance}}" Padding="3" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path x:Name="SvgIcon2" Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource WireframeIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
<Rectangle Grid.Column="5" Width="19" Height="22" Fill="{Binding Material, Converter={x:Static converters:TagToColorConverter.Instance}}"
|
||||
Visibility="{Binding DataContext.ModelViewer.SelectedModel.ShowMaterialColor,
|
||||
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:Views.ModelViewer}},
|
||||
Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsRendering}" Value="True">
|
||||
<Setter TargetName="SvgIcon1" Property="Data" Value="{StaticResource VisibleIcon}" />
|
||||
<Setter TargetName="SvgIcon1" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsRendering}" Value="False">
|
||||
<Setter TargetName="SvgIcon1" Property="Data" Value="{StaticResource NotVisibleIcon}" />
|
||||
<Setter TargetName="SvgIcon1" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding FillMode}" Value="{x:Static sharpDx:FillMode.Solid}">
|
||||
<Setter TargetName="SvgIcon2" Property="Data" Value="{StaticResource WireframeIcon}" />
|
||||
<Setter TargetName="SvgIcon2" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding FillMode}" Value="{x:Static sharpDx:FillMode.Wireframe}">
|
||||
<Setter TargetName="SvgIcon2" Property="Data" Value="{StaticResource NotWireframeIcon}" />
|
||||
<Setter TargetName="SvgIcon2" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ModelViewer.SelectedModel.Group3d.Count, FallbackValue=0}" Value="0">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Grid>
|
||||
<TextBlock Text="No material found in the mesh" FontWeight="SemiBold" TextAlignment="Center"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GameFilesTabControl" TargetType="TabControl" BasedOn="{StaticResource {x:Type TabControl}}">
|
||||
<Setter Property="ItemsSource" Value="{Binding CUE4Parse.TabControl.TabsItems, IsAsync=True}" />
|
||||
<Setter Property="SelectedItem" Value="{Binding CUE4Parse.TabControl.SelectedTab}" />
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class Model : IDisposable
|
|||
public Section[] Sections;
|
||||
public readonly List<CSkelMeshBone> Skeleton;
|
||||
|
||||
public int InstanceIndex;
|
||||
public int TransformsCount;
|
||||
public readonly List<Transform> Transforms;
|
||||
public readonly string[] TransformsLabels = {
|
||||
|
|
@ -37,6 +38,8 @@ public class Model : IDisposable
|
|||
"X Rotation", "Y", "Z",
|
||||
"X Scale", "Y", "Z"
|
||||
};
|
||||
|
||||
public bool IsSelected;
|
||||
public bool DisplayVertexColors;
|
||||
public bool DisplayBones;
|
||||
|
||||
|
|
@ -44,6 +47,7 @@ public class Model : IDisposable
|
|||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
InstanceIndex = 0;
|
||||
Transforms = new List<Transform>();
|
||||
}
|
||||
|
||||
|
|
@ -161,17 +165,35 @@ public class Model : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
public void Bind(Shader shader)
|
||||
public void Bind(Shader shader, Shader outline)
|
||||
{
|
||||
shader.SetUniform("display_vertex_colors", DisplayVertexColors);
|
||||
_vao.Bind();
|
||||
|
||||
var instanceCount = (uint) TransformsCount;
|
||||
shader.SetUniform("display_vertex_colors", DisplayVertexColors);
|
||||
for (int section = 0; section < Sections.Length; section++)
|
||||
{
|
||||
_vao.Bind();
|
||||
Sections[section].Bind(shader, instanceCount);
|
||||
_vao.Unbind();
|
||||
Sections[section].Bind(shader, (uint) TransformsCount);
|
||||
}
|
||||
|
||||
if (IsSelected)
|
||||
{
|
||||
outline.Use();
|
||||
_gl.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
|
||||
_gl.StencilFunc(StencilFunction.Notequal, 1, 0xFF);
|
||||
_gl.Disable(EnableCap.DepthTest);
|
||||
_gl.StencilMask(0x00);
|
||||
for (int section = 0; section < Sections.Length; section++)
|
||||
{
|
||||
_gl.DrawArraysInstanced(PrimitiveType.Triangles, Sections[section].FirstFaceIndex, Sections[section].FacesCount, (uint) InstanceIndex + 1);
|
||||
}
|
||||
_gl.StencilMask(0xFF);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
_gl.StencilFunc(StencilFunction.Always, 0, 0xFF);
|
||||
_gl.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
|
||||
shader.Use();
|
||||
}
|
||||
|
||||
_vao.Unbind();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ public class SnimGui : IDisposable
|
|||
private readonly Vector2 _texturePosition;
|
||||
private bool _viewportFocus;
|
||||
private FGuid _selectedModel;
|
||||
private int _selectedInstance;
|
||||
private int _selectedSection;
|
||||
|
||||
private const ImGuiWindowFlags _noResize = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; // delete once we have a proper docking branch
|
||||
|
|
@ -56,7 +55,6 @@ public class SnimGui : IDisposable
|
|||
_textureSize = _viewportSize with { Y = viewport.WorkSize.Y - _viewportSize.Y - titleBarHeight };
|
||||
_texturePosition = new Vector2(0, _viewportPosition.Y + _viewportSize.Y);
|
||||
_selectedModel = new FGuid();
|
||||
_selectedInstance = 0;
|
||||
_selectedSection = 0;
|
||||
|
||||
Theme(style);
|
||||
|
|
@ -126,7 +124,7 @@ public class SnimGui : IDisposable
|
|||
{
|
||||
ImGui.SetNextWindowSize(_outlinerSize, _firstUse);
|
||||
ImGui.SetNextWindowPos(_outlinerPosition, _firstUse);
|
||||
ImGui.Begin("Scene", _noResize | ImGuiWindowFlags.NoCollapse);
|
||||
ImGui.Begin("Scene Hierarchy", _noResize | ImGuiWindowFlags.NoCollapse);
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Collection"))
|
||||
|
|
@ -135,14 +133,16 @@ public class SnimGui : IDisposable
|
|||
foreach (var (guid, model) in models)
|
||||
{
|
||||
ImGui.PushID(i);
|
||||
if (ImGui.Selectable(model.Name, _selectedModel == guid))
|
||||
model.IsSelected = _selectedModel == guid;
|
||||
if (ImGui.Selectable(model.Name, model.IsSelected))
|
||||
{
|
||||
_selectedModel = guid;
|
||||
_selectedInstance = 0;
|
||||
_selectedSection = 0;
|
||||
}
|
||||
if (ImGui.BeginPopupContextItem())
|
||||
{
|
||||
if (ImGui.Selectable("Deselect"))
|
||||
_selectedModel = Guid.Empty;
|
||||
if (ImGui.Selectable("Delete"))
|
||||
models.Remove(guid);
|
||||
if (ImGui.Selectable("Copy to Clipboard"))
|
||||
|
|
@ -183,10 +183,10 @@ public class SnimGui : IDisposable
|
|||
ImGui.Text($"Entity: {model.Name}");
|
||||
ImGui.Separator();
|
||||
if (ImGui.Button("Focus"))
|
||||
camera.Position = model.Transforms[_selectedInstance].Position;
|
||||
camera.Position = model.Transforms[model.InstanceIndex].Position;
|
||||
ImGui.SameLine();
|
||||
ImGui.BeginDisabled(model.TransformsCount < 2);
|
||||
ImGui.SliderInt("Instance", ref _selectedInstance, 0, model.TransformsCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.SliderInt("Instance", ref model.InstanceIndex, 0, model.TransformsCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.EndDisabled();
|
||||
ImGui.BeginDisabled(!model.HasVertexColors);
|
||||
ImGui.Checkbox("Vertex Colors", ref model.DisplayVertexColors);
|
||||
|
|
@ -202,46 +202,46 @@ public class SnimGui : IDisposable
|
|||
var index = 0;
|
||||
|
||||
ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Position.X, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Position.X, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Position.Y, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Position.Y, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Position.Z, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Position.Z, speed, 0f, 0f, "%.2f m");
|
||||
ImGui.PopID();
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Rotation.Pitch, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Rotation.Pitch, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Rotation.Roll, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Rotation.Roll, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Rotation.Yaw, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Rotation.Yaw, .5f, 0f, 0f, "%.1f°");
|
||||
ImGui.PopID();
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.X, speed, 0f, 0f, "%.3f");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Scale.X, speed, 0f, 0f, "%.3f");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.Y, speed, 0f, 0f, "%.3f");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Scale.Y, speed, 0f, 0f, "%.3f");
|
||||
ImGui.PopID();
|
||||
|
||||
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.Z, speed, 0f, 0f, "%.3f");
|
||||
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[model.InstanceIndex].Scale.Z, speed, 0f, 0f, "%.3f");
|
||||
ImGui.PopID();
|
||||
|
||||
model.UpdateMatrix(_selectedInstance);
|
||||
model.UpdateMatrix(model.InstanceIndex);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public class Snooper
|
|||
private readonly Grid _grid;
|
||||
|
||||
private Shader _shader;
|
||||
private Shader _outline;
|
||||
private Vector3 _diffuseLight;
|
||||
private Vector3 _specularLight;
|
||||
private readonly Dictionary<FGuid, Model> _models;
|
||||
|
|
@ -255,7 +256,11 @@ public class Snooper
|
|||
_mouse = input.Mice[0];
|
||||
|
||||
_gl = GL.GetApi(_window);
|
||||
_gl.Enable(EnableCap.Blend);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
_gl.Enable(EnableCap.Multisample);
|
||||
_gl.Enable(EnableCap.StencilTest);
|
||||
_gl.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||
|
||||
_imGui = new SnimGui(_gl, _window, input);
|
||||
|
||||
|
|
@ -264,6 +269,7 @@ public class Snooper
|
|||
_grid.Setup(_gl);
|
||||
|
||||
_shader = new Shader(_gl);
|
||||
_outline = new Shader(_gl, "outline");
|
||||
_diffuseLight = new Vector3(0.75f);
|
||||
_specularLight = new Vector3(0.5f);
|
||||
foreach (var model in _models.Values)
|
||||
|
|
@ -288,10 +294,16 @@ public class Snooper
|
|||
_skybox.Bind(_camera);
|
||||
_grid.Bind(_camera);
|
||||
|
||||
_shader.Use();
|
||||
var viewMatrix = _camera.GetViewMatrix();
|
||||
var projMatrix = _camera.GetProjectionMatrix();
|
||||
|
||||
_shader.SetUniform("uView", _camera.GetViewMatrix());
|
||||
_shader.SetUniform("uProjection", _camera.GetProjectionMatrix());
|
||||
_outline.Use();
|
||||
_outline.SetUniform("uView", viewMatrix);
|
||||
_outline.SetUniform("uProjection", projMatrix);
|
||||
|
||||
_shader.Use();
|
||||
_shader.SetUniform("uView", viewMatrix);
|
||||
_shader.SetUniform("uProjection", projMatrix);
|
||||
_shader.SetUniform("viewPos", _camera.Position);
|
||||
|
||||
_shader.SetUniform("material.diffuseMap", 0);
|
||||
|
|
@ -305,7 +317,7 @@ public class Snooper
|
|||
|
||||
foreach (var model in _models.Values)
|
||||
{
|
||||
model.Bind(_shader);
|
||||
model.Bind(_shader, _outline);
|
||||
}
|
||||
|
||||
_imGui.Construct(_size, _framebuffer, _camera, _mouse, _models);
|
||||
|
|
@ -319,11 +331,8 @@ public class Snooper
|
|||
|
||||
private void ClearWhatHasBeenDrawn()
|
||||
{
|
||||
_gl.Enable(EnableCap.Blend);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
_gl.ClearColor(1.0f, 0.102f, 0.129f, 1.0f);
|
||||
_gl.Clear((uint) ClearBufferMask.ColorBufferBit | (uint) ClearBufferMask.DepthBufferBit);
|
||||
_gl.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||
_gl.Clear((uint) ClearBufferMask.ColorBufferBit | (uint) ClearBufferMask.DepthBufferBit | (uint) ClearBufferMask.StencilBufferBit);
|
||||
_gl.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +373,7 @@ public class Snooper
|
|||
_grid.Dispose();
|
||||
_skybox.Dispose();
|
||||
_shader.Dispose();
|
||||
_outline.Dispose();
|
||||
foreach (var model in _models.Values)
|
||||
{
|
||||
model.Dispose();
|
||||
|
|
|
|||
BIN
FModel/deps/glfw3.dll
Normal file
BIN
FModel/deps/glfw3.dll
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user