merged options and cache + imgui lights + roughness parameter

This commit is contained in:
4sval 2022-11-29 21:58:42 +01:00
parent 9926d0de7c
commit fd7b1226b5
12 changed files with 289 additions and 245 deletions

View File

@ -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));

View File

@ -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<FGuid, Model> Models;
public readonly Dictionary<FGuid, Texture> Textures;
public readonly List<Light> Lights;
public readonly Dictionary<string, Texture> Icons;
private ETexturePlatform _platform;
private readonly FGame _game;
public Cache()
{
Models = new Dictionary<FGuid, Model>();
Textures = new Dictionary<FGuid, Texture>();
Lights = new List<Light>();
Icons = new Dictionary<string, Texture>
{
["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;
}
/// <summary>
/// Red : Specular
/// Blue : Roughness
/// Green : Metallic
/// </summary>
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();
}
}

View File

@ -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()

View File

@ -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
}
}
/// <param name="cache">just the cache object</param>
/// <param name="options">just the cache object</param>
/// <param name="numTexCoords">number of item in the array</param>
/// <param name="top">has at least 1 clearly defined texture, else will go straight to fallback</param>
/// <param name="triggers">list of texture parameter names by uv channel</param>
/// <param name="fallback">fallback texture name to use if no top texture found</param>
/// <param name="first">if no top texture, no fallback texture, then use the first texture found</param>
private Texture[] FillTextures(Cache cache, int numTexCoords, bool top, IReadOnlyList<string[]> triggers, string fallback, bool first = false)
private Texture[] FillTextures(Options options, int numTexCoords, bool top, IReadOnlyList<string[]> 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();
}

View File

@ -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)

View File

@ -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<FGuid, Model> Models;
public readonly Dictionary<FGuid, Texture> Textures;
public readonly List<Light> Lights;
public readonly Dictionary<string, Texture> Icons;
private ETexturePlatform _platform;
private readonly FGame _game;
public Options()
{
Reset();
Models = new Dictionary<FGuid, Model>();
Textures = new Dictionary<FGuid, Texture>();
Lights = new List<Light>();
Icons = new Dictionary<string, Texture>
{
["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;
}
/// <summary>
/// Red : Specular
/// Blue : Roughness
/// Green : Metallic
/// </summary>
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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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));
}
}