diff --git a/CUE4Parse b/CUE4Parse index 5f323ef9..aa5c9a48 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 5f323ef90c2ee82eb6d7967783c42a3656ed401c +Subproject commit aa5c9a485e236b1788cc484e4bb22ab8ff202573 diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs index b09146f7..f889e0f4 100644 --- a/FModel/App.xaml.cs +++ b/FModel/App.xaml.cs @@ -89,9 +89,13 @@ public partial class App Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Logs")); Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")); +#if DEBUG + Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).CreateLogger(); +#else Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).WriteTo.File( Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.txt"), outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [FModel] [{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger(); +#endif Log.Information("Version {Version}", Constants.APP_VERSION); Log.Information("{OS}", GetOperatingSystemProductName()); diff --git a/FModel/Constants.cs b/FModel/Constants.cs index 9ea96e56..6deb29c7 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -9,6 +9,8 @@ public static class Constants public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000"; public static readonly FGuid ZERO_GUID = new(0U); + public const float SCALE_DOWN_RATIO = 0.01F; + public const string WHITE = "#DAE5F2"; public const string GRAY = "#BBBBBB"; public const string RED = "#E06C75"; diff --git a/FModel/Helper.cs b/FModel/Helper.cs index 93103474..a44a3d09 100644 --- a/FModel/Helper.cs +++ b/FModel/Helper.cs @@ -105,4 +105,9 @@ public static class Helper { return MathF.PI / 180f * degrees; } + + public static float RadiansToDegrees(float radians) + { + return radians* 180f / MathF.PI; + } } diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index bf377057..88c73acc 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -79,7 +79,7 @@ public partial class MainWindow #if DEBUG await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.Extract( - "FortniteGame/Content/Environments/Props/Winter/Meshes/S_Sled.uasset")); + "/Game/Gadgets/Assets/VinderTech_GliderChute/Glider_Mark_II/Meshes/Glider_Mark_II.uasset")); #endif } diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs index 69f71276..e41094f0 100644 --- a/FModel/Views/Snooper/Camera.cs +++ b/FModel/Views/Snooper/Camera.cs @@ -6,22 +6,24 @@ namespace FModel.Views.Snooper; public class Camera { public Vector3 Position { get; set; } - public Vector3 Front { get; set; } - - public Vector3 Up { get; private set; } - public float AspectRatio { get; set; } + public Vector3 Direction { get; private set; } + public Vector3 Up = Vector3.UnitY; + public float AspectRatio { get; } public float Yaw { get; set; } = -90f; public float Pitch { get; set; } + public float Zoom = 45f; - private float Zoom = 45f; - - public Camera(Vector3 position, Vector3 front, Vector3 up, float aspectRatio) + public Camera(Vector3 position, Vector3 direction, float aspectRatio = 16f / 9f) { Position = position; + Direction = direction; AspectRatio = aspectRatio; - Front = front; - Up = up; + + // trigonometric math to calculate the cam's yaw/pitch based on position and direction to look + var yaw = MathF.Atan((-Position.X - Direction.X) / (Position.Z - Direction.Z)); + var pitch = MathF.Atan((Position.Y - Direction.Y) / (Position.Z - Direction.Z)); + ModifyDirection(Helper.RadiansToDegrees(yaw), Helper.RadiansToDegrees(pitch)); } public void ModifyZoom(float zoomAmount) @@ -38,21 +40,25 @@ 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); - var cameraDirection = Vector3.Zero; - cameraDirection.X = MathF.Cos(Helper.DegreesToRadians(Yaw)) * MathF.Cos(Helper.DegreesToRadians(Pitch)); - cameraDirection.Y = MathF.Sin(Helper.DegreesToRadians(Pitch)); - cameraDirection.Z = MathF.Sin(Helper.DegreesToRadians(Yaw)) * MathF.Cos(Helper.DegreesToRadians(Pitch)); - - Front = Vector3.Normalize(cameraDirection); + Direction = Vector3.Normalize(CalculateDirection()); } public Matrix4x4 GetViewMatrix() { - return Matrix4x4.CreateLookAt(Position, Position + Front, Up); + return Matrix4x4.CreateLookAt(Position, Position + Direction, Up); } public Matrix4x4 GetProjectionMatrix() { return Matrix4x4.CreatePerspectiveFieldOfView(Helper.DegreesToRadians(Zoom), AspectRatio, 0.1f, 100.0f); } + + private Vector3 CalculateDirection() + { + var direction = Vector3.Zero; + direction.X = MathF.Cos(Helper.DegreesToRadians(Yaw)) * MathF.Cos(Helper.DegreesToRadians(Pitch)); + direction.Y = MathF.Sin(Helper.DegreesToRadians(Pitch)); + direction.Z = MathF.Sin(Helper.DegreesToRadians(Yaw)) * MathF.Cos(Helper.DegreesToRadians(Pitch)); + return direction; + } } diff --git a/FModel/Views/Snooper/Mesh.cs b/FModel/Views/Snooper/Mesh.cs new file mode 100644 index 00000000..70869128 --- /dev/null +++ b/FModel/Views/Snooper/Mesh.cs @@ -0,0 +1,128 @@ +using System; +using System.Linq; +using System.Numerics; +using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.Texture; +using CUE4Parse_Conversion.Meshes.PSK; +using CUE4Parse_Conversion.Textures; +using Silk.NET.OpenGL; + +namespace FModel.Views.Snooper; + +public class Mesh : IDisposable +{ + private uint _handle; + private GL _gl; + + private BufferObject _ebo; + private BufferObject _vbo; + private VertexArrayObject _vao; + + private Shader _shader; + private Texture[] _albedoMap; + // private Texture _normalMap; + + private const int _vertexSize = 8; // Position + Normals + UV + private const uint _faceSize = 3; // just so we don't have to do .Length + private readonly uint[] _facesIndex = { 1, 0, 2 }; + + public uint[] Indices; + public float[] Vertices; + public CMaterialParams[] Params; + + public Mesh(CBaseMeshLod lod, CMeshVertex[] vertices) + { + var sections = lod.Sections.Value; + Params = new CMaterialParams[sections.Length]; + Indices = new uint[sections.Sum(section => section.NumFaces * _faceSize)]; + Vertices = new float[Indices.Length * _vertexSize]; + + for (var s = 0; s < sections.Length; s++) + { + var section = sections[s]; + for (uint face = 0; face < section.NumFaces; face++) + { + foreach (var f in _facesIndex) + { + var i = face * _faceSize + f; + var index = section.FirstIndex + i; + var indice = lod.Indices.Value[index]; + + var vert = vertices[indice]; + Vertices[index * _vertexSize] = vert.Position.X * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + 1] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + 2] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; + Vertices[index * _vertexSize + 3] = vert.Normal.X; + Vertices[index * _vertexSize + 4] = vert.Normal.Z; + Vertices[index * _vertexSize + 5] = vert.Normal.Y; + Vertices[index * _vertexSize + 6] = vert.UV.U; + Vertices[index * _vertexSize + 7] = vert.UV.V; + + Indices[index] = (uint) index; + } + } + + Params[s] = new CMaterialParams(); + if (section.Material != null && section.Material.TryLoad(out var material) && material is UMaterialInterface unrealMaterial) + { + unrealMaterial.GetParams(Params[s]); + } + } + } + + public void Setup(GL gl) + { + _gl = gl; + + _handle = _gl.CreateProgram(); + + _ebo = new BufferObject(_gl, Indices, BufferTargetARB.ElementArrayBuffer); + _vbo = new BufferObject(_gl, Vertices, BufferTargetARB.ArrayBuffer); + _vao = new VertexArrayObject(_gl, _vbo, _ebo); + + _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, _vertexSize, 0); // position + _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, _vertexSize, 3); // normals + _vao.VertexAttributePointer(2, 2, VertexAttribPointerType.Float, _vertexSize, 6); // uv + + _shader = new Shader(_gl); + + _albedoMap = new Texture[Params.Length]; + for (int i = 0; i < _albedoMap.Length; i++) + { + if (Params[i].Diffuse is UTexture2D { IsVirtual: false } diffuse && diffuse.GetFirstMip() is { } mip) + { + TextureDecoder.DecodeTexture(mip, diffuse.Format, diffuse.isNormalMap, out var data, out _); + _albedoMap[i] = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY); + } + } + } + + public unsafe void Bind(Camera camera) + { + _vao.Bind(); + + _shader.Use(); + + _shader.SetUniform("uModel", Matrix4x4.Identity); + _shader.SetUniform("uView", camera.GetViewMatrix()); + _shader.SetUniform("uProjection", camera.GetProjectionMatrix()); + // _shader.SetUniform("viewPos", _camera.Position); + + for (int i = 0; i < _albedoMap.Length; i++) + { + _shader.SetUniform("material.albedo", i); + _albedoMap[i].Bind(TextureUnit.Texture0 + i); + } + + _gl.DrawElements(PrimitiveType.Triangles, (uint) Indices.Length, DrawElementsType.UnsignedInt, null); + } + + public void Dispose() + { + _ebo.Dispose(); + _vbo.Dispose(); + _vao.Dispose(); + _shader.Dispose(); + _gl.DeleteProgram(_handle); + } +} diff --git a/FModel/Views/Snooper/Shader.cs b/FModel/Views/Snooper/Shader.cs index 5c39376e..8aee622e 100644 --- a/FModel/Views/Snooper/Shader.cs +++ b/FModel/Views/Snooper/Shader.cs @@ -48,7 +48,6 @@ struct Material { }; uniform Material material; -uniform vec3 viewPos; out vec4 FragColor; diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index d7ebecb7..14e8bcbe 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -1,12 +1,10 @@ using System; -using System.Linq; using System.Numerics; 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_Conversion.Meshes; -using CUE4Parse_Conversion.Textures; using Silk.NET.Input; using Silk.NET.Maths; using Silk.NET.OpenGL; @@ -22,25 +20,11 @@ public class Snooper private IKeyboard _keyboard; private Vector2 _previousMousePosition; - private BufferObject _ebo; - private BufferObject _vbo; - private VertexArrayObject _vao; - private Shader _shader; - private CMaterialParams _params; - - private Texture _albedoMap; - private Texture _normalMap; - - private uint[] _indices; - private float[] _vertices; + private Mesh[] _meshes; public int Width { get; } public int Height { get; } - private const int _vertexSize = 8; // Position + Normals + UV - private const uint _faceSize = 3; // just so we don't have to do .Length - private readonly uint[] _facesIndex = { 1, 0, 2 }; - public Snooper(UObject export) { const double ratio = .6; @@ -60,42 +44,19 @@ public class Snooper _window.Render += OnRender; _window.Closing += OnClose; + _meshes = new Mesh[1]; switch (export) { case UStaticMesh st when st.TryConvert(out var mesh): { - _indices = new uint[mesh.LODs[0].Sections.Value.Sum(section => section.NumFaces * _faceSize)]; - _vertices = new float[_indices.Length * _vertexSize]; - foreach (var section in mesh.LODs[0].Sections.Value) - { - for (uint face = 0; face < section.NumFaces; face++) - { - foreach (var f in _facesIndex) - { - var i = face * _faceSize + f; - var index = section.FirstIndex + i; - var indice = mesh.LODs[0].Indices.Value[index]; - - var vert = mesh.LODs[0].Verts[indice]; - _vertices[index * _vertexSize] = vert.Position.X * 0.01f; - _vertices[index * _vertexSize + 1] = vert.Position.Z * 0.01f; - _vertices[index * _vertexSize + 2] = vert.Position.Y * 0.01f; - _vertices[index * _vertexSize + 3] = vert.Normal.X; - _vertices[index * _vertexSize + 4] = vert.Normal.Z; - _vertices[index * _vertexSize + 5] = vert.Normal.Y; - _vertices[index * _vertexSize + 6] = vert.UV.U; - _vertices[index * _vertexSize + 7] = vert.UV.V; - - _indices[index] = i; - } - } - - _params = new CMaterialParams(); - if (section.Material != null && section.Material.TryLoad(out var material) && material is UMaterialInterface unrealMaterial) - { - unrealMaterial.GetParams(_params); - } - } + _meshes[0] = new Mesh(mesh.LODs[0], mesh.LODs[0].Verts); + SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); + break; + } + case USkeletalMesh sk when sk.TryConvert(out var mesh): + { + _meshes[0] = new Mesh(mesh.LODs[0], mesh.LODs[0].Verts); + SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); break; } default: @@ -108,6 +69,17 @@ public class Snooper _window.Run(); } + private void SetupCamera(FBox box) + { + // X Yaw Gauche Droite + // Y Pitch Haut Bas + // Z Avant Arrière + + var center = box.GetCenter(); + var position = new Vector3(0f, center.Z, box.Max.Y * 3); + _camera = new Camera(position, center); + } + private void OnLoad() { var input = _window.CreateInput(); @@ -121,65 +93,44 @@ public class Snooper } _gl = GL.GetApi(_window); + _gl.Enable(EnableCap.DepthTest); - _ebo = new BufferObject(_gl, _indices, BufferTargetARB.ElementArrayBuffer); - _vbo = new BufferObject(_gl, _vertices, BufferTargetARB.ArrayBuffer); - _vao = new VertexArrayObject(_gl, _vbo, _ebo); - - _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, _vertexSize, 0); // position - _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, _vertexSize, 3); // normals - _vao.VertexAttributePointer(2, 2, VertexAttribPointerType.Float, _vertexSize, 6); // uv - - _shader = new Shader(_gl); - - _camera = new Camera(Vector3.UnitZ * 6, Vector3.UnitZ * -1, Vector3.UnitY, Width / Height); - - if (_params.Diffuse is UTexture2D { IsVirtual: false } diffuse && diffuse.GetFirstMip() is { } mip) + foreach (var mesh in _meshes) { - TextureDecoder.DecodeTexture(mip, diffuse.Format, diffuse.isNormalMap, out var data, out _); - _albedoMap = new Texture(_gl, data, (uint) mip.SizeX, (uint) mip.SizeY); + mesh.Setup(_gl); } } - private unsafe void OnRender(double deltaTime) + private void OnRender(double deltaTime) { - _gl.Clear((uint) ClearBufferMask.ColorBufferBit); + _gl.ClearColor(0.149f, 0.149f, 0.188f, 1.0f); + _gl.Clear((uint) ClearBufferMask.ColorBufferBit | (uint) ClearBufferMask.DepthBufferBit); - _vao.Bind(); - _shader.Use(); - - _albedoMap.Bind(TextureUnit.Texture0); - - _shader.SetUniform("uModel", Matrix4x4.Identity); - _shader.SetUniform("uView", _camera.GetViewMatrix()); - _shader.SetUniform("uProjection", _camera.GetProjectionMatrix()); - // _shader.SetUniform("viewPos", _camera.Position); - - //Configure the materials variables. - _shader.SetUniform("material.albedo", 0); - - _gl.DrawElements(PrimitiveType.Triangles, (uint) _indices.Length, DrawElementsType.UnsignedInt, null); + foreach (var mesh in _meshes) + { + mesh.Bind(_camera); + } } private void OnUpdate(double deltaTime) { - var speed = _keyboard.IsKeyPressed(Key.ShiftLeft) ? 5f : 2.5f; + var speed = _keyboard.IsKeyPressed(Key.ShiftLeft) ? 2.5f : 1f; var moveSpeed = speed * (float) deltaTime; if (_keyboard.IsKeyPressed(Key.W)) { - _camera.Position += moveSpeed * _camera.Front; + _camera.Position += moveSpeed * _camera.Direction; } if (_keyboard.IsKeyPressed(Key.S)) { - _camera.Position -= moveSpeed * _camera.Front; + _camera.Position -= moveSpeed * _camera.Direction; } if (_keyboard.IsKeyPressed(Key.A)) { - _camera.Position -= Vector3.Normalize(Vector3.Cross(_camera.Front, _camera.Up)) * moveSpeed; + _camera.Position -= Vector3.Normalize(Vector3.Cross(_camera.Direction, _camera.Up)) * moveSpeed; } if (_keyboard.IsKeyPressed(Key.D)) { - _camera.Position += Vector3.Normalize(Vector3.Cross(_camera.Front, _camera.Up)) * moveSpeed; + _camera.Position += Vector3.Normalize(Vector3.Cross(_camera.Direction, _camera.Up)) * moveSpeed; } if (_keyboard.IsKeyPressed(Key.E)) { @@ -212,10 +163,10 @@ public class Snooper private void OnClose() { - _ebo.Dispose(); - _vbo.Dispose(); - _vao.Dispose(); - _shader.Dispose(); + foreach (var mesh in _meshes) + { + mesh.Dispose(); + } } private void KeyDown(IKeyboard keyboard, Key key, int arg3)