instancing + uworld + fixed black normal maps

This commit is contained in:
4sval 2022-09-05 23:31:40 +02:00
parent c63c1d8434
commit a7885b1dbc
13 changed files with 323 additions and 120 deletions

@ -1 +1 @@
Subproject commit 19dfffa9e55d0951e7da75789e65c8c4e6e62a3e
Subproject commit cebbff9035b292e81493f8b94c19d39394be2618

View File

@ -6,8 +6,8 @@ layout (location = 2) in vec2 vTexCoords;
layout (location = 3) in vec4 vColor;
layout (location = 4) in ivec4 vBoneIds;
layout (location = 5) in vec4 vWeights;
layout (location = 6) in mat4 vInstanceMatrix;
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
@ -18,10 +18,10 @@ out vec4 fColor;
void main()
{
gl_Position = uProjection * uView * uModel * vec4(vPos, 1.0);
gl_Position = uProjection * uView * vInstanceMatrix * vec4(vPos, 1.0);
fPos = vec3(uModel * vec4(vPos, 1.0));
fNormal = mat3(transpose(inverse(uModel))) * vNormal;
fPos = vec3(vInstanceMatrix * vec4(vPos, 1.0));
fNormal = mat3(transpose(inverse(vInstanceMatrix))) * vNormal;
fTexCoords = vTexCoords;
fColor = vColor;
}

View File

@ -23,6 +23,7 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Assets.Exports.Wwise;
using CUE4Parse.UE4.IO;
using CUE4Parse.UE4.Localization;
using CUE4Parse.UE4.Objects.Engine;
using CUE4Parse.UE4.Oodle.Objects;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.Shaders;
@ -749,6 +750,7 @@ public class CUE4ParseViewModel : ViewModel
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
return false;
}
case UWorld:
case UStaticMesh when UserSettings.Default.PreviewStaticMeshes:
case USkeletalMesh when UserSettings.Default.PreviewSkeletalMeshes:
case UMaterialInstance when UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial &&

View File

@ -9,24 +9,38 @@ public class BufferObject<TDataType> : IDisposable where TDataType : unmanaged
private BufferTargetARB _bufferType;
private GL _gl;
public unsafe BufferObject(GL gl, Span<TDataType> data, BufferTargetARB bufferType)
public BufferObject(GL gl, BufferTargetARB bufferType)
{
_gl = gl;
_bufferType = bufferType;
_handle = _gl.GenBuffer();
Bind();
}
public unsafe BufferObject(GL gl, Span<TDataType> data, BufferTargetARB bufferType) : this(gl, bufferType)
{
fixed (void* d = data)
{
_gl.BufferData(bufferType, (nuint) (data.Length * sizeof(TDataType)), d, BufferUsageARB.StaticDraw);
}
}
public unsafe void Update(int offset, TDataType data)
{
_gl.BufferSubData(_bufferType, offset * sizeof(TDataType), (nuint) sizeof(TDataType), data);
}
public void Bind()
{
_gl.BindBuffer(_bufferType, _handle);
}
public void Unbind()
{
_gl.BindBuffer(_bufferType, 0);
}
public void Dispose()
{
_gl.DeleteBuffer(_handle);

View File

@ -45,7 +45,13 @@ public class Camera
//We don't want to be able to look behind us by going over our head or under our feet so make sure it stays within these bounds
Pitch = Math.Clamp(Pitch, -89f, 89f);
CalculateDirection();
var direction = Vector3.Zero;
var yaw = Helper.DegreesToRadians(Yaw);
var pitch = Helper.DegreesToRadians(Pitch);
direction.X = MathF.Cos(yaw) * MathF.Cos(pitch);
direction.Y = MathF.Sin(pitch);
direction.Z = MathF.Sin(yaw) * MathF.Cos(pitch);
Direction = Vector3.Normalize(direction);
}
public Matrix4x4 GetViewMatrix()
@ -57,15 +63,4 @@ public class Camera
{
return Matrix4x4.CreatePerspectiveFieldOfView(Helper.DegreesToRadians(Zoom), AspectRatio, Near, Far);
}
public void CalculateDirection()
{
var direction = Vector3.Zero;
var yaw = Helper.DegreesToRadians(Yaw);
var pitch = Helper.DegreesToRadians(Pitch);
direction.X = MathF.Cos(yaw) * MathF.Cos(pitch);
direction.Y = MathF.Sin(pitch);
direction.Z = MathF.Sin(yaw) * MathF.Cos(pitch);
Direction = Vector3.Normalize(direction);
}
}

View File

@ -62,6 +62,8 @@ public class Cube : Model
};
Sections = new Section[1];
Sections[0] = new Section(unrealMaterial.Name, 0, (uint) Indices.Length, 0, unrealMaterial);
Sections[0] = new Section(0, (uint) Indices.Length, 0, unrealMaterial);
AddInstance(Transform.Identity);
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using CUE4Parse_Conversion.Meshes.PSK;
using Silk.NET.OpenGL;
@ -13,10 +14,9 @@ public class Model : IDisposable
private BufferObject<uint> _ebo;
private BufferObject<float> _vbo;
private BufferObject<Matrix4x4> _mvbo;
private VertexArrayObject<float, uint> _vao;
private Shader _shader;
private uint _vertexSize = 8; // Position + Normal + UV
private const uint _faceSize = 3; // just so we don't have to do .Length
private readonly uint[] _facesIndex = { 1, 0, 2 };
@ -30,7 +30,8 @@ public class Model : IDisposable
public Section[] Sections;
public readonly List<CSkelMeshBone> Skeleton;
public readonly Transform Transforms = Transform.Identity;
public int TransformsCount;
public readonly List<Transform> Transforms;
public readonly string[] TransformsLabels = {
"X Location", "Y", "Z",
"X Rotation", "Y", "Z",
@ -43,9 +44,10 @@ public class Model : IDisposable
{
Name = name;
Type = type;
Transforms = new List<Transform>();
}
public Model(string name, string type, CBaseMeshLod lod, CMeshVertex[] vertices, List<CSkelMeshBone> skeleton = null) : this(name, type)
public Model(string name, string type, CBaseMeshLod lod, CMeshVertex[] vertices, List<CSkelMeshBone> skeleton = null, Transform transform = null) : this(name, type)
{
HasVertexColors = lod.VertexColors != null;
if (HasVertexColors) _vertexSize += 4; // + Color
@ -54,6 +56,8 @@ public class Model : IDisposable
HasBones = Skeleton != null;
if (HasBones) _vertexSize += 8; // + BoneIds + BoneWeights
_vertexSize += 16; // + InstanceMatrix
var sections = lod.Sections.Value;
Sections = new Section[sections.Length];
Indices = new uint[sections.Sum(section => section.NumFaces * _faceSize)];
@ -110,6 +114,17 @@ public class Model : IDisposable
}
}
}
AddInstance(transform ?? Transform.Identity);
}
public void AddInstance(Transform transform) => Transforms.Add(transform);
public void UpdateMatrix(int index)
{
_mvbo.Bind();
_mvbo.Update(index, Transforms[index].Matrix);
_mvbo.Unbind();
}
public void Setup(GL gl)
@ -118,8 +133,6 @@ public class Model : IDisposable
_handle = _gl.CreateProgram();
_shader = new Shader(_gl);
_ebo = new BufferObject<uint>(_gl, Indices, BufferTargetARB.ElementArrayBuffer);
_vbo = new BufferObject<float>(_gl, Vertices, BufferTargetARB.ArrayBuffer);
_vao = new VertexArrayObject<float, uint>(_gl, _vbo, _ebo);
@ -130,36 +143,34 @@ public class Model : IDisposable
_vao.VertexAttributePointer(3, 4, VertexAttribPointerType.Float, _vertexSize, 8); // color
_vao.VertexAttributePointer(4, 4, VertexAttribPointerType.Int, _vertexSize, 12); // boneids
_vao.VertexAttributePointer(5, 4, VertexAttribPointerType.Float, _vertexSize, 16); // boneweights
_vao.VertexAttributePointer(6, 16, VertexAttribPointerType.Float, _vertexSize, 20); // instancematrix
TransformsCount = Transforms.Count;
var instanceMatrix = new Matrix4x4[TransformsCount];
for (var i = 0; i < instanceMatrix.Length; i++)
instanceMatrix[i] = Transforms[i].Matrix;
_mvbo = new BufferObject<Matrix4x4>(_gl, instanceMatrix, BufferTargetARB.ArrayBuffer);
for (int section = 0; section < Sections.Length; section++)
{
_vao.Bind();
_vao.BindInstancing();
_vao.Unbind();
Sections[section].Setup(_gl);
}
}
public void Bind(Camera camera)
public void Bind(Shader shader)
{
_vao.Bind();
_shader.Use();
_shader.SetUniform("uModel", Transforms.Matrix);
_shader.SetUniform("uView", camera.GetViewMatrix());
_shader.SetUniform("uProjection", camera.GetProjectionMatrix());
_shader.SetUniform("viewPos", camera.Position);
_shader.SetUniform("material.diffuseMap", 0);
_shader.SetUniform("material.normalMap", 1);
_shader.SetUniform("material.specularMap", 2);
_shader.SetUniform("material.emissionMap", 3);
_shader.SetUniform("light.position", camera.Position);
_shader.SetUniform("display_vertex_colors", DisplayVertexColors);
shader.SetUniform("display_vertex_colors", DisplayVertexColors);
var instanceCount = (uint) TransformsCount;
for (int section = 0; section < Sections.Length; section++)
{
Sections[section].Bind(_shader);
_vao.Bind();
Sections[section].Bind(shader, instanceCount);
_vao.Unbind();
}
}
@ -167,8 +178,8 @@ public class Model : IDisposable
{
_ebo.Dispose();
_vbo.Dispose();
_mvbo.Dispose();
_vao.Dispose();
_shader.Dispose();
for (int section = 0; section < Sections.Length; section++)
{
Sections[section].Dispose();

View File

@ -17,12 +17,10 @@ public class Section : IDisposable
private GL _gl;
private Vector3 _ambientLight;
private Vector3 _diffuseLight;
private Vector3 _specularLight;
private readonly FGame _game;
public readonly string Name;
public string Name;
public readonly int Index;
public readonly uint FacesCount;
public readonly int FirstFaceIndex;
@ -58,13 +56,18 @@ public class Section : IDisposable
{
if (section.Material != null && section.Material.TryLoad(out var material) && material is UMaterialInterface unrealMaterial)
{
Name = unrealMaterial.Name;
unrealMaterial.GetParams(Parameters);
SwapMaterial(unrealMaterial);
}
}
public Section(string name, int index, uint facesCount, int firstFaceIndex, UMaterialInterface unrealMaterial) : this(name, index, facesCount, firstFaceIndex)
public Section(int index, uint facesCount, int firstFaceIndex, UMaterialInterface unrealMaterial) : this(string.Empty, index, facesCount, firstFaceIndex)
{
SwapMaterial(unrealMaterial);
}
public void SwapMaterial(UMaterialInterface unrealMaterial)
{
Name = unrealMaterial.Name;
unrealMaterial.GetParams(Parameters);
}
@ -119,8 +122,6 @@ public class Section : IDisposable
// diffuse light is based on normal map, so increase ambient if no normal map
_ambientLight = new Vector3(Textures[1] == null ? 1.0f : 0.2f);
_diffuseLight = new Vector3(0.75f);
_specularLight = new Vector3(0.5f);
HasSpecularMap = Textures[2] != null;
HasDiffuseColor = DiffuseColor != Vector4.Zero;
Show = !Parameters.IsNull && !Parameters.IsTransparent;
@ -230,7 +231,7 @@ public class Section : IDisposable
}
}
public void Bind(Shader shader)
public void Bind(Shader shader, uint instanceCount)
{
for (var i = 0; i < Textures.Length; i++)
{
@ -247,11 +248,9 @@ public class Section : IDisposable
shader.SetUniform("material.shininess", Parameters.MetallicValue);
shader.SetUniform("light.ambient", _ambientLight);
shader.SetUniform("light.diffuse", _diffuseLight);
shader.SetUniform("light.specular", _specularLight);
_gl.PolygonMode(MaterialFace.FrontAndBack, Wireframe ? PolygonMode.Line : PolygonMode.Fill);
if (Show) _gl.DrawArrays(PrimitiveType.Triangles, FirstFaceIndex, FacesCount);
if (Show) _gl.DrawArraysInstanced(PrimitiveType.Triangles, FirstFaceIndex, FacesCount, instanceCount);
}
public void Dispose()

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Numerics;
using System.Windows;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Creator;
using ImGuiNET;
using Silk.NET.Input;
@ -17,7 +18,6 @@ public class SnimGui : IDisposable
private readonly ImGuiController _controller;
private readonly GraphicsAPI _api;
private readonly string _renderer;
private readonly string _version;
private readonly Vector2 _outlinerSize;
private readonly Vector2 _outlinerPosition;
@ -28,7 +28,8 @@ public class SnimGui : IDisposable
private readonly Vector2 _textureSize;
private readonly Vector2 _texturePosition;
private bool _viewportFocus;
private int _selectedModel;
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
@ -41,13 +42,12 @@ public class SnimGui : IDisposable
_controller = new ImGuiController(gl, window, input, fontConfig);
_api = window.API;
_renderer = gl.GetStringS(StringName.Renderer);
_version = gl.GetStringS(StringName.Version);
var style = ImGui.GetStyle();
var viewport = ImGui.GetMainViewport();
var titleBarHeight = ImGui.GetFontSize() + style.FramePadding.Y * 2;
_outlinerSize = new Vector2(400, 250);
_outlinerSize = new Vector2(400, 300);
_outlinerPosition = new Vector2(viewport.WorkSize.X - _outlinerSize.X, titleBarHeight);
_propertiesSize = _outlinerSize with { Y = viewport.WorkSize.Y - _outlinerSize.Y - titleBarHeight };
_propertiesPosition = new Vector2(viewport.WorkSize.X - _propertiesSize.X, _outlinerPosition.Y + _outlinerSize.Y);
@ -55,13 +55,16 @@ public class SnimGui : IDisposable
_viewportPosition = new Vector2(0, titleBarHeight);
_textureSize = _viewportSize with { Y = viewport.WorkSize.Y - _viewportSize.Y - titleBarHeight };
_texturePosition = new Vector2(0, _viewportPosition.Y + _viewportSize.Y);
_selectedModel = 0;
_selectedModel = new FGuid();
_selectedInstance = 0;
_selectedSection = 0;
Theme(style);
}
public void Construct(Vector2D<int> size, FramebufferObject framebuffer, Camera camera, IMouse mouse, IList<Model> models)
public void Increment(FGuid guid) => _selectedModel = guid;
public void Construct(Vector2D<int> size, FramebufferObject framebuffer, Camera camera, IMouse mouse, IDictionary<FGuid, Model> models)
{
DrawDockSpace(size);
DrawNavbar();
@ -119,35 +122,38 @@ public class SnimGui : IDisposable
ImGui.EndMainMenuBar();
}
private void DrawOuliner(Camera camera, IList<Model> models)
private void DrawOuliner(Camera camera, IDictionary<FGuid, Model> models)
{
ImGui.SetNextWindowSize(_outlinerSize, _firstUse);
ImGui.SetNextWindowPos(_outlinerPosition, _firstUse);
ImGui.Begin("Scene", _noResize | ImGuiWindowFlags.NoCollapse);
ImGui.Text($"Platform: {_api.API} {_api.Profile} {_api.Version.MajorVersion}.{_api.Version.MinorVersion}");
ImGui.Text($"Renderer: {_renderer}");
ImGui.Text($"Version: {_version}");
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.TreeNode("Collection"))
{
for (var i = 0; i < models.Count; i++)
var i = 0;
foreach (var (guid, model) in models)
{
var model = models[i];
ImGui.PushID(i);
if (ImGui.Selectable(model.Name, _selectedModel == i))
_selectedModel = i;
if (ImGui.Selectable(model.Name, _selectedModel == guid))
{
_selectedModel = guid;
_selectedInstance = 0;
_selectedSection = 0;
}
if (ImGui.BeginPopupContextItem())
{
if (ImGui.Selectable("Delete"))
{
_selectedModel--;
models.RemoveAt(i);
}
models.Remove(guid);
if (ImGui.Selectable("Copy to Clipboard"))
Application.Current.Dispatcher.Invoke(delegate
{
Clipboard.SetText(model.Name);
});
ImGui.EndPopup();
}
ImGui.PopID();
i++;
}
ImGui.TreePop();
}
@ -166,16 +172,22 @@ public class SnimGui : IDisposable
ImGui.End();
}
private void DrawProperties(Camera camera, IList<Model> models)
private void DrawProperties(Camera camera, IDictionary<FGuid, Model> models)
{
ImGui.SetNextWindowSize(_propertiesSize, _firstUse);
ImGui.SetNextWindowPos(_propertiesPosition, _firstUse);
ImGui.Begin("Properties", _noResize | ImGuiWindowFlags.NoCollapse);
if (_selectedModel < 0) return;
var model = models[_selectedModel];
if (!models.TryGetValue(_selectedModel, out var model)) return;
ImGui.Text($"Type: {model.Type}");
ImGui.Text($"Entity: {model.Name}");
ImGui.Separator();
if (ImGui.Button("Focus"))
camera.Position = model.Transforms[_selectedInstance].Position;
ImGui.SameLine();
ImGui.BeginDisabled(model.TransformsCount < 2);
ImGui.SliderInt("Instance", ref _selectedInstance, 0, model.TransformsCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp);
ImGui.EndDisabled();
ImGui.BeginDisabled(!model.HasVertexColors);
ImGui.Checkbox("Vertex Colors", ref model.DisplayVertexColors);
ImGui.EndDisabled();
@ -190,45 +202,46 @@ public class SnimGui : IDisposable
var index = 0;
ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Position.X, speed, 0f, 0f, "%.2f m");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Position.X, speed, 0f, 0f, "%.2f m");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Position.Z, speed, 0f, 0f, "%.2f m");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Position.Y, speed, 0f, 0f, "%.2f m");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Position.Y, speed, 0f, 0f, "%.2f m");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].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.Rotation.X, 1f, 0f, 0f, "%.1f°");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Rotation.Pitch, .5f, 0f, 0f, "%.1f°");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Rotation.Z, 1f, 0f, 0f, "%.1f°");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Rotation.Roll, .5f, 0f, 0f, "%.1f°");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Rotation.Y, 1f, 0f, 0f, "%.1f°");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].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.Scale.X, speed, 0f, 0f, "%.3f");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.X, speed, 0f, 0f, "%.3f");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Scale.Z, speed, 0f, 0f, "%.3f");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.Y, speed, 0f, 0f, "%.3f");
ImGui.PopID();
index++; ImGui.SetNextItemWidth(width); ImGui.PushID(index);
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms.Scale.Y, speed, 0f, 0f, "%.3f");
ImGui.DragFloat(model.TransformsLabels[index], ref model.Transforms[_selectedInstance].Scale.Z, speed, 0f, 0f, "%.3f");
ImGui.PopID();
model.UpdateMatrix(_selectedInstance);
ImGui.TreePop();
}
@ -245,11 +258,11 @@ public class SnimGui : IDisposable
ImGui.PushID(i);
ImGui.TableNextRow();
ImGui.TableNextColumn();
if (!section.Show)
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f)));
ImGui.TableSetColumnIndex(0);
ImGui.Text(section.Index.ToString("D"));
ImGui.TableSetColumnIndex(1);
ImGui.TableNextColumn();
if (ImGui.Selectable(section.Name, _selectedSection == i, ImGuiSelectableFlags.SpanAllColumns))
_selectedSection = i;
ImGui.PopID();
@ -261,14 +274,14 @@ public class SnimGui : IDisposable
ImGui.End();
}
private void DrawTextures(IList<Model> models)
private void DrawTextures(IDictionary<FGuid, Model> models)
{
ImGui.SetNextWindowSize(_textureSize, _firstUse);
ImGui.SetNextWindowPos(_texturePosition, _firstUse);
ImGui.Begin("Textures", _noResize | ImGuiWindowFlags.NoCollapse);
if (_selectedModel < 0) return;
var section = models[_selectedModel].Sections[_selectedSection];
if (!models.TryGetValue(_selectedModel, out var model)) return;
var section = model.Sections[_selectedSection];
ImGui.BeginGroup();
ImGui.Checkbox("Show", ref section.Show);
ImGui.Checkbox("Wireframe", ref section.Wireframe);
@ -356,7 +369,7 @@ public class SnimGui : IDisposable
ImGui.SetNextWindowSize(_viewportSize, _firstUse);
ImGui.SetNextWindowPos(_viewportPosition, _firstUse);
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
ImGui.Begin("Viewport", _noResize | flags);
ImGui.Begin($"Viewport ({_api.API} {_api.Version.MajorVersion}.{_api.Version.MinorVersion}) ({_renderer})", _noResize | flags);
ImGui.PopStyleVar();
var largest = ImGui.GetContentRegionAvail();
@ -425,6 +438,7 @@ public class SnimGui : IDisposable
io.ConfigDockingWithShift = true;
style.WindowMenuButtonPosition = ImGuiDir.Right;
style.ScrollbarSize = 10f;
// style.Colors[(int) ImGuiCol.Text] = new Vector4(0.95f, 0.96f, 0.98f, 1.00f);
// style.Colors[(int) ImGuiCol.TextDisabled] = new Vector4(0.36f, 0.42f, 0.47f, 1.00f);
// style.Colors[(int) ImGuiCol.WindowBg] = new Vector4(0.149f, 0.149f, 0.188f, 0.35f);

View File

@ -7,7 +7,11 @@ 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.UE4.Objects.Core.Misc;
using CUE4Parse.UE4.Objects.Engine;
using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse_Conversion.Meshes;
using ImGuiNET;
using Silk.NET.Core;
@ -15,9 +19,9 @@ using Silk.NET.Input;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
using Silk.NET.Windowing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Image = SixLabors.ImageSharp.Image;
namespace FModel.Views.Snooper;
@ -34,7 +38,11 @@ public class Snooper
private readonly FramebufferObject _framebuffer;
private readonly Skybox _skybox;
private readonly Grid _grid;
private readonly List<Model> _models;
private Shader _shader;
private Vector3 _diffuseLight;
private Vector3 _specularLight;
private readonly Dictionary<FGuid, Model> _models;
private Vector2D<int> _size;
private float _previousSpeed;
@ -80,7 +88,7 @@ public class Snooper
_framebuffer = new FramebufferObject(_size);
_skybox = new Skybox();
_grid = new Grid();
_models = new List<Model>();
_models = new Dictionary<FGuid, Model>();
}
public void Run(UObject export)
@ -89,29 +97,139 @@ public class Snooper
{
case UStaticMesh st when st.TryConvert(out var mesh):
{
_models.Add(new Model(st.Name, st.ExportType, mesh.LODs[0], mesh.LODs[0].Verts));
SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
var guid = st.LightingGuid;
if (!_models.TryGetValue(guid, out _))
{
_models[guid] = new Model(st.Name, st.ExportType, mesh.LODs[0], mesh.LODs[0].Verts);
SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
}
break;
}
case USkeletalMesh sk when sk.TryConvert(out var mesh):
{
_models.Add(new Model(sk.Name, sk.ExportType, mesh.LODs[0], mesh.LODs[0].Verts, mesh.RefSkeleton));
SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
var guid = Guid.NewGuid();
if (!_models.TryGetValue(guid, out _))
{
_models[guid] = new Model(sk.Name, sk.ExportType, mesh.LODs[0], mesh.LODs[0].Verts, mesh.RefSkeleton);
SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
}
break;
}
case UMaterialInstance mi:
{
_models.Add(new Cube(mi.Name, mi.ExportType, mi));
_camera = new Camera(new Vector3(0f, 0f, 2f), Vector3.Zero, 0.01f, 0.5f * 50f, 0.5f / 2f);
var guid = Guid.NewGuid();
if (!_models.TryGetValue(guid, out _))
{
_models[guid] = new Cube(mi.Name, mi.ExportType, mi);
SetupCamera(new FBox(new FVector(-.65f), new FVector(.65f)));
}
break;
}
case UWorld wd:
{
var persistentLevel = wd.PersistentLevel.Load<ULevel>();
for (var i = 0; i < persistentLevel.Actors.Length; i++)
{
if (persistentLevel.Actors[i].Load() is not { } actor || actor.ExportType == "LODActor" ||
!actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent") ||
staticMeshComponent.Load() is not { } staticMeshComp) continue;
if (!staticMeshComp.TryGetValue(out FPackageIndex staticMesh, "StaticMesh") && actor.Class is UBlueprintGeneratedClass)
{
foreach (var actorExp in actor.Class.Owner.GetExports())
if (actorExp.TryGetValue(out staticMesh, "StaticMesh"))
break;
}
if (staticMesh?.Load() is not UStaticMesh m || !m.TryConvert(out var mesh))
continue;
var guid = m.LightingGuid;
var transform = new Transform
{
Position = staticMeshComp.GetOrDefault("RelativeLocation", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO,
Rotation = staticMeshComp.GetOrDefault("RelativeRotation", FRotator.ZeroRotator),
Scale = staticMeshComp.GetOrDefault("RelativeScale3D", FVector.OneVector)
};
// can't seem to find the problem here
// some meshes should have their yaw reversed and others not
transform.Rotation.Yaw = -transform.Rotation.Yaw;
if (_models.TryGetValue(guid, out var model))
{
model.AddInstance(transform);
continue;
}
model = new Model(m.Name, m.ExportType, mesh.LODs[0], mesh.LODs[0].Verts, null, transform);
if (actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData"))
{
for (int j = 0; j < textureData.Length; j++)
{
if (textureData[j].Load() is not { } textureDataIdx)
continue;
if (textureDataIdx.TryGetValue(out FPackageIndex diffuse, "Diffuse") &&
diffuse.Load() is UTexture2D diffuseTexture)
model.Sections[j].Parameters.Diffuse = diffuseTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex normal, "Normal") &&
normal.Load() is UTexture2D normalTexture)
model.Sections[j].Parameters.Normal = normalTexture;
if (textureDataIdx.TryGetValue(out FPackageIndex specular, "Specular") &&
specular.Load() is UTexture2D specularTexture)
model.Sections[j].Parameters.Specular = specularTexture;
}
}
if (staticMeshComp.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials"))
{
var max = model.Sections.Length - 1;
for (var j = 0; j < overrideMaterials.Length; j++)
{
if (j > max) break;
if (overrideMaterials[j].Load() is not UMaterialInterface unrealMaterial) continue;
model.Sections[j].SwapMaterial(unrealMaterial);
}
}
_models[guid] = model;
}
_camera = new Camera(new Vector3(0f, 0f, 5f), Vector3.Zero, 0.01f, 1000f, 5f);
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(export));
}
// because this calls Reset from an internal class we must recall GL.GetApi(_window) on load
// so basically we can't keep our current scene and we have to setup everything again
DoLoop();
}
private void DoLoop()
{
if (_append) _append = false;
_window.Run();
// if (_window.IsInitialized)
// {
// if (!_window.GLContext.IsCurrent)
// {
// // huston we have a problem
// // this is apparently a bug
// }
// _models[^1].Setup(_gl);
// _imGui.Increment();
// }
// else _window.Initialize();
//
// while (!_window.IsClosing && _window.IsVisible)
// {
// _window.DoEvents();
// if (!_window.IsClosing && _window.IsVisible)
// _window.DoUpdate();
// if (_window.IsClosing || !_window.IsVisible)
// return;
// _window.DoRender();
// }
//
// _window.DoEvents();
// if (_window.IsClosing) _window.Reset();
}
private void SetupCamera(FBox box)
@ -120,11 +238,11 @@ public class Snooper
var center = box.GetCenter();
var position = new Vector3(0f, center.Z, box.Max.Y * 3);
var speed = far / 2f;
if (_camera == null)
if (speed > _previousSpeed)
{
_camera = new Camera(position, center, 0.01f, far * 50f, speed);
else if (speed > _previousSpeed)
_camera.Speed = _previousSpeed = speed;
_previousSpeed = _camera.Speed;
}
}
private void OnLoad()
@ -145,7 +263,10 @@ public class Snooper
_skybox.Setup(_gl);
_grid.Setup(_gl);
foreach (var model in _models)
_shader = new Shader(_gl);
_diffuseLight = new Vector3(0.75f);
_specularLight = new Vector3(0.5f);
foreach (var model in _models.Values)
{
model.Setup(_gl);
}
@ -167,9 +288,24 @@ public class Snooper
_skybox.Bind(_camera);
_grid.Bind(_camera);
foreach (var model in _models)
_shader.Use();
_shader.SetUniform("uView", _camera.GetViewMatrix());
_shader.SetUniform("uProjection", _camera.GetProjectionMatrix());
_shader.SetUniform("viewPos", _camera.Position);
_shader.SetUniform("material.diffuseMap", 0);
_shader.SetUniform("material.normalMap", 1);
_shader.SetUniform("material.specularMap", 2);
_shader.SetUniform("material.emissionMap", 3);
_shader.SetUniform("light.position", _camera.Position);
_shader.SetUniform("light.diffuse", _diffuseLight);
_shader.SetUniform("light.specular", _specularLight);
foreach (var model in _models.Values)
{
model.Bind(_camera);
model.Bind(_shader);
}
_imGui.Construct(_size, _framebuffer, _camera, _mouse, _models);
@ -211,6 +347,10 @@ public class Snooper
if (_keyboard.IsKeyPressed(Key.H))
{
// because we lose GLContext when the window is invisible after a few seconds (it's apparently a bug)
// we can't use GLContext back on next load and so, for now, we basically have to reset the window
// if we can't use GLContext, we can't generate handles, can't interact with IsVisible, State, etc
// tldr we dispose everything but don't clear models, so the more you append, the longer it takes to load
_append = true;
_window.Close();
}
@ -223,7 +363,8 @@ public class Snooper
_framebuffer.Dispose();
_grid.Dispose();
_skybox.Dispose();
foreach (var model in _models)
_shader.Dispose();
foreach (var model in _models.Values)
{
model.Dispose();
}

View File

@ -79,7 +79,7 @@ public class Texture : IDisposable
fixed (void* d = &data[0])
{
_gl.TexImage2D(TextureTarget.Texture2D, 0, (int) InternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, d);
_gl.TexImage2D(TextureTarget.Texture2D, 0, (int) InternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, d);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) GLEnum.LinearMipmapLinear);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) GLEnum.Linear);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);

View File

@ -1,4 +1,5 @@
using System.Numerics;
using CUE4Parse.UE4.Objects.Core.Math;
namespace FModel.Views.Snooper;
@ -9,16 +10,16 @@ public class Transform
get => new ();
}
public Vector3 Position = Vector3.Zero;
public Vector3 Rotation = Vector3.Zero;
public Vector3 Scale = Vector3.One;
public FVector Position = FVector.ZeroVector;
public FRotator Rotation = FRotator.ZeroRotator;
public FVector Scale = FVector.OneVector;
public Matrix4x4 Matrix =>
Matrix4x4.Identity *
Matrix4x4.CreateFromYawPitchRoll(
Helper.DegreesToRadians(Rotation.Y),
Helper.DegreesToRadians(Rotation.X),
Helper.DegreesToRadians(Rotation.Z)) *
Helper.DegreesToRadians(Rotation.Yaw),
Helper.DegreesToRadians(Rotation.Pitch),
Helper.DegreesToRadians(Rotation.Roll)) *
Matrix4x4.CreateScale(Scale) *
Matrix4x4.CreateTranslation(Position);
}

View File

@ -1,4 +1,5 @@
using System;
using System.Numerics;
using Silk.NET.OpenGL;
namespace FModel.Views.Snooper;
@ -37,6 +38,29 @@ public class VertexArrayObject<TVertexType, TIndexType> : IDisposable where TVer
_gl.BindVertexArray(_handle);
}
public unsafe void BindInstancing()
{
var vec4Size = (uint) sizeof(Vector4);
_gl.EnableVertexAttribArray(6);
_gl.VertexAttribPointer(6, 4, VertexAttribPointerType.Float, false, 4 * vec4Size, (void*)0);
_gl.EnableVertexAttribArray(7);
_gl.VertexAttribPointer(7, 4, VertexAttribPointerType.Float, false, 4 * vec4Size, (void*)(1 * vec4Size));
_gl.EnableVertexAttribArray(8);
_gl.VertexAttribPointer(8, 4, VertexAttribPointerType.Float, false, 4 * vec4Size, (void*)(2 * vec4Size));
_gl.EnableVertexAttribArray(9);
_gl.VertexAttribPointer(9, 4, VertexAttribPointerType.Float, false, 4 * vec4Size, (void*)(3 * vec4Size));
_gl.VertexAttribDivisor(6, 1);
_gl.VertexAttribDivisor(7, 1);
_gl.VertexAttribDivisor(8, 1);
_gl.VertexAttribDivisor(9, 1);
}
public void Unbind()
{
_gl.BindVertexArray(0);
}
public void Dispose()
{
_gl.DeleteVertexArray(_handle);