From 9cf6c3281762be3bf8863820b69eb410052c84ed Mon Sep 17 00:00:00 2001 From: 4sval Date: Thu, 29 Dec 2022 21:47:58 +0100 Subject: [PATCH 001/109] speed up raw data bulk export --- CUE4Parse | 2 +- FModel/ViewModels/CUE4ParseViewModel.cs | 28 ++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index 5655c30f..80140f6d 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 5655c30f180a50bd07dc0282daace6b6edc9ea72 +Subproject commit 80140f6d70b42d904687c2afca1abb8ea1ddb1f2 diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 17cd9345..589f10e4 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -523,7 +523,15 @@ public class CUE4ParseViewModel : ViewModel } public void ExportFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => ExportData(asset.FullPath)); + { + Parallel.ForEach(folder.AssetsList.Assets, asset => + { + cancellationToken.ThrowIfCancellationRequested(); + ExportData(asset.FullPath, false); + }); + + foreach (var f in folder.Folders) ExportFolder(cancellationToken, f); + } public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs)); @@ -899,23 +907,27 @@ public class CUE4ParseViewModel : ViewModel } } - public void ExportData(string fullPath) + public void ExportData(string fullPath, bool updateUi = true) { var fileName = fullPath.SubstringAfterLast('/'); if (Provider.TrySavePackage(fullPath, out var assets)) { - foreach (var kvp in assets) + Parallel.ForEach(assets, kvp => { var path = Path.Combine(UserSettings.Default.RawDataDirectory, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/'); Directory.CreateDirectory(path.SubstringBeforeLast('/')); File.WriteAllBytes(path, kvp.Value); - } + }); - Log.Information("{FileName} successfully exported", fileName); - FLogger.AppendInformation(); - FLogger.AppendText($"Successfully exported '{fileName}'", Constants.WHITE, true); + if (updateUi) + { + Log.Information("{FileName} successfully exported", fileName); + FLogger.AppendInformation(); + FLogger.AppendText($"Successfully exported '{fileName}'", Constants.WHITE, true); + } + else ApplicationService.ApplicationView.Status.UpdateStatusLabel($"Raw Data for {fullPath.SubstringAfterLast('/')}"); } - else + else if (updateUi) { Log.Error("{FileName} could not be exported", fileName); FLogger.AppendError(); From 9a0e6aa6c679d417868f8ea8dd6a3b2582a3d6a0 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sat, 31 Dec 2022 04:52:58 +0100 Subject: [PATCH 002/109] improved generic roughness --- CUE4Parse | 2 +- FModel/Resources/default.frag | 31 ++++----- FModel/Views/Snooper/Renderer.cs | 2 +- FModel/Views/Snooper/Shading/Material.cs | 40 +++++------ FModel/Views/Snooper/Shading/Texture.cs | 4 +- FModel/Views/Snooper/Shading/TextureHelper.cs | 67 +++++++------------ FModel/Views/Snooper/SnimGui.cs | 2 +- 7 files changed, 58 insertions(+), 90 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index 80140f6d..59b004c3 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 80140f6d70b42d904687c2afca1abb8ea1ddb1f2 +Subproject commit 59b004c355647a22c0ad7945443d0de509ecdaf1 diff --git a/FModel/Resources/default.frag b/FModel/Resources/default.frag index 84b656d9..d37cab11 100644 --- a/FModel/Resources/default.frag +++ b/FModel/Resources/default.frag @@ -43,10 +43,9 @@ struct Parameters bool HasAo; vec4 EmissiveRegion; - float Specular; - float Roughness; + float RoughnessMin; + float RoughnessMax; float EmissiveMult; - float UVScale; }; struct BaseLight @@ -101,11 +100,6 @@ int LayerToIndex() return clamp(int(fTexLayer), 0, uUvCount - 1); } -vec2 ScaledTexCoords() -{ - return fTexCoords * uParameters.UVScale; -} - vec4 SamplerToVector(sampler2D s, vec2 coords) { return texture(s, coords); @@ -113,7 +107,7 @@ vec4 SamplerToVector(sampler2D s, vec2 coords) vec4 SamplerToVector(sampler2D s) { - return SamplerToVector(s, ScaledTexCoords()); + return SamplerToVector(s, fTexCoords); } vec3 ComputeNormals(int layer) @@ -153,7 +147,7 @@ vec3 CalcLight(int layer, vec3 normals, vec3 position, vec3 color, float attenua { vec3 fLambert = SamplerToVector(uParameters.Diffuse[layer].Sampler).rgb * uParameters.Diffuse[layer].Color.rgb; vec3 specular_masks = SamplerToVector(uParameters.SpecularMasks[layer].Sampler).rgb; - float roughness = max(0.0f, specular_masks.b * uParameters.Roughness); + float roughness = mix(uParameters.RoughnessMin, uParameters.RoughnessMax, specular_masks.b); vec3 l = normalize(uViewPos - fPos); @@ -174,11 +168,11 @@ vec3 CalcLight(int layer, vec3 normals, vec3 position, vec3 color, float attenua vec3 specBrdfNom = ggxDistribution(roughness, nDotH) * geomSmith(roughness, nDotL) * geomSmith(roughness, nDotV) * f; float specBrdfDenom = 4.0 * nDotV * nDotL + 0.0001; - vec3 specBrdf = uParameters.Specular * specular_masks.r * specBrdfNom / specBrdfDenom; + vec3 specBrdf = specBrdfNom / specBrdfDenom; vec3 diffuseBrdf = fLambert; if (!global) diffuseBrdf = kD * fLambert / PI; - return (diffuseBrdf + specBrdf) * color * nDotL * attenuation; + return (diffuseBrdf + specBrdf) * color * attenuation * nDotL; } vec3 CalcBaseLight(int layer, vec3 normals, BaseLight base, float attenuation, bool global) @@ -223,13 +217,11 @@ void main() } else if (bVertexColors[3]) { - FragColor = vec4(fNormal, 1); + int layer = LayerToIndex(); + vec3 normals = ComputeNormals(layer); + FragColor = vec4(normals, 1); } else if (bVertexColors[4]) - { - FragColor = vec4(fTangent, 1); - } - else if (bVertexColors[5]) { FragColor = vec4(fTexCoords, 0, 1); } @@ -248,10 +240,11 @@ void main() vec3 color = uParameters.Ao.ColorBoost.Color * uParameters.Ao.ColorBoost.Exponent; result = mix(result, result * color, m.b); } - result = mix(result * m.r * uParameters.Ao.AmbientOcclusion, result, m.g); + result = vec3(uParameters.Ao.AmbientOcclusion) * result * m.r; + result += CalcLight(layer, normals, vec3(0.0), vec3(0.25), m.g, false); } - vec2 coords = ScaledTexCoords(); + vec2 coords = fTexCoords; if (coords.x > uParameters.EmissiveRegion.x && coords.y > uParameters.EmissiveRegion.y && coords.x < uParameters.EmissiveRegion.z && diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 0dd1fde2..417a5e79 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -106,7 +106,7 @@ public class Renderer : IDisposable if (ShowGrid) _grid.Render(viewMatrix, projMatrix, cam.Near, cam.Far); _shader.Render(viewMatrix, cam.Position, projMatrix); - for (int i = 0; i < 6; i++) + for (int i = 0; i < 5; i++) _shader.SetUniform($"bVertexColors[{i}]", i == VertexColor); // render model pass diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index 62427490..f171ace2 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -34,10 +34,9 @@ public class Material : IDisposable public AoParams Ao; public bool HasAo; - public float Specular = 1f; - public float Roughness = 0.5f; + public float RoughnessMin = 0f; + public float RoughnessMax = 1f; public float EmissiveMult = 1f; - public float UVScale = 1f; public Material() { @@ -106,24 +105,22 @@ public class Material : IDisposable } } - // scalars - if (Parameters.TryGetScalar(out var specular, "Specular", "Specular Intensity", "Spec")) - Specular = specular; - - if (Parameters.TryGetScalar(out var roughnessMin, "RoughnessMin", "SpecRoughnessMin") && - Parameters.TryGetScalar(out var roughnessMax, "RoughnessMax", "SpecRoughnessMax")) - Roughness = (roughnessMin + roughnessMax) / 2f; + if (Parameters.TryGetScalar(out var roughnessMin, "RoughnessMin", "SpecRoughnessMin")) + RoughnessMin = roughnessMin; + if (Parameters.TryGetScalar(out var roughnessMax, "RoughnessMax", "SpecRoughnessMax")) + RoughnessMax = roughnessMax; if (Parameters.TryGetScalar(out var roughness, "Rough", "Roughness", "Ro Multiplier", "RO_mul", "Roughness_Mult")) - Roughness = roughness; + { + var d = roughness / 2; + RoughnessMin = roughness - d; + RoughnessMax = roughness + d; + } if (Parameters.TryGetScalar(out var emissiveMultScalar, "emissive mult", "Emissive_Mult", "EmissiveIntensity", "EmissionIntensity")) EmissiveMult = emissiveMultScalar; else if (Parameters.TryGetLinearColor(out var emissiveMultColor, "Emissive Multiplier", "EmissiveMultiplier")) EmissiveMult = emissiveMultColor.R; - if (Parameters.TryGetScalar(out var uvScale, "UV Scale")) - UVScale = uvScale; - if (Parameters.TryGetLinearColor(out var EmissiveUVs, "EmissiveUVs_RG_UpperLeftCorner_BA_LowerRightCorner", "Emissive Texture UVs RG_TopLeft BA_BottomRight", @@ -228,10 +225,9 @@ public class Material : IDisposable shader.SetUniform("uParameters.HasAo", HasAo); shader.SetUniform("uParameters.EmissiveRegion", EmissiveRegion); - shader.SetUniform("uParameters.Specular", Specular); - shader.SetUniform("uParameters.Roughness", Roughness); + shader.SetUniform("uParameters.RoughnessMin", RoughnessMin); + shader.SetUniform("uParameters.RoughnessMax", RoughnessMax); shader.SetUniform("uParameters.EmissiveMult", EmissiveMult); - shader.SetUniform("uParameters.UVScale", UVScale); } private const string _mult = "x %.2f"; @@ -244,14 +240,12 @@ public class Material : IDisposable if (ImGui.BeginTable("parameters", 2)) { var id = 1; - SnimGui.Layout("Specular");ImGui.PushID(id++); - ImGui.DragFloat("", ref Specular, _step, _zero, 1.0f, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Roughness");ImGui.PushID(id++); - ImGui.DragFloat("", ref Roughness, _step, _zero, 1.0f, _mult, _clamp); + SnimGui.Layout("Roughness Min");ImGui.PushID(id++); + ImGui.DragFloat("", ref RoughnessMin, _step, _zero, 1.0f, _mult, _clamp); + ImGui.PopID();SnimGui.Layout("Roughness Max");ImGui.PushID(id++); + ImGui.DragFloat("", ref RoughnessMax, _step, _zero, 1.0f, _mult, _clamp); ImGui.PopID();SnimGui.Layout("Emissive Multiplier");ImGui.PushID(id++); ImGui.DragFloat("", ref EmissiveMult, _step, _zero, _infinite, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("UV Scale");ImGui.PushID(id++); - ImGui.DragFloat("", ref UVScale, _step, _zero, _infinite, _mult, _clamp); ImGui.PopID(); if (HasAo) diff --git a/FModel/Views/Snooper/Shading/Texture.cs b/FModel/Views/Snooper/Shading/Texture.cs index 123caa8a..915e31e7 100644 --- a/FModel/Views/Snooper/Shading/Texture.cs +++ b/FModel/Views/Snooper/Shading/Texture.cs @@ -119,11 +119,13 @@ public class Texture : IDisposable ProcessPixels(textures[t], TextureTarget.TextureCubeMapPositiveX + t); } - GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear); + GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear); GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear); GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge); GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge); GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge); + + GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap); } public Texture(string texture) : this(TextureType.Normal) diff --git a/FModel/Views/Snooper/Shading/TextureHelper.cs b/FModel/Views/Snooper/Shading/TextureHelper.cs index 4df55434..b082aa4c 100644 --- a/FModel/Views/Snooper/Shading/TextureHelper.cs +++ b/FModel/Views/Snooper/Shading/TextureHelper.cs @@ -7,9 +7,9 @@ public static class TextureHelper private static readonly string _game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.GameName; /// - /// Red : Specular (if possible) - /// Blue : Roughness + /// Red : Specular (not used anymore) /// Green : Metallic + /// Blue : Roughness /// public static void FixChannels(UTexture2D o, FTexture2DMipMap mip, ref byte[] data) { @@ -17,8 +17,6 @@ public static class TextureHelper switch (_game) { case "hk_project": - case "gameface": - case "divineknockout": { unsafe { @@ -34,6 +32,27 @@ public static class TextureHelper } break; } + // R: Metallic + // G: Roughness + // B: Whatever (AO / S / E / ...) + case "shootergame": + case "divineknockout": + { + unsafe + { + var offset = 0; + fixed (byte* d = data) + { + for (var i = 0; i < mip.SizeX * mip.SizeY; i++) + { + (d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // GRB + (d[offset], d[offset + 2]) = (d[offset + 2], d[offset]); // RBG + offset += 4; + } + } + } + break; + } // R: Roughness // G: Metallic // B: Whatever (AO / S / E / ...) @@ -54,46 +73,6 @@ public static class TextureHelper } break; } - case "shootergame": - { - var packedPBRType = o.Name[(o.Name.LastIndexOf('_') + 1)..]; - switch (packedPBRType) - { - case "MRAE": // R: Metallic, G: Roughness, B: AO (0-127) & Emissive (128-255) (Character PBR) - unsafe - { - var offset = 0; - fixed (byte* d = data) - { - for (var i = 0; i < mip.SizeX * mip.SizeY; i++) - { - (d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // RMAE - // (d[offset], d[offset + 2]) = (d[offset + 2], d[offset]); // AEMR - offset += 4; - } - } - } - break; - case "MRAS": // R: Metallic, G: Roughness, B: AO, A: Specular (Legacy PBR) - case "MRA": // R: Metallic, G: 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]); // SRM - (d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // SMR - offset += 4; - } - } - } - break; - } - break; - } } } } diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index bffa926f..3ab7af6c 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -139,7 +139,7 @@ public class SnimGui ImGui.Checkbox("", ref s.Renderer.ShowLights); ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(4); ImGui.Combo("vertex_colors", ref s.Renderer.VertexColor, - "Default\0Diffuse Only\0Colors\0Normals\0Tangent\0Texture Coordinates\0"); + "Default\0Diffuse Only\0Colors\0Normals\0Texture Coordinates\0"); ImGui.PopID(); ImGui.EndTable(); From 26f9b5b9cecf278b0f1868d67db59f78b526b835 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 1 Jan 2023 03:49:25 +0100 Subject: [PATCH 003/109] camera mode --- FModel/Resources/default.frag | 1 - FModel/Settings/UserSettings.cs | 8 ++ FModel/Views/Snooper/Camera.cs | 192 +++++++++++++++++++++++---- FModel/Views/Snooper/Models/Model.cs | 14 +- FModel/Views/Snooper/Renderer.cs | 16 +-- FModel/Views/Snooper/SnimGui.cs | 9 +- FModel/Views/Snooper/Snooper.cs | 24 +--- 7 files changed, 198 insertions(+), 66 deletions(-) diff --git a/FModel/Resources/default.frag b/FModel/Resources/default.frag index d37cab11..438d1147 100644 --- a/FModel/Resources/default.frag +++ b/FModel/Resources/default.frag @@ -241,7 +241,6 @@ void main() result = mix(result, result * color, m.b); } result = vec3(uParameters.Ao.AmbientOcclusion) * result * m.r; - result += CalcLight(layer, normals, vec3(0.0), vec3(0.25), m.g, false); } vec2 coords = fTexCoords; diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 4b55c496..40b59eec 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -12,6 +12,7 @@ using CUE4Parse.UE4.Assets.Exports.Material; using FModel.Framework; using FModel.ViewModels; using FModel.ViewModels.ApiEndpoints.Models; +using FModel.Views.Snooper; using Newtonsoft.Json; namespace FModel.Settings @@ -614,6 +615,13 @@ namespace FModel.Settings set => SetProperty(ref _showGrid, value); } + private Camera.WorldMode _cameraMode = Camera.WorldMode.Arcball; + public Camera.WorldMode CameraMode + { + get => _cameraMode; + set => SetProperty(ref _cameraMode, value); + } + private bool _previewStaticMeshes = true; public bool PreviewStaticMeshes { diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs index 2672d2af..520c24a6 100644 --- a/FModel/Views/Snooper/Camera.cs +++ b/FModel/Views/Snooper/Camera.cs @@ -1,21 +1,32 @@ using System; using System.Numerics; +using CUE4Parse.UE4.Objects.Core.Math; +using FModel.Settings; using ImGuiNET; +using OpenTK.Windowing.GraphicsLibraryFramework; namespace FModel.Views.Snooper; public class Camera { + public enum WorldMode + { + FlyCam, + Arcball + } + public Vector3 Position; public Vector3 Direction; + public Vector3 Focus => Position - Direction; public Vector3 Up = Vector3.UnitY; + public WorldMode Mode = UserSettings.Default.CameraMode; public float Yaw = -90f; public float Pitch = 0f; public float Zoom = 60f; public float Speed = 1f; - public float Near = 0.01f; public float Far = 100f; + public float Near => 0.01f; public float AspectRatio = 16f / 9f; public Camera() @@ -26,51 +37,173 @@ public class Camera InitDirection(); } - public Camera(Vector3 position, Vector3 direction, float near, float far, float speed) + public Camera(FBox box, float far) + { + Far = far; + Teleport(FVector.ZeroVector, box, true); + } + + public Camera(Vector3 position, Vector3 direction, float far, float speed) { Position = position; Direction = direction; - Near = near; Far = far; Speed = speed; InitDirection(); } + public void Teleport(FVector instancePos, FBox box, bool updateSpeed = false) + { + box.GetCenterAndExtents(out var center, out var extents); + center = center.ToMapVector(); + center += instancePos; + var distance = extents.AbsMax(); + + Position = new Vector3(instancePos.X, center.Y, instancePos.Z + distance * 2); + Direction = new Vector3(center.X, center.Y, center.Z); + if (updateSpeed) Speed = distance; + + InitDirection(); + } + private void InitDirection() { // 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)); + Modify(Helper.RadiansToDegrees(yaw), Helper.RadiansToDegrees(pitch)); } - public void ModifyZoom(float zoomAmount) + public void Modify(Vector2 mouseDelta) + { + var lookSensitivity = Mode switch + { + WorldMode.FlyCam => 0.1f, + WorldMode.Arcball => 0.01f, + _ => throw new ArgumentOutOfRangeException() + }; + mouseDelta *= lookSensitivity; + Modify(mouseDelta.X, mouseDelta.Y); + } + private void Modify(float xOffset, float yOffset) + { + switch (Mode) + { + case WorldMode.FlyCam: + { + Yaw += xOffset; + Pitch -= yOffset; + Pitch = Math.Clamp(Pitch, -89f, 89f); + + 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); + break; + } + case WorldMode.Arcball: + { + var up = -Up; + var rotationX = Matrix4x4.CreateFromAxisAngle(up, xOffset); + Position = Vector3.Transform(Focus, rotationX) + Direction; + + var right = Vector3.Normalize(Vector3.Cross(up, Focus)); + var rotationY = Matrix4x4.CreateFromAxisAngle(right, yOffset); + Position = Vector3.Transform(Focus, rotationY) + Direction; + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + } + + public void Modify(KeyboardState keyboard, float time) + { + var multiplier = keyboard.IsKeyDown(Keys.LeftShift) ? 2f : 1f; + var moveSpeed = Speed * multiplier * time; + + var focus = Mode switch + { + WorldMode.FlyCam => Direction, + WorldMode.Arcball => -Focus, + _ => throw new ArgumentOutOfRangeException() + }; + + if (keyboard.IsKeyDown(Keys.W)) + Position += moveSpeed * focus; + if (keyboard.IsKeyDown(Keys.S)) + Position -= moveSpeed * focus; + + switch (Mode) + { + case WorldMode.FlyCam: + { + if (keyboard.IsKeyDown(Keys.A)) + Position -= Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; + if (keyboard.IsKeyDown(Keys.D)) + Position += Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; + if (keyboard.IsKeyDown(Keys.E)) + Position += moveSpeed * Up; + if (keyboard.IsKeyDown(Keys.Q)) + Position -= moveSpeed * Up; + break; + } + case WorldMode.Arcball: + { + if (keyboard.IsKeyDown(Keys.A)) + { + var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; + Position -= d; + Direction -= d; + } + if (keyboard.IsKeyDown(Keys.D)) + { + var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; + Position += d; + Direction += d; + } + if (keyboard.IsKeyDown(Keys.E)) + { + var d = moveSpeed * Up; + Position += d; + Direction += d; + } + if (keyboard.IsKeyDown(Keys.Q)) + { + var d = moveSpeed * Up; + Position -= d; + Direction -= d; + } + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + if (keyboard.IsKeyDown(Keys.X)) + ModifyZoom(-.5f); + if (keyboard.IsKeyDown(Keys.C)) + ModifyZoom(+.5f); + } + + private void ModifyZoom(float zoomAmount) { //We don't want to be able to zoom in too close or too far away so clamp to these values Zoom = Math.Clamp(Zoom - zoomAmount, 1.0f, 89f); } - public void ModifyDirection(float xOffset, float yOffset) - { - Yaw += xOffset; - Pitch -= yOffset; - - //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 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() { - return Matrix4x4.CreateLookAt(Position, Position + Direction, Up); + return Mode switch + { + WorldMode.FlyCam => Matrix4x4.CreateLookAt(Position, Position + Direction, Up), + WorldMode.Arcball => Matrix4x4.CreateLookAt(Position, Direction, Up), + _ => throw new ArgumentOutOfRangeException() + }; } public Matrix4x4 GetProjectionMatrix() @@ -84,13 +217,18 @@ public class Camera private const ImGuiSliderFlags _clamp = ImGuiSliderFlags.AlwaysClamp; public void ImGuiCamera() { - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new System.Numerics.Vector2(8, 3)); - ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new System.Numerics.Vector2(0, 1)); + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3)); + ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1)); if (ImGui.BeginTable("camera_editor", 2)) { - SnimGui.Layout("Speed");ImGui.PushID(1); + SnimGui.Layout("Mode"); + ImGui.PushID(1);var m = (int) Mode; + ImGui.Combo("world_mode", ref m, "Fly Cam\0Arcball\0"); + Mode = (WorldMode) m;ImGui.PopID(); + + SnimGui.Layout("Speed");ImGui.PushID(2); ImGui.DragFloat("", ref Speed, _step, _zero, _infinite, "%.2f m/s", _clamp); - ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(2); + ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(3); ImGui.DragFloat("", ref Far, 0.1f, 0.1f, Far * 2f, "%.2f m", _clamp); ImGui.PopID(); diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index 0da18c58..f4e57576 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -11,6 +11,7 @@ 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.Objects.Core.Math; using FModel.Extensions; using FModel.Services; using FModel.Settings; @@ -41,6 +42,7 @@ public class Model : IDisposable public readonly bool HasVertexColors; public readonly bool HasMorphTargets; public readonly int UvCount; + public readonly FBox Box; public uint[] Indices; public float[] Vertices; public Section[] Sections; @@ -68,12 +70,20 @@ public class Model : IDisposable Name = Path.SubstringAfterLast('/').SubstringBefore('.'); Type = export.ExportType; UvCount = 1; + Box = new FBox(new FVector(-.65f), new FVector(.65f)); Transforms = new List(); } public Model(UStaticMesh export, CStaticMesh staticMesh) : this(export, staticMesh, Transform.Identity) {} - public Model(UStaticMesh export, CStaticMesh staticMesh, Transform transform) : this(export, export.Materials, null, staticMesh.LODs.Count, staticMesh.LODs[0], staticMesh.LODs[0].Verts, transform) {} - private Model(USkeletalMesh export, CSkeletalMesh skeletalMesh, Transform transform) : this(export, export.Materials, export.Skeleton, skeletalMesh.LODs.Count, skeletalMesh.LODs[0], skeletalMesh.LODs[0].Verts, transform) {} + + public Model(UStaticMesh export, CStaticMesh staticMesh, Transform transform) : this(export, export.Materials, null, staticMesh.LODs.Count, staticMesh.LODs[0], staticMesh.LODs[0].Verts, transform) + { + Box = staticMesh.BoundingBox *= Constants.SCALE_DOWN_RATIO; + } + private Model(USkeletalMesh export, CSkeletalMesh skeletalMesh, Transform transform) : this(export, export.Materials, export.Skeleton, skeletalMesh.LODs.Count, skeletalMesh.LODs[0], skeletalMesh.LODs[0].Verts, transform) + { + Box = skeletalMesh.BoundingBox *= Constants.SCALE_DOWN_RATIO; + } public Model(USkeletalMesh export, CSkeletalMesh skeletalMesh) : this(export, skeletalMesh, Transform.Identity) { var morphTargets = export.MorphTargets; diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 417a5e79..044bce80 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -143,11 +143,7 @@ public class Renderer : IDisposable private Camera SetupCamera(FBox box) { var far = box.Max.AbsMax(); - var center = box.GetCenter(); - return new Camera( - new Vector3(0f, center.Z, box.Max.Y * 3), - new Vector3(center.X, center.Z, center.Y), - 0.01f, far * 50f, far / 1.5f); + return new Camera(box, far * 50f); } private Camera LoadStaticMesh(UStaticMesh original) @@ -165,7 +161,7 @@ public class Renderer : IDisposable Options.Models[guid] = new Model(original, mesh); Options.SelectModel(guid); - return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); + return SetupCamera(Options.Models[guid].Box); } private Camera LoadSkeletalMesh(USkeletalMesh original) @@ -175,7 +171,7 @@ public class Renderer : IDisposable Options.Models[guid] = new Model(original, mesh); Options.SelectModel(guid); - return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); + return SetupCamera(Options.Models[guid].Box); } private Camera LoadMaterialInstance(UMaterialInstance original) @@ -185,12 +181,12 @@ public class Renderer : IDisposable Options.Models[guid] = new Cube(original); Options.SelectModel(guid); - return SetupCamera(new FBox(new FVector(-.65f), new FVector(.65f))); + return SetupCamera(Options.Models[guid].Box); } private Camera LoadWorld(CancellationToken cancellationToken, UWorld original, Transform transform) { - var cam = new Camera(new Vector3(0f, 5f, 5f), Vector3.Zero, 0.01f, 1000f, 5f); + var cam = new Camera(new Vector3(0f, 5f, 5f), Vector3.Zero, 1000f, 5f); if (original.PersistentLevel.Load() is not { } persistentLevel) return cam; @@ -224,7 +220,7 @@ public class Renderer : IDisposable cam = new Camera( new Vector3(position.X, position.Y, position.Z), new Vector3(direction.X, direction.Y, direction.Z), - 0.01f, far * 25f, Math.Max(5f, far / 10f)); + far * 25f, Math.Max(5f, far / 10f)); } private void WorldLight(UObject actor) diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 3ab7af6c..b8df4f06 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -351,7 +351,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio if (ImGui.Selectable("Teleport To")) { var instancePos = model.Transforms[model.SelectedInstance].Position; - s.Camera.Position = new Vector3(instancePos.X, instancePos.Y, instancePos.Z); + s.Camera.Teleport(instancePos, model.Box); } if (ImGui.Selectable("Delete")) s.Renderer.Options.Models.Remove(guid); @@ -672,12 +672,9 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio } } - const float lookSensitivity = 0.1f; - if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus) + if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && _viewportFocus) { - var io = ImGui.GetIO(); - var delta = io.MouseDelta * lookSensitivity; - s.Camera.ModifyDirection(delta.X, delta.Y); + s.Camera.Modify(ImGui.GetIO().MouseDelta); } // if left button up and mouse was in viewport diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index 271513ed..a59860ab 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -1,10 +1,10 @@ using System; using System.ComponentModel; -using System.Numerics; using System.Runtime.InteropServices; using System.Threading; using System.Windows; using CUE4Parse.UE4.Assets.Exports; +using FModel.Settings; using FModel.Views.Snooper.Buffers; using ImGuiNET; using OpenTK.Graphics.OpenGL4; @@ -61,6 +61,7 @@ public class Snooper : GameWindow Renderer.Save(); } + UserSettings.Default.CameraMode = Camera.Mode; GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop IsVisible = !value; } @@ -82,7 +83,7 @@ public class Snooper : GameWindow private unsafe void LoadWindowIcon() { - var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/engine.png", UriKind.Relative)); + var info = Application.GetResourceStream(new Uri("/FModel;component/Resources/engine.png", UriKind.Relative)); using var img = SixLabors.ImageSharp.Image.Load(info.Stream); var memoryGroup = img.GetPixelMemoryGroup(); Memory array = new byte[memoryGroup.TotalLength * sizeof(Rgba32)]; @@ -162,24 +163,7 @@ public class Snooper : GameWindow if (!IsVisible || ImGui.GetIO().WantTextInput) return; - var multiplier = KeyboardState.IsKeyDown(Keys.LeftShift) ? 2f : 1f; - var moveSpeed = Camera.Speed * multiplier * (float) e.Time; - if (KeyboardState.IsKeyDown(Keys.W)) - Camera.Position += moveSpeed * Camera.Direction; - if (KeyboardState.IsKeyDown(Keys.S)) - Camera.Position -= moveSpeed * Camera.Direction; - if (KeyboardState.IsKeyDown(Keys.A)) - Camera.Position -= Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed; - if (KeyboardState.IsKeyDown(Keys.D)) - Camera.Position += Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed; - if (KeyboardState.IsKeyDown(Keys.E)) - Camera.Position += moveSpeed * Camera.Up; - if (KeyboardState.IsKeyDown(Keys.Q)) - Camera.Position -= moveSpeed * Camera.Up; - if (KeyboardState.IsKeyDown(Keys.X)) - Camera.ModifyZoom(-.5f); - if (KeyboardState.IsKeyDown(Keys.C)) - Camera.ModifyZoom(+.5f); + Camera.Modify(KeyboardState, (float) e.Time); if (KeyboardState.IsKeyPressed(Keys.H)) WindowShouldClose(true, false); From 76a9f88eeef8cde41e1f283cce0fae31a4cfae23 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 1 Jan 2023 20:09:46 +0100 Subject: [PATCH 004/109] cleaned camera + generated .umap position --- FModel/Views/Snooper/Camera.cs | 190 ++++++++++----------------- FModel/Views/Snooper/Models/Cube.cs | 83 ++++-------- FModel/Views/Snooper/Models/Model.cs | 32 ++--- FModel/Views/Snooper/Renderer.cs | 120 ++++++++++------- FModel/Views/Snooper/SnimGui.cs | 10 +- FModel/Views/Snooper/Snooper.cs | 23 +--- 6 files changed, 204 insertions(+), 254 deletions(-) diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs index 520c24a6..a0af4c15 100644 --- a/FModel/Views/Snooper/Camera.cs +++ b/FModel/Views/Snooper/Camera.cs @@ -17,12 +17,11 @@ public class Camera public Vector3 Position; public Vector3 Direction; - public Vector3 Focus => Position - Direction; - public Vector3 Up = Vector3.UnitY; - public WorldMode Mode = UserSettings.Default.CameraMode; + public WorldMode Mode; + public Vector3 PositionArc => Position - Direction; + public Vector3 DirectionArc => Direction - Position; + public Vector3 Up => Vector3.UnitY; - public float Yaw = -90f; - public float Pitch = 0f; public float Zoom = 60f; public float Speed = 1f; public float Far = 100f; @@ -33,27 +32,11 @@ public class Camera { Position = new Vector3(0, 1, 1); Direction = Vector3.Zero; - - InitDirection(); + Mode = UserSettings.Default.CameraMode; } - public Camera(FBox box, float far) - { - Far = far; - Teleport(FVector.ZeroVector, box, true); - } - - public Camera(Vector3 position, Vector3 direction, float far, float speed) - { - Position = position; - Direction = direction; - Far = far; - Speed = speed; - - InitDirection(); - } - - public void Teleport(FVector instancePos, FBox box, bool updateSpeed = false) + public void Setup(FBox box) => Teleport(FVector.ZeroVector, box, true); + public void Teleport(FVector instancePos, FBox box, bool updateAll = false) { box.GetCenterAndExtents(out var center, out var extents); center = center.ToMapVector(); @@ -62,58 +45,42 @@ public class Camera Position = new Vector3(instancePos.X, center.Y, instancePos.Z + distance * 2); Direction = new Vector3(center.X, center.Y, center.Z); - if (updateSpeed) Speed = distance; - - InitDirection(); - } - - private void InitDirection() - { - // 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)); - Modify(Helper.RadiansToDegrees(yaw), Helper.RadiansToDegrees(pitch)); + if (updateAll) + { + Far = Math.Max(Far, box.Max.AbsMax() * 50f); + Speed = Math.Max(Speed, distance); + } } public void Modify(Vector2 mouseDelta) { var lookSensitivity = Mode switch { - WorldMode.FlyCam => 0.1f, - WorldMode.Arcball => 0.01f, + WorldMode.FlyCam => 0.002f, + WorldMode.Arcball => 0.003f, _ => throw new ArgumentOutOfRangeException() }; mouseDelta *= lookSensitivity; - Modify(mouseDelta.X, mouseDelta.Y); - } - private void Modify(float xOffset, float yOffset) - { + + var rotationX = Matrix4x4.CreateFromAxisAngle(-Up, mouseDelta.X); switch (Mode) { case WorldMode.FlyCam: { - Yaw += xOffset; - Pitch -= yOffset; - Pitch = Math.Clamp(Pitch, -89f, 89f); + Direction = Vector3.Transform(DirectionArc, rotationX) + Position; - 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); + var right = Vector3.Normalize(Vector3.Cross(Up, DirectionArc)); + var rotationY = Matrix4x4.CreateFromAxisAngle(right, mouseDelta.Y); + Direction = Vector3.Transform(DirectionArc, rotationY) + Position; break; } case WorldMode.Arcball: { - var up = -Up; - var rotationX = Matrix4x4.CreateFromAxisAngle(up, xOffset); - Position = Vector3.Transform(Focus, rotationX) + Direction; + Position = Vector3.Transform(PositionArc, rotationX) + Direction; - var right = Vector3.Normalize(Vector3.Cross(up, Focus)); - var rotationY = Matrix4x4.CreateFromAxisAngle(right, yOffset); - Position = Vector3.Transform(Focus, rotationY) + Direction; + var right = Vector3.Normalize(Vector3.Cross(-Up, PositionArc)); + var rotationY = Matrix4x4.CreateFromAxisAngle(right, mouseDelta.Y); + Position = Vector3.Transform(PositionArc, rotationY) + Direction; break; } default: @@ -123,71 +90,71 @@ public class Camera public void Modify(KeyboardState keyboard, float time) { + if (!keyboard.IsAnyKeyDown) return; var multiplier = keyboard.IsKeyDown(Keys.LeftShift) ? 2f : 1f; var moveSpeed = Speed * multiplier * time; - - var focus = Mode switch - { - WorldMode.FlyCam => Direction, - WorldMode.Arcball => -Focus, - _ => throw new ArgumentOutOfRangeException() - }; - - if (keyboard.IsKeyDown(Keys.W)) - Position += moveSpeed * focus; - if (keyboard.IsKeyDown(Keys.S)) - Position -= moveSpeed * focus; + var moveAxis = Vector3.Normalize(-PositionArc); + var panAxis = Vector3.Normalize(Vector3.Cross(moveAxis, Up)); switch (Mode) { case WorldMode.FlyCam: { - if (keyboard.IsKeyDown(Keys.A)) - Position -= Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; - if (keyboard.IsKeyDown(Keys.D)) - Position += Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; - if (keyboard.IsKeyDown(Keys.E)) - Position += moveSpeed * Up; - if (keyboard.IsKeyDown(Keys.Q)) - Position -= moveSpeed * Up; + if (keyboard.IsKeyDown(Keys.W)) // forward + { + var d = moveSpeed * moveAxis; + Position += d; + Direction += d; + } + if (keyboard.IsKeyDown(Keys.S)) // backward + { + var d = moveSpeed * moveAxis; + Position -= d; + Direction -= d; + } break; } case WorldMode.Arcball: { - if (keyboard.IsKeyDown(Keys.A)) - { - var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; - Position -= d; - Direction -= d; - } - if (keyboard.IsKeyDown(Keys.D)) - { - var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed; - Position += d; - Direction += d; - } - if (keyboard.IsKeyDown(Keys.E)) - { - var d = moveSpeed * Up; - Position += d; - Direction += d; - } - if (keyboard.IsKeyDown(Keys.Q)) - { - var d = moveSpeed * Up; - Position -= d; - Direction -= d; - } + if (keyboard.IsKeyDown(Keys.W)) // forward + Position += moveSpeed * moveAxis; + if (keyboard.IsKeyDown(Keys.S)) // backward + Position -= moveSpeed * moveAxis; break; } default: throw new ArgumentOutOfRangeException(); } - if (keyboard.IsKeyDown(Keys.X)) - ModifyZoom(-.5f); - if (keyboard.IsKeyDown(Keys.C)) + if (keyboard.IsKeyDown(Keys.A)) // left + { + var d = panAxis * moveSpeed; + Position -= d; + Direction -= d; + } + if (keyboard.IsKeyDown(Keys.D)) // right + { + var d = panAxis * moveSpeed; + Position += d; + Direction += d; + } + if (keyboard.IsKeyDown(Keys.E)) // up + { + var d = moveSpeed * Up; + Position += d; + Direction += d; + } + if (keyboard.IsKeyDown(Keys.Q)) // down + { + var d = moveSpeed * Up; + Position -= d; + Direction -= d; + } + + if (keyboard.IsKeyDown(Keys.C)) // zoom in ModifyZoom(+.5f); + if (keyboard.IsKeyDown(Keys.X)) // zoom out + ModifyZoom(-.5f); } private void ModifyZoom(float zoomAmount) @@ -196,20 +163,9 @@ public class Camera Zoom = Math.Clamp(Zoom - zoomAmount, 1.0f, 89f); } - public Matrix4x4 GetViewMatrix() - { - return Mode switch - { - WorldMode.FlyCam => Matrix4x4.CreateLookAt(Position, Position + Direction, Up), - WorldMode.Arcball => Matrix4x4.CreateLookAt(Position, Direction, Up), - _ => throw new ArgumentOutOfRangeException() - }; - } - + public Matrix4x4 GetViewMatrix() => Matrix4x4.CreateLookAt(Position, Direction, Up); public Matrix4x4 GetProjectionMatrix() - { - return Matrix4x4.CreatePerspectiveFieldOfView(Helper.DegreesToRadians(Zoom), AspectRatio, Near, Far); - } + => Matrix4x4.CreatePerspectiveFieldOfView(Helper.DegreesToRadians(Zoom), AspectRatio, Near, Far); private const float _step = 0.01f; private const float _zero = 0.000001f; // doesn't actually work if _infinite is used as max value /shrug diff --git a/FModel/Views/Snooper/Models/Cube.cs b/FModel/Views/Snooper/Models/Cube.cs index b984720e..3681cf08 100644 --- a/FModel/Views/Snooper/Models/Cube.cs +++ b/FModel/Views/Snooper/Models/Cube.cs @@ -1,66 +1,41 @@ -using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse_Conversion.Meshes.PSK; +using CUE4Parse.UE4.Assets.Exports.Material; using FModel.Views.Snooper.Shading; namespace FModel.Views.Snooper.Models; public class Cube : Model { - public Cube(UMaterialInterface unrealMaterial) : base(unrealMaterial) + public Cube(CStaticMesh mesh, UMaterialInterface unrealMaterial) : base(unrealMaterial) { - Indices = new uint[] + var lod = mesh.LODs[0]; + + Indices = new uint[lod.Indices.Value.Length]; + for (int i = 0; i < Indices.Length; i++) { - 0, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, - 32, 33, 34, 35 - }; - Vertices = new float[] { - // I X Y Z Normals Tangent U V Layer - -1, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, .5f, - -1, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, .5f, - -1, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, .5f, + Indices[i] = (uint) lod.Indices.Value[i]; + } - -1, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f, - -1, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, .5f, - -1, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f, - - -1, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, .5f, - -1, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, .5f, - -1, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, .5f, - -1, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, .5f, - - -1, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f, - -1, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, .5f, - -1, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, .5f, - -1, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f, - - -1, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, .5f, - -1, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, .5f, - -1, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, .5f, - -1, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, .5f, - - -1, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f, - -1, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, .5f, - -1, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, .5f, - -1, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, .5f, - -1, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, .5f - }; + Vertices = new float[lod.NumVerts * VertexSize]; + for (int i = 0; i < lod.Verts.Length; i++) + { + var count = 0; + var baseIndex = i * VertexSize; + var vert = lod.Verts[i]; + Vertices[baseIndex + count++] = i; + Vertices[baseIndex + count++] = vert.Position.X * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vert.Position.Z * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vert.Position.Y * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vert.Normal.X; + Vertices[baseIndex + count++] = vert.Normal.Z; + Vertices[baseIndex + count++] = vert.Normal.Y; + Vertices[baseIndex + count++] = vert.Tangent.X; + Vertices[baseIndex + count++] = vert.Tangent.Z; + Vertices[baseIndex + count++] = vert.Tangent.Y; + Vertices[baseIndex + count++] = vert.UV.U; + Vertices[baseIndex + count++] = vert.UV.V; + Vertices[baseIndex + count++] = .5f; + } Materials = new Material[1]; Materials[0] = new Material(unrealMaterial) { IsUsed = true }; diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index f4e57576..a8450660 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -33,7 +33,7 @@ public class Model : IDisposable private VertexArrayObject _vao; private readonly UObject _export; - private readonly int _vertexSize = 13; // VertexIndex + Position + Normal + Tangent + UV + TextureLayer + protected readonly int VertexSize = 13; // VertexIndex + Position + Normal + Tangent + UV + TextureLayer private const int _faceSize = 3; public readonly string Path; @@ -70,7 +70,7 @@ public class Model : IDisposable Name = Path.SubstringAfterLast('/').SubstringBefore('.'); Type = export.ExportType; UvCount = 1; - Box = new FBox(new FVector(-.65f), new FVector(.65f)); + Box = new FBox(new FVector(-2f), new FVector(2f)); Transforms = new List(); } @@ -96,7 +96,7 @@ public class Model : IDisposable Morphs = new Morph[length]; for (var i = 0; i < Morphs.Length; i++) { - Morphs[i] = new Morph(Vertices, _vertexSize, morphTargets[i].Load()); + Morphs[i] = new Morph(Vertices, VertexSize, morphTargets[i].Load()); ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{Morphs[i].Name} ... {i}/{length}"); } ApplicationService.ApplicationView.Status.UpdateStatusLabel(""); @@ -117,13 +117,13 @@ public class Model : IDisposable if (lod.VertexColors is { Length: > 0}) { HasVertexColors = true; - _vertexSize += 4; // + Color + VertexSize += 4; // + Color } if (skeleton != null) { Skeleton = new Skeleton(skeleton); - _vertexSize += 8; // + BoneIds + BoneWeights + VertexSize += 8; // + BoneIds + BoneWeights } Indices = new uint[lod.Indices.Value.Length]; @@ -132,11 +132,11 @@ public class Model : IDisposable Indices[i] = (uint) lod.Indices.Value[i]; } - Vertices = new float[lod.NumVerts * _vertexSize]; + Vertices = new float[lod.NumVerts * VertexSize]; for (int i = 0; i < vertices.Length; i++) { var count = 0; - var baseIndex = i * _vertexSize; + var baseIndex = i * VertexSize; var vert = vertices[i]; Vertices[baseIndex + count++] = i; Vertices[baseIndex + count++] = vert.Position.X * Constants.SCALE_DOWN_RATIO; @@ -224,15 +224,15 @@ public class Model : IDisposable _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer); _vao = new VertexArrayObject(_vbo, _ebo); - _vao.VertexAttributePointer(0, 1, VertexAttribPointerType.Int, _vertexSize, 0); // vertex index - _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, _vertexSize, 1); // position - _vao.VertexAttributePointer(2, 3, VertexAttribPointerType.Float, _vertexSize, 4); // normal - _vao.VertexAttributePointer(3, 3, VertexAttribPointerType.Float, _vertexSize, 7); // tangent - _vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, _vertexSize, 10); // uv - if (!broken) _vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, _vertexSize, 12); // texture index - _vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, _vertexSize, 13); // color - _vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, _vertexSize, 17); // boneids - _vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, _vertexSize, 21); // boneweights + _vao.VertexAttributePointer(0, 1, VertexAttribPointerType.Int, VertexSize, 0); // vertex index + _vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, VertexSize, 1); // position + _vao.VertexAttributePointer(2, 3, VertexAttribPointerType.Float, VertexSize, 4); // normal + _vao.VertexAttributePointer(3, 3, VertexAttribPointerType.Float, VertexSize, 7); // tangent + _vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, VertexSize, 10); // uv + if (!broken) _vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, VertexSize, 12); // texture index + _vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, VertexSize, 13); // color + _vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, VertexSize, 17); // boneids + _vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, VertexSize, 21); // boneweights SetupInstances(); // instanced models transform diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 044bce80..dcd84a64 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -14,6 +14,8 @@ using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.Engine; using CUE4Parse.UE4.Objects.UObject; +using FModel.Creator; +using FModel.Extensions; using FModel.Settings; using FModel.Views.Snooper.Buffers; using FModel.Views.Snooper.Lights; @@ -36,6 +38,7 @@ public class Renderer : IDisposable public bool ShowLights; public int VertexColor; + public Camera CameraOp { get; } public PickingTexture Picking { get; } public Options Options { get; } @@ -44,6 +47,7 @@ public class Renderer : IDisposable _skybox = new Skybox(); _grid = new Grid(); + CameraOp = new Camera(); Picking = new PickingTexture(width, height); Options = new Options(); @@ -52,17 +56,25 @@ public class Renderer : IDisposable VertexColor = 0; // default } - public Camera Load(CancellationToken cancellationToken, UObject export) + public void Load(CancellationToken cancellationToken, UObject export) { ShowLights = false; - return export switch + switch (export) { - UStaticMesh st => LoadStaticMesh(st), - USkeletalMesh sk => LoadSkeletalMesh(sk), - UMaterialInstance mi => LoadMaterialInstance(mi), - UWorld wd => LoadWorld(cancellationToken, wd, Transform.Identity), - _ => throw new ArgumentOutOfRangeException(nameof(export)) - }; + case UStaticMesh st: + LoadStaticMesh(st); + break; + case USkeletalMesh sk: + LoadSkeletalMesh(sk); + break; + case UMaterialInstance mi: + LoadMaterialInstance(mi); + break; + case UWorld wd: + LoadWorld(cancellationToken, wd, Transform.Identity); + CameraOp.Mode = Camera.WorldMode.FlyCam; + break; + } } public void Swap(UMaterialInstance unrealMaterial) @@ -97,15 +109,15 @@ public class Renderer : IDisposable Options.SetupModelsAndLights(); } - public void Render(Camera cam) + public void Render() { - var viewMatrix = cam.GetViewMatrix(); - var projMatrix = cam.GetProjectionMatrix(); + var viewMatrix = CameraOp.GetViewMatrix(); + var projMatrix = CameraOp.GetProjectionMatrix(); if (ShowSkybox) _skybox.Render(viewMatrix, projMatrix); - if (ShowGrid) _grid.Render(viewMatrix, projMatrix, cam.Near, cam.Far); + if (ShowGrid) _grid.Render(viewMatrix, projMatrix, CameraOp.Near, CameraOp.Far); - _shader.Render(viewMatrix, cam.Position, projMatrix); + _shader.Render(viewMatrix, CameraOp.Position, projMatrix); for (int i = 0; i < 5; i++) _shader.SetUniform($"bVertexColors[{i}]", i == VertexColor); @@ -132,7 +144,7 @@ public class Renderer : IDisposable // outline pass if (Options.TryGetModel(out var selected) && selected.Show) { - _outline.Render(viewMatrix, cam.Position, projMatrix); + _outline.Render(viewMatrix, CameraOp.Position, projMatrix); selected.Outline(_outline); } @@ -140,55 +152,73 @@ public class Renderer : IDisposable Picking.Render(viewMatrix, projMatrix, Options.Models); } - private Camera SetupCamera(FBox box) - { - var far = box.Max.AbsMax(); - return new Camera(box, far * 50f); - } - - private Camera LoadStaticMesh(UStaticMesh original) + private void LoadStaticMesh(UStaticMesh original) { var guid = original.LightingGuid; if (Options.TryGetModel(guid, out var model)) { model.AddInstance(Transform.Identity); Application.Current.Dispatcher.Invoke(() => model.SetupInstances()); - return null; + return; } if (!original.TryConvert(out var mesh)) - return null; + return; Options.Models[guid] = new Model(original, mesh); Options.SelectModel(guid); - return SetupCamera(Options.Models[guid].Box); + SetupCamera(Options.Models[guid].Box); } - private Camera LoadSkeletalMesh(USkeletalMesh original) + private void LoadSkeletalMesh(USkeletalMesh original) { var guid = Guid.NewGuid(); - if (Options.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return null; + if (Options.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return; Options.Models[guid] = new Model(original, mesh); Options.SelectModel(guid); - return SetupCamera(Options.Models[guid].Box); + SetupCamera(Options.Models[guid].Box); } - private Camera LoadMaterialInstance(UMaterialInstance original) + private void LoadMaterialInstance(UMaterialInstance original) { - var guid = Guid.NewGuid(); - if (Options.Models.ContainsKey(guid)) return null; + if (!Utils.TryLoadObject("Engine/Content/EditorMeshes/EditorCube.EditorCube", out UStaticMesh editorCube)) + return; - Options.Models[guid] = new Cube(original); + var guid = editorCube.LightingGuid; + if (Options.TryGetModel(guid, out var model)) + { + model.Materials[0].SwapMaterial(original); + Application.Current.Dispatcher.Invoke(() => model.Materials[0].Setup(Options, model.UvCount)); + return; + } + + if (!editorCube.TryConvert(out var mesh)) + return; + + Options.Models[guid] = new Cube(mesh, original); Options.SelectModel(guid); - return SetupCamera(Options.Models[guid].Box); + SetupCamera(Options.Models[guid].Box); } - private Camera LoadWorld(CancellationToken cancellationToken, UWorld original, Transform transform) + private void SetupCamera(FBox box) => CameraOp.Setup(box); + + private void LoadWorld(CancellationToken cancellationToken, UWorld original, Transform transform) { - var cam = new Camera(new Vector3(0f, 5f, 5f), Vector3.Zero, 1000f, 5f); + CameraOp.Setup(new FBox(FVector.ZeroVector, new FVector(0, 10, 10))); if (original.PersistentLevel.Load() is not { } persistentLevel) - return cam; + return; + + if (persistentLevel.TryGetValue(out FSoftObjectPath runtimeCell, "WorldPartitionRuntimeCell") && + Utils.TryLoadObject(runtimeCell.AssetPathName.Text.SubstringBeforeWithLast(".") + runtimeCell.SubPathString.SubstringAfterLast("."), out UObject worldPartition)) + { + var ratio = MathF.Pow(Constants.SCALE_DOWN_RATIO, 2); + var position = worldPartition.GetOrDefault("Position", FVector.ZeroVector).ToMapVector() * Constants.SCALE_DOWN_RATIO; + var box = worldPartition.GetOrDefault("ContentBounds", new FBox(FVector.ZeroVector, FVector.OneVector)); + box.Min *= ratio;box.Max *= ratio; + + CameraOp.Teleport(position, box, true); + } var length = persistentLevel.Actors.Length; for (var i = 0; i < length; i++) @@ -200,27 +230,22 @@ public class Renderer : IDisposable continue; Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {i}/{length}"); - WorldCamera(actor, ref cam); + WorldCamera(actor); // WorldLight(actor); WorldMesh(actor, transform); AdditionalWorlds(actor, transform.Matrix, cancellationToken); } Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}"); - return cam; } - private void WorldCamera(UObject actor, ref Camera cam) + private void WorldCamera(UObject actor) { if (actor.ExportType != "LevelBounds" || !actor.TryGetValue(out FPackageIndex boxComponent, "BoxComponent") || boxComponent.Load() is not { } boxObject) return; var direction = boxObject.GetOrDefault("RelativeLocation", FVector.ZeroVector).ToMapVector() * Constants.SCALE_DOWN_RATIO; var position = boxObject.GetOrDefault("RelativeScale3D", FVector.OneVector).ToMapVector() / 2f * Constants.SCALE_DOWN_RATIO; - var far = position.AbsMax(); - cam = new Camera( - new Vector3(position.X, position.Y, position.Z), - new Vector3(direction.X, direction.Y, direction.Z), - far * 25f, Math.Max(5f, far / 10f)); + CameraOp.Setup(new FBox(direction, position)); } private void WorldLight(UObject actor) @@ -344,12 +369,19 @@ public class Renderer : IDisposable }; for (int j = 0; j < additionalWorlds.Length; j++) - if (Creator.Utils.TryLoadObject(additionalWorlds[j].AssetPathName.Text, out UWorld w)) + if (Utils.TryLoadObject(additionalWorlds[j].AssetPathName.Text, out UWorld w)) LoadWorld(cancellationToken, w, transform); } + public void WindowResized(int width, int height) + { + CameraOp.AspectRatio = width / (float) height; + Picking.WindowResized(width, height); + } + public void Save() { + UserSettings.Default.CameraMode = CameraOp.Mode; UserSettings.Default.ShowSkybox = ShowSkybox; UserSettings.Default.ShowGrid = ShowGrid; } diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index b8df4f06..0974e14e 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -149,7 +149,7 @@ public class SnimGui ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.CollapsingHeader("Camera")) { - s.Camera.ImGuiCamera(); + s.Renderer.CameraOp.ImGuiCamera(); } ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); @@ -351,7 +351,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio if (ImGui.Selectable("Teleport To")) { var instancePos = model.Transforms[model.SelectedInstance].Position; - s.Camera.Teleport(instancePos, model.Box); + s.Renderer.CameraOp.Teleport(instancePos, model.Box); } if (ImGui.Selectable("Delete")) s.Renderer.Options.Models.Remove(guid); @@ -524,7 +524,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.SliderInt("", ref model.SelectedInstance, 0, model.TransformsCount - 1, "Instance %i", ImGuiSliderFlags.AlwaysClamp); ImGui.EndDisabled(); ImGui.PopID(); - model.Transforms[model.SelectedInstance].ImGuiTransform(s.Camera.Speed / 100f); + model.Transforms[model.SelectedInstance].ImGuiTransform(s.Renderer.CameraOp.Speed / 100f); model.UpdateMatrix(model.SelectedInstance); ImGui.EndTabItem(); } @@ -653,7 +653,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio largest.Y -= ImGui.GetScrollY(); var size = new Vector2(largest.X, largest.Y); - s.Camera.AspectRatio = size.X / size.Y; + s.Renderer.CameraOp.AspectRatio = size.X / size.Y; ImGui.ImageButton(s.Framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0), 0); if (ImGui.IsItemHovered()) @@ -674,7 +674,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && _viewportFocus) { - s.Camera.Modify(ImGui.GetIO().MouseDelta); + s.Renderer.CameraOp.Modify(ImGui.GetIO().MouseDelta); } // if left button up and mouse was in viewport diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index a59860ab..a63826c8 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Windows; using CUE4Parse.UE4.Assets.Exports; -using FModel.Settings; using FModel.Views.Snooper.Buffers; using ImGuiNET; using OpenTK.Graphics.OpenGL4; @@ -19,19 +18,15 @@ namespace FModel.Views.Snooper; public class Snooper : GameWindow { - public Camera Camera; public readonly FramebufferObject Framebuffer; public readonly Renderer Renderer; private readonly SnimGui _gui; - private float _previousSpeed; - private bool _init; public Snooper(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings) { - Camera = new Camera(); Framebuffer = new FramebufferObject(ClientSize); Renderer = new Renderer(ClientSize.X, ClientSize.Y); @@ -41,13 +36,7 @@ public class Snooper : GameWindow public bool TryLoadExport(CancellationToken cancellationToken, UObject export) { - var newCamera = Renderer.Load(cancellationToken, export) ?? new Camera(); - if (newCamera.Speed > _previousSpeed) - { - newCamera.Zoom = Camera.Zoom; - Camera = newCamera; - _previousSpeed = Camera.Speed; - } + Renderer.Load(cancellationToken, export); return Renderer.Options.Models.Count > 0; } @@ -55,13 +44,12 @@ public class Snooper : GameWindow { if (clear) { - _previousSpeed = 0f; + Renderer.CameraOp.Speed = 0; Renderer.Options.ResetModelsAndLights(); Renderer.Options.SelectModel(Guid.Empty); Renderer.Save(); } - UserSettings.Default.CameraMode = Camera.Mode; GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop IsVisible = !value; } @@ -134,7 +122,7 @@ public class Snooper : GameWindow Framebuffer.Bind(); // switch to viewport background ClearWhatHasBeenDrawn(); // clear viewport background - Renderer.Render(Camera); + Renderer.Render(); Framebuffer.BindMsaa(); Framebuffer.Bind(0); // switch to window background @@ -163,7 +151,7 @@ public class Snooper : GameWindow if (!IsVisible || ImGui.GetIO().WantTextInput) return; - Camera.Modify(KeyboardState, (float) e.Time); + Renderer.CameraOp.Modify(KeyboardState, (float) e.Time); if (KeyboardState.IsKeyPressed(Keys.H)) WindowShouldClose(true, false); @@ -177,9 +165,8 @@ public class Snooper : GameWindow GL.Viewport(0, 0, e.Width, e.Height); - Camera.AspectRatio = e.Width / (float) e.Height; Framebuffer.WindowResized(e.Width, e.Height); - Renderer.Picking.WindowResized(e.Width, e.Height); + Renderer.WindowResized(e.Width, e.Height); _gui.Controller.WindowResized(e.Width, e.Height); } From 174401ec42b5c1e6abf1a2303efa8d2f3e1a3d9f Mon Sep 17 00:00:00 2001 From: 4sval Date: Mon, 2 Jan 2023 20:36:38 +0100 Subject: [PATCH 005/109] morph tangent + cull facing --- CUE4Parse | 2 +- FModel/Resources/default.vert | 8 +++++--- FModel/Views/Snooper/Models/Grid.cs | 2 ++ FModel/Views/Snooper/Models/Model.cs | 7 ++++++- FModel/Views/Snooper/Models/Morph.cs | 18 +++++++++++----- FModel/Views/Snooper/Models/Section.cs | 2 +- FModel/Views/Snooper/Models/Skybox.cs | 2 ++ FModel/Views/Snooper/Renderer.cs | 1 + FModel/Views/Snooper/Shading/Material.cs | 26 +++++++++++++++++++----- FModel/Views/Snooper/SnimGui.cs | 8 ++++++++ FModel/Views/Snooper/Snooper.cs | 1 + 11 files changed, 61 insertions(+), 16 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index 59b004c3..db65a8e9 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 59b004c355647a22c0ad7945443d0de509ecdaf1 +Subproject commit db65a8e98518cfaef7fa935b28a8f83e9a2f39d0 diff --git a/FModel/Resources/default.vert b/FModel/Resources/default.vert index 1f688d95..c120b5a9 100644 --- a/FModel/Resources/default.vert +++ b/FModel/Resources/default.vert @@ -9,7 +9,8 @@ layout (location = 6) in vec4 vColor; layout (location = 7) in vec4 vBoneIds; layout (location = 8) in vec4 vBoneWeights; layout (location = 9) in mat4 vInstanceMatrix; -layout (location = 13) in vec3 vMorphTarget; +layout (location = 13) in vec3 vMorphTargetPos; +layout (location = 14) in vec3 vMorphTargetTangent; //const int MAX_BONES = 0; //const int MAX_BONE_INFLUENCE = 0; @@ -28,7 +29,8 @@ out vec4 fColor; void main() { - vec4 pos = vec4(mix(vPos, vMorphTarget, uMorphTime), 1.0); + vec4 pos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0); + vec3 tangent = mix(vTangent, vMorphTargetTangent, uMorphTime); // for(int i = 0 ; i < MAX_BONE_INFLUENCE; i++) // { // if(vBoneIds[i] == -1) continue; @@ -45,7 +47,7 @@ void main() fPos = vec3(vInstanceMatrix * pos); fNormal = mat3(transpose(inverse(vInstanceMatrix))) * vNormal; - fTangent = mat3(transpose(inverse(vInstanceMatrix))) * vTangent; + fTangent = mat3(transpose(inverse(vInstanceMatrix))) * tangent; fTexCoords = vTexCoords; fTexLayer = vTexLayer; fColor = vColor; diff --git a/FModel/Views/Snooper/Models/Grid.cs b/FModel/Views/Snooper/Models/Grid.cs index 5da3e4f0..deef2f80 100644 --- a/FModel/Views/Snooper/Models/Grid.cs +++ b/FModel/Views/Snooper/Models/Grid.cs @@ -43,6 +43,7 @@ public class Grid : IDisposable public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix, float near, float far) { + GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.DepthTest); _vao.Bind(); @@ -55,6 +56,7 @@ public class Grid : IDisposable GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); GL.Enable(EnableCap.DepthTest); + GL.Enable(EnableCap.CullFace); } public void Dispose() diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index a8450660..9957667d 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -47,6 +47,7 @@ public class Model : IDisposable public float[] Vertices; public Section[] Sections; public Material[] Materials; + public bool bMirrored; public bool HasSkeleton => Skeleton is { IsLoaded: true }; public readonly Skeleton Skeleton; @@ -106,6 +107,7 @@ public class Model : IDisposable { var hasCustomUvs = lod.ExtraUV.IsValueCreated; UvCount = hasCustomUvs ? Math.Max(lod.NumTexCoords, numLods) : lod.NumTexCoords; + bMirrored = lod.IsMirrored; Materials = new Material[materials.Length]; for (int m = 0; m < Materials.Length; m++) @@ -252,7 +254,8 @@ public class Model : IDisposable _morphVbo = new BufferObject(Morphs[morph].Vertices, BufferTarget.ArrayBuffer); } _vao.Bind(); - _vao.VertexAttributePointer(13, 3, VertexAttribPointerType.Float, 3, 0); // morph position + _vao.VertexAttributePointer(13, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph position + _vao.VertexAttributePointer(14, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph tangent _vao.Unbind(); } @@ -267,6 +270,7 @@ public class Model : IDisposable public void Render(Shader shader) { + if (bMirrored) GL.Disable(EnableCap.CullFace); if (IsSelected) { GL.Enable(EnableCap.StencilTest); @@ -292,6 +296,7 @@ public class Model : IDisposable GL.StencilFunc(StencilFunction.Always, 0, 0xFF); GL.Disable(EnableCap.StencilTest); } + if (bMirrored) GL.Enable(EnableCap.CullFace); } public void SimpleRender(Shader shader) diff --git a/FModel/Views/Snooper/Models/Morph.cs b/FModel/Views/Snooper/Models/Morph.cs index 528a0873..628680fd 100644 --- a/FModel/Views/Snooper/Models/Morph.cs +++ b/FModel/Views/Snooper/Models/Morph.cs @@ -9,7 +9,7 @@ public class Morph : IDisposable { private int _handle; - private readonly int _vertexSize = 3; // Position + public static readonly int VertexSize = 6; // Position + Tangent public readonly string Name; public readonly float[] Vertices; @@ -17,37 +17,45 @@ public class Morph : IDisposable public Morph(float[] vertices, int vertexSize, UMorphTarget morphTarget) { Name = morphTarget.Name; - Vertices = new float[vertices.Length / vertexSize * _vertexSize]; + Vertices = new float[vertices.Length / vertexSize * VertexSize]; - bool TryFindVertex(uint index, out FVector positionDelta) + bool TryFindVertex(uint index, out FVector positionDelta, out FVector tangentDelta) { foreach (var vertex in morphTarget.MorphLODModels[0].Vertices) { if (vertex.SourceIdx == index) { positionDelta = vertex.PositionDelta; + tangentDelta = vertex.TangentZDelta; return true; } } positionDelta = FVector.ZeroVector; + tangentDelta = FVector.ZeroVector; return false; } for (int i = 0; i < vertices.Length; i += vertexSize) { var count = 0; - var baseIndex = i / vertexSize * _vertexSize; - if (TryFindVertex((uint) vertices[i + 0], out var positionDelta)) + var baseIndex = i / vertexSize * VertexSize; + if (TryFindVertex((uint) vertices[i + 0], out var positionDelta, out var tangentDelta)) { Vertices[baseIndex + count++] = vertices[i + 1] + positionDelta.X * Constants.SCALE_DOWN_RATIO; Vertices[baseIndex + count++] = vertices[i + 2] + positionDelta.Z * Constants.SCALE_DOWN_RATIO; Vertices[baseIndex + count++] = vertices[i + 3] + positionDelta.Y * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vertices[i + 7] + tangentDelta.X * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vertices[i + 8] + tangentDelta.Z * Constants.SCALE_DOWN_RATIO; + Vertices[baseIndex + count++] = vertices[i + 9] + tangentDelta.Y * Constants.SCALE_DOWN_RATIO; } else { Vertices[baseIndex + count++] = vertices[i + 1]; Vertices[baseIndex + count++] = vertices[i + 2]; Vertices[baseIndex + count++] = vertices[i + 3]; + Vertices[baseIndex + count++] = vertices[i + 7]; + Vertices[baseIndex + count++] = vertices[i + 8]; + Vertices[baseIndex + count++] = vertices[i + 9]; } } } diff --git a/FModel/Views/Snooper/Models/Section.cs b/FModel/Views/Snooper/Models/Section.cs index 6264818f..a9435285 100644 --- a/FModel/Views/Snooper/Models/Section.cs +++ b/FModel/Views/Snooper/Models/Section.cs @@ -27,7 +27,7 @@ public class Section : IDisposable public Section(int index, int facesCount, int firstFaceIndex, Material material) : this(index, facesCount, firstFaceIndex) { material.IsUsed = true; - Show = !material.Parameters.IsNull && !material.Parameters.IsTransparent; + Show = !material.Parameters.IsNull && !material.Parameters.IsTranslucent; } public void Setup() diff --git a/FModel/Views/Snooper/Models/Skybox.cs b/FModel/Views/Snooper/Models/Skybox.cs index 532b27d9..9ff8c53f 100644 --- a/FModel/Views/Snooper/Models/Skybox.cs +++ b/FModel/Views/Snooper/Models/Skybox.cs @@ -83,6 +83,7 @@ public class Skybox : IDisposable public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix) { + GL.Disable(EnableCap.CullFace); GL.DepthFunc(DepthFunction.Lequal); _vao.Bind(); @@ -101,6 +102,7 @@ public class Skybox : IDisposable GL.DrawArrays(PrimitiveType.Triangles, 0, 36); GL.DepthFunc(DepthFunction.Less); + GL.Enable(EnableCap.CullFace); } public void Dispose() diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index dcd84a64..9b6425ff 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -285,6 +285,7 @@ public class Renderer : IDisposable else if (m.TryConvert(out var mesh)) { model = new Model(m, mesh, t); + model.bMirrored = actor.GetOrDefault("bMirrored", model.bMirrored); if (actor.TryGetValue(out FPackageIndex baseMaterial, "BaseMaterial") && actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData")) { diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index f171ace2..5a9b9c40 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -6,6 +6,7 @@ using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.Core.Misc; +using FModel.Extensions; using FModel.Views.Snooper.Models; using ImGuiNET; using OpenTK.Graphics.OpenGL4; @@ -265,17 +266,23 @@ public class Material : IDisposable } } + public void ImGuiBaseProperties(string id) + { + if (ImGui.BeginTable(id, 2, ImGuiTableFlags.SizingStretchProp)) + { + Layout("Blend", Parameters.BlendMode.GetDescription(), true, true); + Layout("Shading", Parameters.ShadingModel.GetDescription(), true, true); + ImGui.EndTable(); + } + } + public void ImGuiDictionaries(string id, Dictionary dictionary, bool center = false, bool wrap = false) { if (ImGui.BeginTable(id, 2)) { foreach ((string key, T value) in dictionary.Reverse()) { - SnimGui.Layout(key, true); - var text = $"{value:N}"; - if (center) ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() - ImGui.CalcTextSize(text).X) / 2); - if (wrap) ImGui.TextWrapped(text); else ImGui.Text(text); - SnimGui.TooltipCopy(text); + Layout(key, value, center, wrap); } ImGui.EndTable(); } @@ -363,6 +370,15 @@ public class Material : IDisposable }; } + private void Layout(string key, T value, bool center = false, bool wrap = false) + { + SnimGui.Layout(key, true); + var text = $"{value:N}"; + if (center) ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() - ImGui.CalcTextSize(text).X) / 2); + if (wrap) ImGui.TextWrapped(text); else ImGui.Text(text); + SnimGui.TooltipCopy(text); + } + public void Dispose() { for (int i = 0; i < Diffuse.Length; i++) diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 0974e14e..0f3adfac 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -430,6 +430,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio { Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}"); Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); + Layout("Mirrored");ImGui.Text($" : {model.bMirrored}"); if (model.HasSkeleton) { Layout("Skeleton");ImGui.Text($" : {model.Skeleton.RefSkel.Name}"); @@ -591,6 +592,13 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.CollapsingHeader("Properties")) { + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); + if (ImGui.TreeNode("Base")) + { + material.ImGuiBaseProperties("base"); + ImGui.TreePop(); + } + ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.TreeNode("Scalars")) { diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index a63826c8..33c742ba 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -99,6 +99,7 @@ public class Snooper : GameWindow GL.ClearColor(OpenTK.Mathematics.Color4.Black); GL.Enable(EnableCap.Blend); + GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.Multisample); GL.StencilOp(StencilOp.Keep, StencilOp.Replace, StencilOp.Replace); From d02272b82d1e0d2873b32a1cf1525153eeae9b49 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 3 Jan 2023 20:58:09 +0100 Subject: [PATCH 006/109] fixes --- CUE4Parse | 2 +- FModel/Views/Snooper/Lights/Light.cs | 4 ++ FModel/Views/Snooper/Models/Model.cs | 56 ++++++++---------------- FModel/Views/Snooper/Models/Section.cs | 17 +------ FModel/Views/Snooper/Renderer.cs | 7 ++- FModel/Views/Snooper/Shading/Material.cs | 4 +- FModel/Views/Snooper/SnimGui.cs | 5 ++- 7 files changed, 37 insertions(+), 58 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index db65a8e9..7d4cd465 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit db65a8e98518cfaef7fa935b28a8f83e9a2f39d0 +Subproject commit 7d4cd4656e2ebf97caea2d010e20b033612e88ca diff --git a/FModel/Views/Snooper/Lights/Light.cs b/FModel/Views/Snooper/Lights/Light.cs index 374a304a..f64e0cf6 100644 --- a/FModel/Views/Snooper/Lights/Light.cs +++ b/FModel/Views/Snooper/Lights/Light.cs @@ -75,6 +75,8 @@ public abstract class Light : IDisposable public void Render(Shader shader) { + GL.Disable(EnableCap.CullFace); + _vao.Bind(); Icon?.Bind(TextureUnit.Texture0); @@ -82,6 +84,8 @@ public abstract class Light : IDisposable shader.SetUniform("uColor", Color); GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); + + GL.Enable(EnableCap.CullFace); } public virtual void Render(int i, Shader shader) diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs index 9957667d..777a75cf 100644 --- a/FModel/Views/Snooper/Models/Model.cs +++ b/FModel/Views/Snooper/Models/Model.cs @@ -47,7 +47,7 @@ public class Model : IDisposable public float[] Vertices; public Section[] Sections; public Material[] Materials; - public bool bMirrored; + public bool TwoSided; public bool HasSkeleton => Skeleton is { IsLoaded: true }; public readonly Skeleton Skeleton; @@ -107,7 +107,7 @@ public class Model : IDisposable { var hasCustomUvs = lod.ExtraUV.IsValueCreated; UvCount = hasCustomUvs ? Math.Max(lod.NumTexCoords, numLods) : lod.NumTexCoords; - bMirrored = lod.IsMirrored; + TwoSided = lod.IsTwoSided; Materials = new Material[materials.Length]; for (int m = 0; m < Materials.Length; m++) @@ -182,7 +182,8 @@ public class Model : IDisposable for (var s = 0; s < Sections.Length; s++) { var section = lod.Sections.Value[s]; - Sections[s] = new Section(section.MaterialIndex, section.NumFaces * _faceSize, section.FirstIndex, Materials[section.MaterialIndex]); + Sections[s] = new Section(section.MaterialIndex, section.NumFaces * _faceSize, section.FirstIndex); + if (section.IsValid) Sections[s].SetupMaterial(Materials[section.MaterialIndex]); } AddInstance(transform ?? Transform.Identity); @@ -262,31 +263,34 @@ public class Model : IDisposable for (int section = 0; section < Sections.Length; section++) { if (!Show) Show = Sections[section].Show; - Sections[section].Setup(); } IsSetup = true; } - public void Render(Shader shader) + public void Render(Shader shader, bool outline = false) { - if (bMirrored) GL.Disable(EnableCap.CullFace); + if (outline) GL.Disable(EnableCap.DepthTest); + if (TwoSided) GL.Disable(EnableCap.CullFace); if (IsSelected) { GL.Enable(EnableCap.StencilTest); - GL.StencilFunc(StencilFunction.Always, 1, 0xFF); + GL.StencilFunc(outline ? StencilFunction.Notequal : StencilFunction.Always, 1, 0xFF); } _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); - shader.SetUniform("uUvCount", UvCount); - shader.SetUniform("uHasVertexColors", HasVertexColors); + if (!outline) + { + shader.SetUniform("uUvCount", UvCount); + shader.SetUniform("uHasVertexColors", HasVertexColors); + } GL.PolygonMode(MaterialFace.FrontAndBack, Wireframe ? PolygonMode.Line : PolygonMode.Fill); foreach (var section in Sections) { if (!section.Show) continue; - Materials[section.MaterialIndex].Render(shader); + if (!outline) Materials[section.MaterialIndex].Render(shader); GL.DrawElementsInstanced(PrimitiveType.Triangles, section.FacesCount, DrawElementsType.UnsignedInt, section.FirstFaceIndexPtr, TransformsCount); } _vao.Unbind(); @@ -296,11 +300,14 @@ public class Model : IDisposable GL.StencilFunc(StencilFunction.Always, 0, 0xFF); GL.Disable(EnableCap.StencilTest); } - if (bMirrored) GL.Enable(EnableCap.CullFace); + if (TwoSided) GL.Enable(EnableCap.CullFace); + if (outline) GL.Enable(EnableCap.DepthTest); } public void SimpleRender(Shader shader) { + if (TwoSided) GL.Disable(EnableCap.CullFace); + _vao.Bind(); shader.SetUniform("uMorphTime", MorphTime); foreach (var section in Sections) @@ -309,28 +316,8 @@ public class Model : IDisposable GL.DrawElementsInstanced(PrimitiveType.Triangles, section.FacesCount, DrawElementsType.UnsignedInt, section.FirstFaceIndexPtr, TransformsCount); } _vao.Unbind(); - } - public void Outline(Shader shader) - { - GL.Enable(EnableCap.StencilTest); - GL.Disable(EnableCap.DepthTest); - GL.StencilFunc(StencilFunction.Notequal, 1, 0xFF); - - _vao.Bind(); - shader.SetUniform("uMorphTime", MorphTime); - - GL.PolygonMode(MaterialFace.FrontAndBack, Wireframe ? PolygonMode.Line : PolygonMode.Fill); - foreach (var section in Sections) - { - if (!section.Show) continue; - GL.DrawElementsInstancedBaseInstance(PrimitiveType.Triangles, section.FacesCount, DrawElementsType.UnsignedInt, section.FirstFaceIndexPtr, TransformsCount, SelectedInstance); - } - _vao.Unbind(); - - GL.StencilFunc(StencilFunction.Always, 0, 0xFF); - GL.Enable(EnableCap.DepthTest); - GL.Disable(EnableCap.StencilTest); + if (TwoSided) GL.Enable(EnableCap.CullFace); } public bool TrySave(out string label, out string savedFilePath) @@ -364,11 +351,6 @@ public class Model : IDisposable } } - for (int section = 0; section < Sections.Length; section++) - { - Sections[section].Dispose(); - } - GL.DeleteProgram(_handle); } } diff --git a/FModel/Views/Snooper/Models/Section.cs b/FModel/Views/Snooper/Models/Section.cs index a9435285..e39d24b4 100644 --- a/FModel/Views/Snooper/Models/Section.cs +++ b/FModel/Views/Snooper/Models/Section.cs @@ -1,13 +1,10 @@ using System; using FModel.Views.Snooper.Shading; -using OpenTK.Graphics.OpenGL4; namespace FModel.Views.Snooper.Models; -public class Section : IDisposable +public class Section { - private int _handle; - public readonly int MaterialIndex; public readonly int FacesCount; public readonly int FirstFaceIndex; @@ -24,19 +21,9 @@ public class Section : IDisposable Show = true; } - public Section(int index, int facesCount, int firstFaceIndex, Material material) : this(index, facesCount, firstFaceIndex) + public void SetupMaterial(Material material) { material.IsUsed = true; Show = !material.Parameters.IsNull && !material.Parameters.IsTranslucent; } - - public void Setup() - { - _handle = GL.CreateProgram(); - } - - public void Dispose() - { - GL.DeleteProgram(_handle); - } } diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index 9b6425ff..d0123da8 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -145,7 +145,7 @@ public class Renderer : IDisposable if (Options.TryGetModel(out var selected) && selected.Show) { _outline.Render(viewMatrix, CameraOp.Position, projMatrix); - selected.Outline(_outline); + selected.Render(_outline, true); } // picking pass (dedicated FBO, binding to 0 afterward) @@ -285,7 +285,8 @@ public class Renderer : IDisposable else if (m.TryConvert(out var mesh)) { model = new Model(m, mesh, t); - model.bMirrored = actor.GetOrDefault("bMirrored", model.bMirrored); + model.TwoSided = actor.GetOrDefault("bMirrored", staticMeshComp.GetOrDefault("bDisallowMeshPaintPerInstance", model.TwoSided)); + if (actor.TryGetValue(out FPackageIndex baseMaterial, "BaseMaterial") && actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData")) { @@ -322,6 +323,7 @@ public class Renderer : IDisposable } } } + if (staticMeshComp.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials")) { var max = model.Sections.Length - 1; @@ -333,6 +335,7 @@ public class Renderer : IDisposable model.Materials[model.Sections[j].MaterialIndex].SwapMaterial(unrealMaterial); } } + Options.Models[guid] = model; } diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index 5a9b9c40..49e74068 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -73,8 +73,8 @@ public class Material : IDisposable if (uvCount < 1 || Parameters.IsNull) { Diffuse = new[] { new Texture(new FLinearColor(1f, 0f, 0f, 1f)) }; - Normals = new[] { new Texture(new FLinearColor(0.498f, 0.498f, 0.996f, 1f))}; - SpecularMasks = new Texture[1]; + Normals = new[] { new Texture(new FLinearColor(0.498f, 0.498f, 0.996f, 1f)) }; + SpecularMasks = new [] { new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f)) }; Emissive = new Texture[1]; DiffuseColor = new[] { new Vector4(0.5f) }; EmissiveColor = new[] { Vector4.One }; diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 0f3adfac..7845fa9e 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -430,13 +430,16 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio { Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}"); Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); - Layout("Mirrored");ImGui.Text($" : {model.bMirrored}"); if (model.HasSkeleton) { Layout("Skeleton");ImGui.Text($" : {model.Skeleton.RefSkel.Name}"); Layout("Bones");ImGui.Text($" : x{model.Skeleton.RefSkel.BoneTree.Length}"); Layout("Sockets");ImGui.Text($" : x{model.Skeleton.Sockets.Length}"); } + else + { + Layout("Two Sided");ImGui.Text($" : {model.TwoSided}"); + } ImGui.EndTable(); } From eecf5f16cef7ed3860f2278cd2a6a0b8d3f5fc36 Mon Sep 17 00:00:00 2001 From: 4sval Date: Wed, 4 Jan 2023 01:35:01 +0100 Subject: [PATCH 007/109] don't you dare tell me you don't know where things go now --- FModel/MainWindow.xaml.cs | 3 ++ FModel/ViewModels/AudioPlayerViewModel.cs | 3 +- FModel/ViewModels/BackupManagerViewModel.cs | 5 +-- FModel/ViewModels/CUE4ParseViewModel.cs | 10 +++--- FModel/ViewModels/TabControlViewModel.cs | 3 +- FModel/Views/ImageMerger.xaml.cs | 5 +-- .../Controls/Rtb/CustomRichTextBox.cs | 31 ++++++++++++++++++- FModel/Views/Resources/Resources.xaml | 1 + 8 files changed, 50 insertions(+), 11 deletions(-) diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 5074460d..191079d2 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -175,6 +175,9 @@ public partial class MainWindow if (AssetsFolderName.SelectedItem is TreeItem folder) { await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExportFolder(cancellationToken, folder); }); + FLogger.AppendInformation(); + FLogger.AppendText("Successfully exported ", Constants.WHITE); + FLogger.AppendLink(folder.PathAtThisPoint, UserSettings.Default.RawDataDirectory, true); } } diff --git a/FModel/ViewModels/AudioPlayerViewModel.cs b/FModel/ViewModels/AudioPlayerViewModel.cs index eab9f342..5befa872 100644 --- a/FModel/ViewModels/AudioPlayerViewModel.cs +++ b/FModel/ViewModels/AudioPlayerViewModel.cs @@ -329,7 +329,8 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable { Log.Information("{FileName} successfully saved", fileToSave.FileName); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully saved '{fileToSave.FileName}'", Constants.WHITE, true); + FLogger.AppendText("Successfully saved ", Constants.WHITE); + FLogger.AppendLink(fileToSave.FileName, path, true); } else { diff --git a/FModel/ViewModels/BackupManagerViewModel.cs b/FModel/ViewModels/BackupManagerViewModel.cs index bef4da3b..a9c5b983 100644 --- a/FModel/ViewModels/BackupManagerViewModel.cs +++ b/FModel/ViewModels/BackupManagerViewModel.cs @@ -104,7 +104,8 @@ public class BackupManagerViewModel : ViewModel { Log.Information("{FileName} successfully {Type}", fileName, type1); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully {type1} '{fileName}'", Constants.WHITE, true); + FLogger.AppendText($"Successfully {type1} ", Constants.WHITE); + FLogger.AppendLink(fileName, fullPath, true); } else { @@ -113,4 +114,4 @@ public class BackupManagerViewModel : ViewModel FLogger.AppendText($"Could not {type2} '{fileName}'", Constants.WHITE, true); } } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 589f10e4..8a6c1056 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -897,7 +897,8 @@ public class CUE4ParseViewModel : ViewModel { Log.Information("Successfully saved {FilePath}", savedFilePath); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully saved {label}", Constants.WHITE, true); + FLogger.AppendText("Successfully saved ", Constants.WHITE); + FLogger.AppendLink(label, savedFilePath, true); } else { @@ -912,9 +913,10 @@ public class CUE4ParseViewModel : ViewModel var fileName = fullPath.SubstringAfterLast('/'); if (Provider.TrySavePackage(fullPath, out var assets)) { + string path = UserSettings.Default.RawDataDirectory; Parallel.ForEach(assets, kvp => { - var path = Path.Combine(UserSettings.Default.RawDataDirectory, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/'); + path = Path.Combine(path, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/'); Directory.CreateDirectory(path.SubstringBeforeLast('/')); File.WriteAllBytes(path, kvp.Value); }); @@ -923,9 +925,9 @@ public class CUE4ParseViewModel : ViewModel { Log.Information("{FileName} successfully exported", fileName); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully exported '{fileName}'", Constants.WHITE, true); + FLogger.AppendText("Successfully exported ", Constants.WHITE); + FLogger.AppendLink(fileName, path, true); } - else ApplicationService.ApplicationView.Status.UpdateStatusLabel($"Raw Data for {fullPath.SubstringAfterLast('/')}"); } else if (updateUi) { diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index f768d4e1..440b42a4 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -379,7 +379,8 @@ public class TabItem : ViewModel { Log.Information("{FileName} successfully saved", fileName); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully saved '{fileName}'", Constants.WHITE, true); + FLogger.AppendText("Successfully saved ", Constants.WHITE); + FLogger.AppendLink(fileName, path, true); } else { diff --git a/FModel/Views/ImageMerger.xaml.cs b/FModel/Views/ImageMerger.xaml.cs index 7bc5def0..f6175aec 100644 --- a/FModel/Views/ImageMerger.xaml.cs +++ b/FModel/Views/ImageMerger.xaml.cs @@ -280,7 +280,8 @@ public partial class ImageMerger { Log.Information("{FileName} successfully saved", fileName); FLogger.AppendInformation(); - FLogger.AppendText($"Successfully saved '{fileName}'", Constants.WHITE, true); + FLogger.AppendText("Successfully saved ", Constants.WHITE); + FLogger.AppendLink(fileName, path, true); } else { @@ -294,4 +295,4 @@ public partial class ImageMerger { ClipboardExtensions.SetImage(_imageBuffer, FILENAME); } -} \ No newline at end of file +} diff --git a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs index cc5faaa1..26e334bb 100644 --- a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs +++ b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; +using System.Windows.Input; using System.Windows.Media; namespace FModel.Views.Resources.Controls; @@ -46,6 +48,33 @@ public class FLogger : ITextFormatter }); } + public static void AppendLink(string message, string url, bool newLine = false) + { + Application.Current.Dispatcher.Invoke(delegate + { + var link = new Hyperlink(new Run(newLine ? $"{message}{Environment.NewLine}" : message), Logger.Document.ContentEnd) + { + NavigateUri = new Uri(url), + OverridesDefaultStyle = true, + Style = new Style(typeof(Hyperlink)) { Setters = + { + new Setter(FrameworkContentElement.CursorProperty, Cursors.Hand), + new Setter(TextBlock.TextDecorationsProperty, TextDecorations.Underline), + new Setter(TextElement.ForegroundProperty, Brushes.Cornsilk) + }} + }; + + try + { + link.Click += (sender, _) => Process.Start("explorer.exe", $"/select, \"{((Hyperlink)sender).NavigateUri.AbsoluteUri}\""); + } + finally + { + Logger.ScrollToEnd(); + } + }); + } + public string GetText(FlowDocument document) { return new TextRange(document.ContentStart, document.ContentEnd).Text; @@ -168,4 +197,4 @@ public class CustomRichTextBox : RichTextBox UpdateTextFromDocument(); } } -} \ No newline at end of file +} diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index bd11345e..3d03acd8 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -1367,6 +1367,7 @@ diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 12e09244..03a5fd18 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -63,7 +63,7 @@ public partial class MainWindow await _applicationView.CUE4Parse.Initialize(); await _applicationView.AesManager.InitAes(); - await _applicationView.AesManager.UpdateProvider(true); + await _applicationView.UpdateProvider(true); #if !DEBUG await _applicationView.CUE4Parse.InitInformation(); #endif @@ -73,15 +73,12 @@ public partial class MainWindow await _applicationView.InitOodle(); if (UserSettings.Default.DiscordRpc == EDiscordRpc.Always) - _discordHandler.Initialize(_applicationView.CUE4Parse.Provider.GameName); + _discordHandler.Initialize(_applicationView.GameDisplayName); #if DEBUG - await _threadWorkerView.Begin(cancellationToken => - _applicationView.CUE4Parse.Extract(cancellationToken, - "ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Models/AB_BountyHunter_S0_4_TrailCreature_Skelmesh.uasset")); - await _threadWorkerView.Begin(cancellationToken => - _applicationView.CUE4Parse.Extract(cancellationToken, - "ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Anims/FP_BountyHunter_S0_4_Aim_S.uasset")); + // await _threadWorkerView.Begin(cancellationToken => + // _applicationView.CUE4Parse.Extract(cancellationToken, + // "Discovery/Content/Discovery/Items/Charms/Charm_Skateboard_01/SM_Charm_Skateboard_01_A.uasset")); #endif } diff --git a/FModel/Services/DiscordService.cs b/FModel/Services/DiscordService.cs index 2d8712bf..822afd53 100644 --- a/FModel/Services/DiscordService.cs +++ b/FModel/Services/DiscordService.cs @@ -48,7 +48,7 @@ namespace FModel.Services public void UpdatePresence(CUE4ParseViewModel viewModel) => UpdatePresence( - $"{viewModel.Provider.GameName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages", + $"{viewModel.Provider.GameDisplayName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages", $"Mode: {UserSettings.Default.LoadingMode.GetDescription()} - {viewModel.SearchVm.ResultsCount:### ### ###} Loaded Assets".Trim()); public void UpdatePresence(string details, string state) diff --git a/FModel/ViewModels/AesManagerViewModel.cs b/FModel/ViewModels/AesManagerViewModel.cs index 82b37e18..19dc8258 100644 --- a/FModel/ViewModels/AesManagerViewModel.cs +++ b/FModel/ViewModels/AesManagerViewModel.cs @@ -103,17 +103,11 @@ public class AesManagerViewModel : ViewModel } } - public async Task UpdateProvider(bool isLaunch) + public void SetAesKeys() { - if (!isLaunch && !HasChange) return; - - _cue4Parse.ClearProvider(); - await _cue4Parse.LoadVfs(AesKeys); - if (_cue4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.ContainsKey(UserSettings.Default.GameDirectory)) UserSettings.Default.ManualGames[UserSettings.Default.GameDirectory].AesKeys = _keysFromSettings; else UserSettings.Default.AesKeys[_cue4Parse.Game] = _keysFromSettings; - Log.Information("{@Json}", UserSettings.Default); } diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index f9de0272..b922da9d 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -25,7 +25,7 @@ public class ApplicationViewModel : ViewModel public EBuildKind Build { get => _build; - private set + private init { SetProperty(ref _build, value); RaisePropertyChanged(nameof(TitleExtra)); @@ -36,7 +36,7 @@ public class ApplicationViewModel : ViewModel public FStatus Status { get => _status; - set => SetProperty(ref _status, value); + private init => SetProperty(ref _status, value); } public RightClickMenuCommand RightClickMenuCommand => _rightClickMenuCommand ??= new RightClickMenuCommand(this); @@ -46,9 +46,10 @@ public class ApplicationViewModel : ViewModel public CopyCommand CopyCommand => _copyCommand ??= new CopyCommand(this); private CopyCommand _copyCommand; + public string InitialWindowTitle => $"FModel {UserSettings.Default.UpdateMode}"; + public string GameDisplayName => CUE4Parse.Provider.GameDisplayName ?? "Unknown"; public string TitleExtra => - $"{UserSettings.Default.UpdateMode} - {CUE4Parse.Game.GetDescription()} (" + // FModel {UpdateMode} - {FGame} ({UE}) ({Build}) - $"{(CUE4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.TryGetValue(UserSettings.Default.GameDirectory, out var settings) ? settings.OverridedGame : UserSettings.Default.OverridedGame[CUE4Parse.Game])})" + + $"({(CUE4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.TryGetValue(UserSettings.Default.GameDirectory, out var settings) ? settings.OverridedGame : UserSettings.Default.OverridedGame[CUE4Parse.Game])})" + $"{(Build != EBuildKind.Release ? $" ({Build})" : "")}"; public LoadingModesViewModel LoadingModes { get; } @@ -79,6 +80,7 @@ public class ApplicationViewModel : ViewModel //A hard exit is preferable to an unhandled expection in this case Environment.Exit(0); } + CUE4Parse = new CUE4ParseViewModel(UserSettings.Default.GameDirectory); CustomDirectories = new CustomDirectoriesViewModel(CUE4Parse.Game, UserSettings.Default.GameDirectory); SettingsView = new SettingsViewModel(CUE4Parse.Game); @@ -103,6 +105,21 @@ public class ApplicationViewModel : ViewModel RestartWithWarning(); } + public async Task UpdateProvider(bool isLaunch) + { + if (!isLaunch && !AesManager.HasChange) return; + + CUE4Parse.ClearProvider(); + await ApplicationService.ThreadWorkerView.Begin(cancellationToken => + { + CUE4Parse.LoadVfs(cancellationToken, AesManager.AesKeys); + CUE4Parse.Provider.LoadIniConfigs(); + // ConsoleVariables - a.StripAdditiveRefPose=1 + AesManager.SetAesKeys(); + }); + RaisePropertyChanged(nameof(GameDisplayName)); + } + public void RestartWithWarning() { MessageBox.Show("It looks like you just changed something.\nFModel will restart to apply your changes.", "Uh oh, a restart is needed", MessageBoxButton.OK, MessageBoxImage.Warning); diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 3121471c..64e7edeb 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -298,41 +298,38 @@ public class CUE4ParseViewModel : ViewModel /// load virtual files system from GameDirectory /// /// - public async Task LoadVfs(IEnumerable aesKeys) + public void LoadVfs(CancellationToken token, IEnumerable aesKeys) { - await _threadWorkerView.Begin(cancellationToken => + GameDirectory.DeactivateAll(); + + // load files using UnloadedVfs to include non-encrypted vfs + foreach (var key in aesKeys) { - GameDirectory.DeactivateAll(); + token.ThrowIfCancellationRequested(); // cancel if needed - // load files using UnloadedVfs to include non-encrypted vfs - foreach (var key in aesKeys) + var k = key.Key.Trim(); + if (k.Length != 66) k = Constants.ZERO_64_CHAR; + Provider.SubmitKey(key.Guid, new FAesKey(k)); + } + + // files in MountedVfs will be enabled + foreach (var file in GameDirectory.DirectoryFiles) + { + token.ThrowIfCancellationRequested(); + if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs) { - cancellationToken.ThrowIfCancellationRequested(); // cancel if needed + if (Provider.UnloadedVfs.FirstOrDefault(x => x.Name == file.Name) is IoStoreReader store) + file.FileCount = (int) store.Info.TocEntryCount - 1; - var k = key.Key.Trim(); - if (k.Length != 66) k = Constants.ZERO_64_CHAR; - Provider.SubmitKey(key.Guid, new FAesKey(k)); + continue; } - // files in MountedVfs will be enabled - foreach (var file in GameDirectory.DirectoryFiles) - { - cancellationToken.ThrowIfCancellationRequested(); - if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs) - { - if (Provider.UnloadedVfs.FirstOrDefault(x => x.Name == file.Name) is IoStoreReader store) - file.FileCount = (int) store.Info.TocEntryCount - 1; + file.IsEnabled = true; + file.MountPoint = vfs.MountPoint; + file.FileCount = vfs.FileCount; + } - continue; - } - - file.IsEnabled = true; - file.MountPoint = vfs.MountPoint; - file.FileCount = vfs.FileCount; - } - - Game = Helper.IAmThePanda(Provider.GameName) ? FGame.PandaGame : Provider.GameName.ToEnum(Game); - }); + Game = Helper.IAmThePanda(Provider.GameName) ? FGame.PandaGame : Provider.GameName.ToEnum(Game); } public void ClearProvider() diff --git a/FModel/ViewModels/Commands/MenuCommand.cs b/FModel/ViewModels/Commands/MenuCommand.cs index ca4e84e7..b33e534e 100644 --- a/FModel/ViewModels/Commands/MenuCommand.cs +++ b/FModel/ViewModels/Commands/MenuCommand.cs @@ -96,28 +96,26 @@ public class MenuCommand : ViewModelCommand } } - private static void SetFoldersIsExpanded(AssetsFolderViewModel root, bool isExpanded, CancellationToken cancellationToken) + private void SetFoldersIsExpanded(AssetsFolderViewModel root, bool expand, CancellationToken cancellationToken) { - LinkedList nodes = new(); + var nodes = new LinkedList(); foreach (TreeItem folder in root.Folders) - { nodes.AddLast(folder); - } - LinkedListNode current = nodes.First; + var current = nodes.First; while (current != null) { - TreeItem folder = current.Value; + var folder = current.Value; // Collapse top-down (reduce layout updates) - if (!isExpanded) + if (!expand && folder.IsExpanded) { - folder.IsExpanded = isExpanded; + folder.IsExpanded = false; Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); } - foreach (TreeItem child in folder.Folders) + foreach (var child in folder.Folders) { nodes.AddLast(child); } @@ -125,15 +123,14 @@ public class MenuCommand : ViewModelCommand current = current.Next; } + if (!expand) return; + // Expand bottom-up (reduce layout updates) - if (isExpanded) + for (var node = nodes.Last; node != null; node = node.Previous) { - for (LinkedListNode node = nodes.Last; node != null; node = node.Previous) - { - node.Value.IsExpanded = isExpanded; - Thread.Yield(); - cancellationToken.ThrowIfCancellationRequested(); - } + node.Value.IsExpanded = true; + Thread.Yield(); + cancellationToken.ThrowIfCancellationRequested(); } } } diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index 1da8395e..ab009a4b 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -29,7 +29,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Extract_New_Tab": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true); } @@ -37,7 +37,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Export_Data": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.ExportData(asset.FullPath); } @@ -45,7 +45,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Properties": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Properties); } @@ -53,7 +53,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Textures": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Textures); } @@ -61,7 +61,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Models": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Meshes | EBulkType.Auto); } @@ -69,7 +69,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Animations": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Animations | EBulkType.Auto); } diff --git a/FModel/Views/AesManager.xaml.cs b/FModel/Views/AesManager.xaml.cs index 7abe0a41..aa0c59ba 100644 --- a/FModel/Views/AesManager.xaml.cs +++ b/FModel/Views/AesManager.xaml.cs @@ -29,6 +29,6 @@ public partial class AesManager private async void OnClosing(object sender, CancelEventArgs e) { - await _applicationView.AesManager.UpdateProvider(false); + await _applicationView.UpdateProvider(false); } -} \ No newline at end of file +} diff --git a/README.md b/README.md index 1682005f..9e49065c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ FModel is actively maintained and developed by a dedicated community of contribu ### Installation: For installation, follow the instructions from [here](https://github.com/4sval/FModel/wiki/Installing-FModel) -### Support: +### Sponsorship:

From 7bd3f7da58e91cfa87cebf5161d9e31e703866bd Mon Sep 17 00:00:00 2001 From: MountainFlash <65584814+MinshuG@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:32:11 +0530 Subject: [PATCH 090/109] Fallen Order --- FModel/ViewModels/GameSelectorViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index 35bcb99e..8fac24cb 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -118,6 +118,7 @@ public class GameSelectorViewModel : ViewModel yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks"); yield return GetSteamGame(381210, "\\DeadByDaylight\\Content\\Paks"); // Dead By Daylight yield return GetSteamGame(578080, "\\TslGame\\Content\\Paks"); // PUBG + yield return GetSteamGame(1172380, "\\SwGame\\Content\\Paks"); // STAR WARS Jedi: Fallen Order™ yield return GetSteamGame(677620, "\\PortalWars\\Content\\Paks"); // Splitgate yield return GetSteamGame(1172620, "\\Athena\\Content\\Paks"); // Sea of Thieves yield return GetSteamGame(1665460, "\\pak"); // eFootball 2023 From f261dd904364c50b5b822a9d02dce187dc40b262 Mon Sep 17 00:00:00 2001 From: 4sval Date: Wed, 29 Mar 2023 23:47:17 +0200 Subject: [PATCH 091/109] workaround #372 --- CUE4Parse | 2 +- FModel/MainWindow.xaml | 20 ++++++++++---------- FModel/Services/DiscordService.cs | 2 +- FModel/ViewModels/Commands/MenuCommand.cs | 12 ++++++------ FModel/Views/Resources/Resources.xaml | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index ba77931b..269bfa7a 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit ba77931bb2ff50610a05fbabb30180c80f8ab06b +Subproject commit 269bfa7afcc23021ee3667f2f98ec6a8cd354d2c diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index 3abdaba7..3db666be 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -318,15 +318,15 @@ - -