From fd7b1226b54d60c31aa36e4922e5fc9348d0df37 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 29 Nov 2022 21:58:42 +0100 Subject: [PATCH] merged options and cache + imgui lights + roughness parameter --- FModel/Resources/default.frag | 16 ++-- FModel/Views/Snooper/Cache.cs | 125 ----------------------------- FModel/Views/Snooper/Light.cs | 34 ++++++-- FModel/Views/Snooper/Material.cs | 45 ++++++----- FModel/Views/Snooper/Model.cs | 4 +- FModel/Views/Snooper/Options.cs | 123 ++++++++++++++++++++++++++-- FModel/Views/Snooper/PointLight.cs | 21 +++-- FModel/Views/Snooper/Renderer.cs | 63 +++++++-------- FModel/Views/Snooper/Shader.cs | 6 ++ FModel/Views/Snooper/SnimGui.cs | 61 +++++++------- FModel/Views/Snooper/Snooper.cs | 9 ++- FModel/Views/Snooper/SpotLight.cs | 27 +++++-- 12 files changed, 289 insertions(+), 245 deletions(-) delete mode 100644 FModel/Views/Snooper/Cache.cs diff --git a/FModel/Resources/default.frag b/FModel/Resources/default.frag index 8e3c84e5..90bdb2bf 100644 --- a/FModel/Resources/default.frag +++ b/FModel/Resources/default.frag @@ -42,8 +42,8 @@ struct Parameters Mask M; bool HasM; + float Roughness; float EmissiveMult; - float UVScale; }; @@ -52,7 +52,7 @@ struct Light { vec3 Position; float Intensity; - vec3 Direction; + vec2 Direction; float ConeAngle; float Attenuation; @@ -114,10 +114,10 @@ float geomSmith(float roughness, float dp) return dp / denom; } -vec3 CalcPBRLight(int layer, vec3 normals) +vec3 CalcCameraLight(int layer, vec3 normals) { vec3 specular_masks = SamplerToVector(uParameters.SpecularMasks[layer].Sampler).rgb; - float roughness = max(0.0f, specular_masks.b); + float roughness = max(0.0f, mix(specular_masks.r, specular_masks.b, uParameters.Roughness)); vec3 intensity = vec3(1.0f) * 1.0f; vec3 l = -uViewDir; @@ -192,21 +192,21 @@ void main() if (!bVertexColors[1]) { - result += CalcPBRLight(layer, normals); + result += CalcCameraLight(layer, normals); vec3 lights = vec3(uNumLights > 0 ? 0 : 1); for (int i = 0; i < uNumLights; i++) { - float distanceToLight = length(uLights[i].Position - fPos); - float attenuation = 0.0; + float distanceToLight = length(uLights[i].Position - fPos); + if (uLights[i].Type == 0) { attenuation = 1.0 / (1.0 + uLights[i].Linear * distanceToLight + uLights[i].Quadratic * pow(distanceToLight, 2)); } else if (uLights[i].Type == 1) { - float theta = dot(normalize(uLights[i].Position - fPos), normalize(-uLights[i].Direction)); + float theta = dot(normalize(uLights[i].Position - fPos), normalize(-vec3(uLights[i].Direction.x, -uLights[i].Attenuation, uLights[i].Direction.y))); if(theta > uLights[i].ConeAngle) { attenuation = 1.0 / (1.0 + uLights[i].Attenuation * pow(distanceToLight, 2)); diff --git a/FModel/Views/Snooper/Cache.cs b/FModel/Views/Snooper/Cache.cs deleted file mode 100644 index c7e0bbeb..00000000 --- a/FModel/Views/Snooper/Cache.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using CUE4Parse_Conversion.Meshes; -using CUE4Parse_Conversion.Textures; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Settings; - -namespace FModel.Views.Snooper; - -public class Cache : IDisposable -{ - public readonly Dictionary Models; - public readonly Dictionary Textures; - public readonly List Lights; - - public readonly Dictionary Icons; - - private ETexturePlatform _platform; - private readonly FGame _game; - - public Cache() - { - Models = new Dictionary(); - Textures = new Dictionary(); - Lights = new List(); - - Icons = new Dictionary - { - ["material"] = new ("materialicon"), - ["noimage"] = new ("T_Placeholder_Item_Image"), - ["pointlight"] = new ("pointlight"), - ["spotlight"] = new ("spotlight"), - }; - - _platform = UserSettings.Default.OverridedPlatform; - _game = Services.ApplicationService.ApplicationView.CUE4Parse.Game; - } - - public bool TryGetCachedModel(UStaticMesh o, out Model model) - { - var guid = o.LightingGuid; - if (!Models.TryGetValue(guid, out model) && o.TryConvert(out var mesh)) - { - model = new Model(o.Name, o.ExportType, o.Materials, mesh); - Models[guid] = model; - } - return model != null; - } - - public bool TryGetCachedTexture(UTexture2D o, bool fix, out Texture texture) - { - var guid = o.LightingGuid; - if (!Textures.TryGetValue(guid, out texture) && o.GetFirstMip() is { } mip) - { - TextureDecoder.DecodeTexture(mip, o.Format, o.isNormalMap, _platform, out var data, out _); - // if (fix) FixChannels(o, mip, ref data); - - texture = new Texture(data, mip.SizeX, mip.SizeY, o); - Textures[guid] = texture; - } - return texture != null; - } - - /// - /// Red : Specular - /// Blue : Roughness - /// Green : Metallic - /// - private void FixChannels(UTexture2D o, FTexture2DMipMap mip, ref byte[] data) - { - // only if it makes a big difference pls - } - - public void Setup() - { - foreach (var model in Models.Values) - { - if (model.IsSetup) continue; - model.Setup(this); - } - - foreach (var light in Lights) - { - light.Setup(); - } - } - - public void DisposeModels() - { - foreach (var model in Models.Values) - { - model.Dispose(); - } - } - public void DisposeTextures() - { - foreach (var texture in Textures.Values) - { - texture.Dispose(); - } - - foreach (var texture in Icons.Values) - { - texture.Dispose(); - } - } - - public void Reset() - { - DisposeModels(); - Models.Clear(); - Lights.Clear(); - } - - public void Dispose() - { - Reset(); - - DisposeTextures(); - Textures.Clear(); - Icons.Clear(); - } -} diff --git a/FModel/Views/Snooper/Light.cs b/FModel/Views/Snooper/Light.cs index 4b3ce79e..446b62e2 100644 --- a/FModel/Views/Snooper/Light.cs +++ b/FModel/Views/Snooper/Light.cs @@ -2,6 +2,8 @@ using System; using System.Numerics; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.Core.Misc; +using ImGuiNET; using OpenTK.Graphics.OpenGL4; namespace FModel.Views.Snooper; @@ -24,13 +26,15 @@ public abstract class Light : IDisposable 1f, 1f, 0f, 1f, -1f, 0 }; - public Texture Icon; + public readonly FGuid Model; + public readonly Texture Icon; public readonly Transform Transform; - public readonly Vector4 Color; - public readonly float Intensity; + public Vector4 Color; + public float Intensity; + public bool IsSetup; - public Light(Texture icon, UObject parent, UObject light, FVector position) + public Light(FGuid model, Texture icon, UObject parent, UObject light, FVector position) { var p = light.GetOrDefault("RelativeLocation", parent.GetOrDefault("RelativeLocation", FVector.ZeroVector)); var r = light.GetOrDefault("RelativeRotation", parent.GetOrDefault("RelativeRotation", FRotator.ZeroRotator)); @@ -39,6 +43,7 @@ public abstract class Light : IDisposable Transform.Scale = new FVector(0.25f); Transform.Position = position + r.RotateVector(p.ToMapVector()) * Constants.SCALE_DOWN_RATIO; + Model = model; Icon = icon; Color = light.GetOrDefault("LightColor", parent.GetOrDefault("LightColor", new FColor(0xFF, 0xFF, 0xFF, 0xFF))); @@ -62,13 +67,12 @@ public abstract class Light : IDisposable _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position SetupInstances(); - } - public abstract void Render(int i, Shader shader); + IsSetup = true; + } public void Render(Shader shader) { - // GL.Disable(EnableCap.DepthTest); _vao.Bind(); Icon?.Bind(TextureUnit.Texture0); @@ -76,7 +80,21 @@ public abstract class Light : IDisposable shader.SetUniform("uColor", Color); GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length); - // GL.Enable(EnableCap.DepthTest); + } + + public virtual void Render(int i, Shader shader) + { + shader.SetUniform($"uLights[{i}].Color", Color); + shader.SetUniform($"uLights[{i}].Position", Transform.Position); + shader.SetUniform($"uLights[{i}].Intensity", Intensity); + } + + public virtual void ImGuiLight() + { + SnimGui.Layout("Color");ImGui.PushID(1); + ImGui.ColorEdit4("", ref Color, ImGuiColorEditFlags.NoAlpha); + ImGui.PopID();SnimGui.Layout("Intensity");ImGui.PushID(2); + ImGui.DragFloat("", ref Intensity, 0.1f);ImGui.PopID(); } public void Dispose() diff --git a/FModel/Views/Snooper/Material.cs b/FModel/Views/Snooper/Material.cs index 06a2ae50..35487c8f 100644 --- a/FModel/Views/Snooper/Material.cs +++ b/FModel/Views/Snooper/Material.cs @@ -31,6 +31,7 @@ public class Material : IDisposable public Mask M; public bool HasM; + public float Roughness = 0.5f; public float EmissiveMult = 1f; public float UVScale = 1f; @@ -60,7 +61,7 @@ public class Material : IDisposable unrealMaterial.GetParams(Parameters); } - public void Setup(Cache cache, int numTexCoords) + public void Setup(Options options, int numTexCoords) { _handle = GL.CreateProgram(); @@ -76,10 +77,10 @@ public class Material : IDisposable else { { // textures - Diffuse = FillTextures(cache, numTexCoords, Parameters.HasTopDiffuse, CMaterialParams2.Diffuse, CMaterialParams2.FallbackDiffuse, true); - Normals = FillTextures(cache, numTexCoords, Parameters.HasTopNormals, CMaterialParams2.Normals, CMaterialParams2.FallbackNormals); - SpecularMasks = FillTextures(cache, numTexCoords, Parameters.HasTopSpecularMasks, CMaterialParams2.SpecularMasks, CMaterialParams2.FallbackSpecularMasks); - Emissive = FillTextures(cache, numTexCoords, true, CMaterialParams2.Emissive, CMaterialParams2.FallbackEmissive); + Diffuse = FillTextures(options, numTexCoords, Parameters.HasTopDiffuse, CMaterialParams2.Diffuse, CMaterialParams2.FallbackDiffuse, true); + Normals = FillTextures(options, numTexCoords, Parameters.HasTopNormals, CMaterialParams2.Normals, CMaterialParams2.FallbackNormals); + SpecularMasks = FillTextures(options, numTexCoords, Parameters.HasTopSpecularMasks, CMaterialParams2.SpecularMasks, CMaterialParams2.FallbackSpecularMasks); + Emissive = FillTextures(options, numTexCoords, true, CMaterialParams2.Emissive, CMaterialParams2.FallbackEmissive); } { // colors @@ -88,7 +89,7 @@ public class Material : IDisposable } { // scalars - if (Parameters.TryGetTexture2d(out var original, "M", "AEM") && cache.TryGetCachedTexture(original, false, out var transformed)) + if (Parameters.TryGetTexture2d(out var original, "M", "AEM") && options.TryGetTexture(original, false, out var transformed)) { M = new Mask { Texture = transformed, AmbientOcclusion = 0.7f }; HasM = true; @@ -96,6 +97,12 @@ public class Material : IDisposable M.SkinBoost = new Boost { Color = new Vector3(l.R, l.G, l.B), Exponent = l.A }; } + if (Parameters.TryGetScalar(out var roughnessMin, "RoughnessMin", "SpecRoughnessMin") && + Parameters.TryGetScalar(out var roughnessMax, "RoughnessMax", "SpecRoughnessMax")) + Roughness = (roughnessMin + roughnessMax) / 2f; + if (Parameters.TryGetScalar(out var roughness, "Rough", "Roughness")) + Roughness = roughness; + if (Parameters.TryGetScalar(out var emissiveMult, "emissive mult", "Emissive_Mult")) EmissiveMult = emissiveMult; @@ -105,13 +112,13 @@ public class Material : IDisposable } } - /// just the cache object + /// just the cache object /// number of item in the array /// has at least 1 clearly defined texture, else will go straight to fallback /// list of texture parameter names by uv channel /// fallback texture name to use if no top texture found /// if no top texture, no fallback texture, then use the first texture found - private Texture[] FillTextures(Cache cache, int numTexCoords, bool top, IReadOnlyList triggers, string fallback, bool first = false) + private Texture[] FillTextures(Options options, int numTexCoords, bool top, IReadOnlyList triggers, string fallback, bool first = false) { UTexture2D original; Texture transformed; @@ -122,18 +129,18 @@ public class Material : IDisposable { for (int i = 0; i < textures.Length; i++) { - if (Parameters.TryGetTexture2d(out original, triggers[i]) && cache.TryGetCachedTexture(original, fix, out transformed)) + if (Parameters.TryGetTexture2d(out original, triggers[i]) && options.TryGetTexture(original, fix, out transformed)) textures[i] = transformed; else if (i > 0 && textures[i - 1] != null) textures[i] = textures[i - 1]; } } - else if (Parameters.TryGetTexture2d(out original, fallback) && cache.TryGetCachedTexture(original, fix, out transformed)) + else if (Parameters.TryGetTexture2d(out original, fallback) && options.TryGetTexture(original, fix, out transformed)) { for (int i = 0; i < textures.Length; i++) textures[i] = transformed; } - else if (first && Parameters.TryGetFirstTexture2d(out original) && cache.TryGetCachedTexture(original, fix, out transformed)) + else if (first && Parameters.TryGetFirstTexture2d(out original) && options.TryGetTexture(original, fix, out transformed)) { for (int i = 0; i < textures.Length; i++) textures[i] = transformed; @@ -198,8 +205,8 @@ public class Material : IDisposable shader.SetUniform("uParameters.M.Cavity", M.Cavity); shader.SetUniform("uParameters.HasM", HasM); + shader.SetUniform("uParameters.Roughness", Roughness); shader.SetUniform("uParameters.EmissiveMult", EmissiveMult); - shader.SetUniform("uParameters.UVScale", UVScale); } @@ -219,21 +226,23 @@ public class Material : IDisposable PushStyle(); if (ImGui.BeginTable("parameters", 2)) { - SnimGui.Layout("Emissive Multiplier");ImGui.PushID(1); + SnimGui.Layout("Roughness");ImGui.PushID(1); + ImGui.DragFloat("", ref Roughness, _step, _zero, 1.0f, _mult, _clamp); + SnimGui.Layout("Emissive Multiplier");ImGui.PushID(2); ImGui.DragFloat("", ref EmissiveMult, _step, _zero, _infinite, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("UV Scale");ImGui.PushID(2); + ImGui.PopID();SnimGui.Layout("UV Scale");ImGui.PushID(3); ImGui.DragFloat("", ref UVScale, _step, _zero, _infinite, _mult, _clamp); ImGui.PopID(); if (HasM) { - SnimGui.Layout("Ambient Occlusion");ImGui.PushID(3); + SnimGui.Layout("Ambient Occlusion");ImGui.PushID(4); ImGui.DragFloat("", ref M.AmbientOcclusion, _step, _zero, 1.0f, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Cavity");ImGui.PushID(4); + ImGui.PopID();SnimGui.Layout("Cavity");ImGui.PushID(5); ImGui.DragFloat("", ref M.Cavity, _step, _zero, 1.0f, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Skin Boost Exponent");ImGui.PushID(5); + ImGui.PopID();SnimGui.Layout("Skin Boost Exponent");ImGui.PushID(6); ImGui.DragFloat("", ref M.SkinBoost.Exponent, _step, _zero, _infinite, _mult, _clamp); - ImGui.PopID();SnimGui.Layout("Skin Boost Color");ImGui.PushID(6); + ImGui.PopID();SnimGui.Layout("Skin Boost Color");ImGui.PushID(7); ImGui.ColorEdit3("", ref M.SkinBoost.Color); ImGui.PopID(); } diff --git a/FModel/Views/Snooper/Model.cs b/FModel/Views/Snooper/Model.cs index 5e3b0ef0..2e10c94c 100644 --- a/FModel/Views/Snooper/Model.cs +++ b/FModel/Views/Snooper/Model.cs @@ -192,7 +192,7 @@ public class Model : IDisposable _vao.BindInstancing(); // VertexAttributePointer } - public void Setup(Cache cache) + public void Setup(Options options) { _handle = GL.CreateProgram(); @@ -216,7 +216,7 @@ public class Model : IDisposable for (var i = 0; i < Materials.Length; i++) { if (!Materials[i].IsUsed) continue; - Materials[i].Setup(cache, NumTexCoords); + Materials[i].Setup(options, NumTexCoords); } if (HasMorphTargets) diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index 8445be2b..0a271cff 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using CUE4Parse_Conversion.Textures; +using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Misc; +using FModel.Settings; namespace FModel.Views.Snooper; @@ -9,21 +13,65 @@ public class Options public int SelectedSection { get; private set; } public int SelectedMorph { get; private set; } + public readonly Dictionary Models; + public readonly Dictionary Textures; + public readonly List Lights; + + public readonly Dictionary Icons; + + private ETexturePlatform _platform; + private readonly FGame _game; + public Options() { - Reset(); + Models = new Dictionary(); + Textures = new Dictionary(); + Lights = new List(); + + Icons = new Dictionary + { + ["material"] = new ("materialicon"), + ["noimage"] = new ("T_Placeholder_Item_Image"), + ["pointlight"] = new ("pointlight"), + ["spotlight"] = new ("spotlight"), + }; + + _platform = UserSettings.Default.OverridedPlatform; + _game = Services.ApplicationService.ApplicationView.CUE4Parse.Game; + + SelectModel(Guid.Empty); } - public void Reset() + public void SetupModelsAndLights() { - SelectedModel = Guid.Empty; - SelectedSection = 0; - SelectedMorph = 0; + foreach (var model in Models.Values) + { + if (model.IsSetup) continue; + model.Setup(this); + } + + foreach (var light in Lights) + { + if (light.IsSetup) continue; + light.Setup(); + } } public void SelectModel(FGuid guid) { - SelectedModel = guid; + // unselect old + if (TryGetModel(out var model)) + model.IsSelected = false; + + // select new + if (!TryGetModel(guid, out model)) + SelectedModel = Guid.Empty; + else + { + model.IsSelected = true; + SelectedModel = guid; + } + SelectedSection = 0; SelectedMorph = 0; } @@ -39,6 +87,44 @@ public class Options model.UpdateMorph(SelectedMorph); } + public bool TryGetTexture(UTexture2D o, bool fix, out Texture texture) + { + var guid = o.LightingGuid; + if (!Textures.TryGetValue(guid, out texture) && o.GetFirstMip() is { } mip) + { + TextureDecoder.DecodeTexture(mip, o.Format, o.isNormalMap, _platform, out var data, out _); + // if (fix) FixChannels(o, mip, ref data); + + texture = new Texture(data, mip.SizeX, mip.SizeY, o); + Textures[guid] = texture; + } + return texture != null; + } + + /// + /// Red : Specular + /// Blue : Roughness + /// Green : Metallic + /// + private void FixChannels(UTexture2D o, FTexture2DMipMap mip, ref byte[] data) + { + // only if it makes a big difference pls + } + + public bool TryGetModel(out Model model) => Models.TryGetValue(SelectedModel, out model); + public bool TryGetModel(FGuid guid, out Model model) => Models.TryGetValue(guid, out model); + + public bool TryGetSection(out Section section) => TryGetSection(SelectedModel, out section); + public bool TryGetSection(FGuid guid, out Section section) + { + if (TryGetModel(guid, out var model)) + { + return TryGetSection(model, out section); + } + + section = null; + return false; + } public bool TryGetSection(Model model, out Section section) { if (SelectedSection >= 0 && SelectedSection < model.Sections.Length) @@ -55,4 +141,29 @@ public class Options { Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value; } + + public void ResetModelsAndLights() + { + foreach (var model in Models.Values) + { + model.Dispose(); + } + Models.Clear(); + Lights.Clear(); + } + + public void Dispose() + { + ResetModelsAndLights(); + foreach (var texture in Textures.Values) + { + texture.Dispose(); + } + Textures.Clear(); + foreach (var texture in Icons.Values) + { + texture.Dispose(); + } + Icons.Clear(); + } } diff --git a/FModel/Views/Snooper/PointLight.cs b/FModel/Views/Snooper/PointLight.cs index dcde2a7e..1ccc2b25 100644 --- a/FModel/Views/Snooper/PointLight.cs +++ b/FModel/Views/Snooper/PointLight.cs @@ -1,15 +1,17 @@ using System; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.Core.Misc; +using ImGuiNET; namespace FModel.Views.Snooper; public class PointLight : Light { - public readonly float Linear; - public readonly float Quadratic; + public float Linear; + public float Quadratic; - public PointLight(Texture icon, UObject parent, UObject point, FVector position) : base(icon, parent, point, position) + public PointLight(FGuid model, Texture icon, UObject parent, UObject point, FVector position) : base(model, icon, parent, point, position) { if (!point.TryGetValue(out float radius, "AttenuationRadius", "SourceRadius")) radius = 1.0f; @@ -21,12 +23,19 @@ public class PointLight : Light public override void Render(int i, Shader shader) { - shader.SetUniform($"uLights[{i}].Color", Color); - shader.SetUniform($"uLights[{i}].Position", Transform.Position); - shader.SetUniform($"uLights[{i}].Intensity", Intensity); + base.Render(i, shader); shader.SetUniform($"uLights[{i}].Linear", Linear); shader.SetUniform($"uLights[{i}].Quadratic", Quadratic); shader.SetUniform($"uLights[{i}].Type", 0); } + + public override void ImGuiLight() + { + base.ImGuiLight(); + SnimGui.Layout("Linear");ImGui.PushID(3); + ImGui.DragFloat("", ref Linear, 0.1f); + ImGui.PopID();SnimGui.Layout("Quadratic");ImGui.PushID(4); + ImGui.DragFloat("", ref Quadratic, 0.1f);ImGui.PopID(); + } } diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index a3370e7a..1b6cfde0 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -31,8 +31,7 @@ public class Renderer : IDisposable public int VertexColor; public PickingTexture Picking { get; } - public Cache Cache { get; } - public Options Settings { get; } + public Options Options { get; } public Renderer(int width, int height) { @@ -40,8 +39,7 @@ public class Renderer : IDisposable _grid = new Grid(); Picking = new PickingTexture(width, height); - Cache = new Cache(); - Settings = new Options(); + Options = new Options(); ShowSkybox = UserSettings.Default.ShowSkybox; ShowGrid = UserSettings.Default.ShowGrid; @@ -63,22 +61,21 @@ public class Renderer : IDisposable public void Swap(UMaterialInstance unrealMaterial) { - if (!Cache.Models.TryGetValue(Settings.SelectedModel, out var model) || - !Settings.TryGetSection(model, out var section)) return; + if (!Options.TryGetModel(out var model) || !Options.TryGetSection(model, out var section)) return; model.Materials[section.MaterialIndex].SwapMaterial(unrealMaterial); - Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(Cache, model.NumTexCoords)); - Settings.SwapMaterial(false); + Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(Options, model.NumTexCoords)); + Options.SwapMaterial(false); } public void Animate(UAnimSequence animSequence) { - if (!Cache.Models.TryGetValue(Settings.SelectedModel, out var model) || !model.Skeleton.IsLoaded || + if (!Options.TryGetModel(out var model) || !model.Skeleton.IsLoaded || model.Skeleton?.RefSkel.ConvertAnims(animSequence) is not { } anim || anim.Sequences.Count == 0) return; model.Skeleton.Anim = new Animation(anim); - Settings.AnimateMesh(false); + Options.AnimateMesh(false); } public void Setup() @@ -91,7 +88,7 @@ public class Renderer : IDisposable _light = new Shader("light"); Picking.Setup(); - Cache.Setup(); + Options.SetupModelsAndLights(); } public void Render(Camera cam) @@ -107,34 +104,34 @@ public class Renderer : IDisposable _shader.SetUniform($"bVertexColors[{i}]", i == VertexColor); // render model pass - foreach (var model in Cache.Models.Values) + foreach (var model in Options.Models.Values) { if (!model.Show) continue; model.Render(_shader); } { // light pass - var uNumLights = Cache.Lights.Count; + var uNumLights = Math.Min(Options.Lights.Count, 100); _shader.SetUniform("uNumLights", ShowLights ? uNumLights : 0); if (ShowLights) for (int i = 0; i < uNumLights; i++) - Cache.Lights[i].Render(i, _shader); + Options.Lights[i].Render(i, _shader); _light.Render(viewMatrix, projMatrix); for (int i = 0; i < uNumLights; i++) - Cache.Lights[i].Render(_light); + Options.Lights[i].Render(_light); } // outline pass - if (Cache.Models.TryGetValue(Settings.SelectedModel, out var selected) && selected.Show) + if (Options.TryGetModel(out var selected) && selected.Show) { _outline.Render(viewMatrix, cam.Position, projMatrix); selected.Outline(_outline); } // picking pass (dedicated FBO, binding to 0 afterward) - Picking.Render(viewMatrix, projMatrix, Cache.Models); + Picking.Render(viewMatrix, projMatrix, Options.Models); } private Camera SetupCamera(FBox box) @@ -150,7 +147,7 @@ public class Renderer : IDisposable private Camera LoadStaticMesh(UStaticMesh original) { var guid = original.LightingGuid; - if (Cache.Models.TryGetValue(guid, out var model)) + if (Options.TryGetModel(guid, out var model)) { model.AddInstance(Transform.Identity); Application.Current.Dispatcher.Invoke(() => model.SetupInstances()); @@ -160,28 +157,28 @@ public class Renderer : IDisposable if (!original.TryConvert(out var mesh)) return null; - Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, mesh); - Settings.SelectModel(guid); + Options.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, mesh); + Options.SelectModel(guid); return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); } private Camera LoadSkeletalMesh(USkeletalMesh original) { var guid = Guid.NewGuid(); - if (Cache.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return null; + if (Options.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return null; - Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, original.Skeleton, original.MorphTargets, mesh); - Settings.SelectModel(guid); + Options.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, original.Skeleton, original.MorphTargets, mesh); + Options.SelectModel(guid); return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO); } private Camera LoadMaterialInstance(UMaterialInstance original) { var guid = Guid.NewGuid(); - if (Cache.Models.ContainsKey(guid)) return null; + if (Options.Models.ContainsKey(guid)) return null; - Cache.Models[guid] = new Cube(original); - Settings.SelectModel(guid); + Options.Models[guid] = new Cube(original); + Options.SelectModel(guid); return SetupCamera(new FBox(new FVector(-.65f), new FVector(.65f))); } @@ -254,7 +251,7 @@ public class Renderer : IDisposable Scale = staticMeshComp.GetOrDefault("RelativeScale3D", FVector.OneVector).ToMapVector() }; - if (Cache.Models.TryGetValue(guid, out var model)) + if (Options.TryGetModel(guid, out var model)) { model.AddInstance(t); } @@ -295,18 +292,18 @@ public class Renderer : IDisposable model.Materials[model.Sections[j].MaterialIndex].SwapMaterial(unrealMaterial); } } - Cache.Models[guid] = model; + Options.Models[guid] = model; } - if (actor.TryGetValue(out FPackageIndex treasureLight, "TreasureLight", "PointLight") && - treasureLight.TryLoad(out var tl1) && tl1.Template.TryLoad(out var tl2)) + if (actor.TryGetValue(out FPackageIndex treasureLight, "PointLight", "TreasureLight") && + treasureLight.TryLoad(out var pl1) && pl1.Template.TryLoad(out var pl2)) { - Cache.Lights.Add(new PointLight(Cache.Icons["pointlight"], tl1, tl2, t.Position)); + Options.Lights.Add(new PointLight(guid, Options.Icons["pointlight"], pl1, pl2, t.Position)); } if (actor.TryGetValue(out FPackageIndex spotLight, "SpotLight") && spotLight.TryLoad(out var sl1) && sl1.Template.TryLoad(out var sl2)) { - Cache.Lights.Add(new SpotLight(Cache.Icons["spotlight"], sl1, sl2, t.Position)); + Options.Lights.Add(new SpotLight(guid, Options.Icons["spotlight"], sl1, sl2, t.Position)); } } @@ -344,6 +341,6 @@ public class Renderer : IDisposable _outline?.Dispose(); _light?.Dispose(); Picking?.Dispose(); - Cache?.Dispose(); + Options?.Dispose(); } } diff --git a/FModel/Views/Snooper/Shader.cs b/FModel/Views/Snooper/Shader.cs index 54c25eb6..ece9ce27 100644 --- a/FModel/Views/Snooper/Shader.cs +++ b/FModel/Views/Snooper/Shader.cs @@ -96,6 +96,12 @@ public class Shader : IDisposable GL.Uniform1(GetUniformLocation(name), value); } + public void SetUniform(string name, Vector2 value) => SetUniform3(name, value.X, value.Y); + public void SetUniform3(string name, float x, float y) + { + GL.Uniform2(GetUniformLocation(name), x, y); + } + public void SetUniform(string name, Vector3 value) => SetUniform3(name, value.X, value.Y, value.Z); public void SetUniform3(string name, float x, float y, float z) { diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs index 2229018d..bfdc7eca 100644 --- a/FModel/Views/Snooper/SnimGui.cs +++ b/FModel/Views/Snooper/SnimGui.cs @@ -82,7 +82,17 @@ public class SnimGui ImGui.SetNextItemOpen(true, ImGuiCond.Appearing); if (ImGui.CollapsingHeader("Lights")) { - + for (int i = 0; i < s.Renderer.Options.Lights.Count; i++) + { + var id = $"[{i}] {s.Renderer.Options.Models[s.Renderer.Options.Lights[i].Model].Name}"; + if (ImGui.TreeNode(id) && ImGui.BeginTable(id, 2)) + { + s.Renderer.Options.SelectModel(s.Renderer.Options.Lights[i].Model); + s.Renderer.Options.Lights[i].ImGuiLight(); + ImGui.EndTable(); + ImGui.TreePop(); + } + } } } @@ -143,7 +153,7 @@ public class SnimGui ImGui.TableHeadersRow(); var i = 0; - foreach ((FGuid guid, Model model) in s.Renderer.Cache.Models) + foreach ((FGuid guid, Model model) in s.Renderer.Options.Models) { ImGui.PushID(i); ImGui.TableNextRow(); @@ -157,14 +167,13 @@ public class SnimGui ImGui.TableNextColumn(); ImGui.Text(model.NumTexCoords.ToString("D")); ImGui.TableNextColumn(); - model.IsSelected = s.Renderer.Settings.SelectedModel == guid; - if (ImGui.Selectable(model.Name, model.IsSelected, ImGuiSelectableFlags.SpanAllColumns)) + if (ImGui.Selectable(model.Name, s.Renderer.Options.SelectedModel == guid, ImGuiSelectableFlags.SpanAllColumns)) { - s.Renderer.Settings.SelectModel(guid); + s.Renderer.Options.SelectModel(guid); } if (ImGui.BeginPopupContextItem()) { - s.Renderer.Settings.SelectModel(guid); + s.Renderer.Options.SelectModel(guid); if (ImGui.MenuItem("Show", null, model.Show)) model.Show = !model.Show; if (ImGui.MenuItem("Wireframe", null, model.Wireframe)) model.Wireframe = !model.Wireframe; ImGui.Separator(); @@ -175,12 +184,12 @@ public class SnimGui ImGui.BeginDisabled(!model.HasSkeleton); if (ImGui.Selectable("Animate")) { - s.Renderer.Settings.AnimateMesh(true); + s.Renderer.Options.AnimateMesh(true); s.WindowShouldClose(true, false); } ImGui.EndDisabled(); - if (ImGui.Selectable("Delete")) s.Renderer.Cache.Models.Remove(guid); - if (ImGui.Selectable("Deselect")) s.Renderer.Settings.SelectModel(Guid.Empty); + if (ImGui.Selectable("Delete")) s.Renderer.Options.Models.Remove(guid); + if (ImGui.Selectable("Deselect")) s.Renderer.Options.SelectModel(Guid.Empty); ImGui.Separator(); if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(model.Name); ImGui.EndPopup(); @@ -197,7 +206,7 @@ public class SnimGui private void DrawSockets(Snooper s) { - foreach (var model in s.Renderer.Cache.Models.Values) + foreach (var model in s.Renderer.Options.Models.Values) { if (!model.HasSkeleton || model.IsSelected) return; if (ImGui.TreeNode($"{model.Name} [{model.Skeleton.Sockets.Length}]")) @@ -210,14 +219,10 @@ public class SnimGui ImGui.Text($"P: {socket.Transform.Matrix.M41} | {socket.Transform.Matrix.M42} | {socket.Transform.Matrix.M43}"); // ImGui.Text($"R: {socket.Transform.Rotation}"); // ImGui.Text($"S: {socket.Transform.Scale}"); - if (ImGui.Button("Attach")) + if (ImGui.Button("Attach") && s.Renderer.Options.TryGetModel(out var selected)) { - var guid = s.Renderer.Settings.SelectedModel; - if (s.Renderer.Cache.Models.TryGetValue(guid, out var selected)) - { - selected.Transforms[selected.SelectedInstance] = socket.Transform; - selected.UpdateMatrix(selected.SelectedInstance); - } + selected.Transforms[selected.SelectedInstance] = socket.Transform; + selected.UpdateMatrix(selected.SelectedInstance); } ImGui.PopID(); i++; @@ -233,7 +238,7 @@ public class SnimGui MeshWindow("Details", s.Renderer, (icons, model) => { ImGui.Text($"Entity: ({model.Type}) {model.Name}"); - ImGui.Text($"Guid: {s.Renderer.Settings.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); + ImGui.Text($"Guid: {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}"); ImGui.Spacing(); if (ImGui.Button("Go To")) { @@ -265,19 +270,19 @@ public class SnimGui ImGui.Text(section.MaterialIndex.ToString("D")); ImGui.TableNextColumn(); - if (ImGui.Selectable(material.Name, s.Renderer.Settings.SelectedSection == i, ImGuiSelectableFlags.SpanAllColumns)) + if (ImGui.Selectable(material.Name, s.Renderer.Options.SelectedSection == i, ImGuiSelectableFlags.SpanAllColumns)) { - s.Renderer.Settings.SelectSection(i); + s.Renderer.Options.SelectSection(i); } if (ImGui.BeginPopupContextItem()) { - s.Renderer.Settings.SelectSection(i); + s.Renderer.Options.SelectSection(i); if (ImGui.MenuItem("Show", null, section.Show)) section.Show = !section.Show; if (ImGui.Selectable("Swap")) { if (_swapAwareness) { - s.Renderer.Settings.SwapMaterial(true); + s.Renderer.Options.SwapMaterial(true); s.WindowShouldClose(true, false); } else swap = true; @@ -306,7 +311,7 @@ public class SnimGui if (ImGui.Button("OK", size)) { ImGui.CloseCurrentPopup(); - s.Renderer.Settings.SwapMaterial(true); + s.Renderer.Options.SwapMaterial(true); s.WindowShouldClose(true, false); } @@ -399,9 +404,9 @@ public class SnimGui for (int i = 0; i < model.Morphs.Length; i++) { ImGui.PushID(i); - if (ImGui.Selectable(model.Morphs[i].Name, s.Renderer.Settings.SelectedMorph == i)) + if (ImGui.Selectable(model.Morphs[i].Name, s.Renderer.Options.SelectedMorph == i)) { - s.Renderer.Settings.SelectMorph(i, model); + s.Renderer.Options.SelectMorph(i, model); } ImGui.PopID(); } @@ -491,7 +496,7 @@ public class SnimGui if (ImGui.IsMouseClicked(ImGuiMouseButton.Right)) { var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size); - s.Renderer.Settings.SelectModel(guid); + s.Renderer.Options.SelectModel(guid); ImGui.SetWindowFocus("Outliner"); } } @@ -534,7 +539,7 @@ public class SnimGui { Window(name, () => { - if (renderer.Cache.Models.TryGetValue(renderer.Settings.SelectedModel, out var model)) content(renderer.Cache.Icons, model); + if (renderer.Options.TryGetModel(out var model)) content(renderer.Options.Icons, model); else NoMeshSelected(); }, styled); } @@ -543,7 +548,7 @@ public class SnimGui { MeshWindow(name, renderer, (icons, model) => { - if (renderer.Settings.TryGetSection(model, out var section)) content(icons, model, section); + if (renderer.Options.TryGetSection(model, out var section)) content(icons, model, section); else NoSectionSelected(); }, styled); } diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index f12d852f..4a330e43 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -1,3 +1,4 @@ +using System; using System.ComponentModel; using System.Numerics; using System.Threading; @@ -41,7 +42,7 @@ public class Snooper : GameWindow Camera = newCamera; _previousSpeed = Camera.Speed; } - return Renderer.Cache.Models.Count > 0; + return Renderer.Options.Models.Count > 0; } public unsafe void WindowShouldClose(bool value, bool clear) @@ -49,8 +50,8 @@ public class Snooper : GameWindow if (clear) { _previousSpeed = 0f; - Renderer.Cache.Reset(); - Renderer.Settings.Reset(); + Renderer.Options.ResetModelsAndLights(); + Renderer.Options.SelectModel(Guid.Empty); Renderer.Save(); } @@ -71,7 +72,7 @@ public class Snooper : GameWindow { if (_init) { - Renderer.Cache.Setup(); + Renderer.Options.SetupModelsAndLights(); return; } diff --git a/FModel/Views/Snooper/SpotLight.cs b/FModel/Views/Snooper/SpotLight.cs index 498019c3..fae276b7 100644 --- a/FModel/Views/Snooper/SpotLight.cs +++ b/FModel/Views/Snooper/SpotLight.cs @@ -2,36 +2,49 @@ using System.Numerics; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.Core.Misc; +using ImGuiNET; namespace FModel.Views.Snooper; public class SpotLight : Light { - public Vector3 Direction; // ??? + public Vector2 Direction; // ??? public float Attenuation; public float ConeAngle; - public SpotLight(Texture icon, UObject parent, UObject spot, FVector position) : base(icon, parent, spot, position) + public SpotLight(FGuid model, Texture icon, UObject parent, UObject spot, FVector position) : base(model, icon, parent, spot, position) { if (!spot.TryGetValue(out Attenuation, "AttenuationRadius", "SourceRadius")) Attenuation = 1.0f; Attenuation *= Constants.SCALE_DOWN_RATIO; - Direction = Vector3.Zero; - Direction.Y -= Attenuation; + Direction = Vector2.Zero; ConeAngle = (spot.GetOrDefault("InnerConeAngle", 50.0f) + spot.GetOrDefault("OuterConeAngle", 60.0f)) / 2.0f; ConeAngle = MathF.Cos(Helper.DegreesToRadians(ConeAngle)); } public override void Render(int i, Shader shader) { - shader.SetUniform($"uLights[{i}].Color", Color); - shader.SetUniform($"uLights[{i}].Position", Transform.Position); - shader.SetUniform($"uLights[{i}].Intensity", Intensity); + base.Render(i, shader); shader.SetUniform($"uLights[{i}].Direction", Direction); shader.SetUniform($"uLights[{i}].Attenuation", Attenuation); shader.SetUniform($"uLights[{i}].ConeAngle", ConeAngle); shader.SetUniform($"uLights[{i}].Type", 1); } + + public override void ImGuiLight() + { + base.ImGuiLight(); + SnimGui.Layout("Direction");ImGui.PushID(3); + ImGui.DragFloat2("", ref Direction, 0.01f); + ImGui.PopID();SnimGui.Layout("Attenuation");ImGui.PushID(4); + ImGui.DragFloat("", ref Attenuation, 0.1f);ImGui.PopID(); + + var angle = Helper.RadiansToDegrees(MathF.Acos(ConeAngle)); + SnimGui.Layout("Cone Angle");ImGui.PushID(5); + ImGui.DragFloat("", ref angle, 0.1f, 0.0f, 90.0f, "%.1f°");ImGui.PopID(); + ConeAngle = MathF.Cos(Helper.DegreesToRadians(angle)); + } }