diff --git a/CUE4Parse b/CUE4Parse
index 2e3274d9..54a95bf8 160000
--- a/CUE4Parse
+++ b/CUE4Parse
@@ -1 +1 @@
-Subproject commit 2e3274d96eb02b4b376f9f44942da81593130d28
+Subproject commit 54a95bf82efd13cd9e4423f182296f0c0127ccaf
diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj
index 36d889d9..557f6aec 100644
--- a/FModel/FModel.csproj
+++ b/FModel/FModel.csproj
@@ -105,6 +105,8 @@
+
+
@@ -126,6 +128,8 @@
+
+
@@ -222,6 +226,8 @@
+
+
diff --git a/FModel/Resources/default.frag b/FModel/Resources/default.frag
index 2a392bb5..ae2968b0 100644
--- a/FModel/Resources/default.frag
+++ b/FModel/Resources/default.frag
@@ -2,6 +2,7 @@
#define PI 3.1415926535897932384626433832795
#define MAX_UV_COUNT 8
+#define MAX_LIGHT_COUNT 100
in vec3 fPos;
in vec3 fNormal;
@@ -46,7 +47,19 @@ struct Parameters
float UVScale;
};
+struct Light {
+ vec4 Color;
+ vec3 Position;
+ float Intensity;
+
+ float Constant;
+ float Linear;
+ float Quadratic;
+};
+
uniform Parameters uParameters;
+uniform Light uLights[MAX_LIGHT_COUNT];
+uniform int uNumLights;
uniform int uNumTexCoords;
uniform vec3 uViewPos;
uniform vec3 uViewDir;
@@ -175,6 +188,27 @@ void main()
if (!bVertexColors[1])
{
result += CalcPBRLight(layer, normals);
+
+ vec3 lights = vec3(uNumLights > 0 ? 0 : 1);
+ for (int i = 0; i < uNumLights; i++)
+ {
+ float distance = length(uLights[i].Position - fPos);
+ float attenuation = 1.0 / (1.0 + uLights[i].Linear * distance + uLights[i].Quadratic * (distance * distance));
+ vec3 intensity = uLights[i].Color.rgb * uLights[i].Intensity;
+ lights += result * intensity * attenuation;
+
+// float attenuation = 0.0;
+// float theta = dot(normalize(uLights[i].Position - fPos), normalize(-uLights[i].Direction));
+// if(theta > uLights[i].ConeAngle)
+// {
+// float distanceToLight = length(uLights[i].Position - fPos);
+// attenuation = 1.0 / (1.0 + uLights[i].Attenuation * pow(distanceToLight, 2));
+// }
+//
+// vec3 intensity = uLights[i].Color.rgb * uLights[i].Intensity;
+// lights += result * intensity * attenuation;
+ }
+ result *= lights; // use * to darken the scene, + to lighten it
}
result = result / (result + vec3(1.0f));
diff --git a/FModel/Resources/light.frag b/FModel/Resources/light.frag
new file mode 100644
index 00000000..26b943f2
--- /dev/null
+++ b/FModel/Resources/light.frag
@@ -0,0 +1,16 @@
+#version 330
+
+uniform sampler2D uIcon;
+uniform vec4 uColor;
+
+in vec2 fTexCoords;
+
+out vec4 FragColor;
+
+void main()
+{
+ vec4 color = uColor * texture(uIcon, fTexCoords);
+ if (color.a < 0.1) discard;
+
+ FragColor = uColor;
+}
diff --git a/FModel/Resources/light.vert b/FModel/Resources/light.vert
new file mode 100644
index 00000000..14516356
--- /dev/null
+++ b/FModel/Resources/light.vert
@@ -0,0 +1,15 @@
+#version 330 core
+
+layout (location = 0) in vec3 vPos;
+layout (location = 9) in mat4 vInstanceMatrix;
+
+uniform mat4 uView;
+uniform mat4 uProjection;
+
+out vec2 fTexCoords;
+
+void main()
+{
+ gl_Position = uProjection * uView * vInstanceMatrix * vec4(inverse(mat3(uView)) * vPos, 1.0);
+ fTexCoords = -vPos.xy;
+}
diff --git a/FModel/Resources/pointlight.png b/FModel/Resources/pointlight.png
new file mode 100644
index 00000000..7360e2b1
Binary files /dev/null and b/FModel/Resources/pointlight.png differ
diff --git a/FModel/Resources/spotlight.png b/FModel/Resources/spotlight.png
new file mode 100644
index 00000000..83b0e048
Binary files /dev/null and b/FModel/Resources/spotlight.png differ
diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml
index dd224932..dfae0bd7 100644
--- a/FModel/Views/SettingsView.xaml
+++ b/FModel/Views/SettingsView.xaml
@@ -372,7 +372,7 @@
IsChecked="{Binding SaveMaterials, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
-
+
diff --git a/FModel/Views/Snooper/Cache.cs b/FModel/Views/Snooper/Cache.cs
index a85f0603..c7e0bbeb 100644
--- a/FModel/Views/Snooper/Cache.cs
+++ b/FModel/Views/Snooper/Cache.cs
@@ -13,6 +13,8 @@ public class Cache : IDisposable
{
public readonly Dictionary Models;
public readonly Dictionary Textures;
+ public readonly List Lights;
+
public readonly Dictionary Icons;
private ETexturePlatform _platform;
@@ -22,12 +24,18 @@ public class Cache : IDisposable
{
Models = new Dictionary();
Textures = new Dictionary();
- Icons = 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;
-
- Icons["material"] = new Texture("materialicon");
- Icons["noimage"] = new Texture("T_Placeholder_Item_Image");
}
public bool TryGetCachedModel(UStaticMesh o, out Model model)
@@ -72,6 +80,11 @@ public class Cache : IDisposable
if (model.IsSetup) continue;
model.Setup(this);
}
+
+ foreach (var light in Lights)
+ {
+ light.Setup();
+ }
}
public void DisposeModels()
@@ -94,10 +107,16 @@ public class Cache : IDisposable
}
}
- public void Dispose()
+ public void Reset()
{
DisposeModels();
Models.Clear();
+ Lights.Clear();
+ }
+
+ public void Dispose()
+ {
+ Reset();
DisposeTextures();
Textures.Clear();
diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs
index 0afd2de4..0f783ce0 100644
--- a/FModel/Views/Snooper/Camera.cs
+++ b/FModel/Views/Snooper/Camera.cs
@@ -129,7 +129,7 @@ public class Camera
if (ImGui.BeginTable("camera_editor", 2))
{
SnimGui.Layout("Speed");ImGui.PushID(1);
- ImGui.DragFloat("", ref Speed, _step, _zero, _infinite, "%.2f s/m", _clamp);
+ ImGui.DragFloat("", ref Speed, _step, _zero, _infinite, "%.2f m/s", _clamp);
ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(2);
ImGui.DragFloat("", ref Far, 0.1f, 0.1f, Far * 2f, "%.2f m", _clamp);
ImGui.PopID();
diff --git a/FModel/Views/Snooper/Light.cs b/FModel/Views/Snooper/Light.cs
new file mode 100644
index 00000000..1ab6209d
--- /dev/null
+++ b/FModel/Views/Snooper/Light.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Numerics;
+using CUE4Parse.UE4.Assets.Exports;
+using CUE4Parse.UE4.Objects.Core.Math;
+using OpenTK.Graphics.OpenGL4;
+
+namespace FModel.Views.Snooper;
+
+public abstract class Light : IDisposable
+{
+ private int _handle;
+
+ private BufferObject _ebo;
+ private BufferObject _vbo;
+ private BufferObject _matrixVbo;
+ private VertexArrayObject _vao;
+
+ public readonly uint[] Indices = { 0, 1, 2, 3, 4, 5 };
+ public readonly float[] Vertices = {
+ 1f, 1f, 0f,
+ -1f, -1f, 0f,
+ -1f, 1f, 0f,
+ -1f, -1f, 0f,
+ 1f, 1f, 0f,
+ 1f, -1f, 0
+ };
+ public Texture Icon;
+ public readonly Transform Transform;
+
+ public readonly Vector4 Color;
+ public readonly float Intensity;
+ public readonly float Linear;
+ public readonly float Quadratic;
+
+ public Light(Texture icon, UObject light, FVector position)
+ {
+ var p = light.GetOrDefault("RelativeLocation", FVector.ZeroVector);
+ var r = light.GetOrDefault("RelativeRotation", FRotator.ZeroRotator);
+
+ Transform = Transform.Identity;
+ Transform.Scale = new FVector(0.25f);
+ Transform.Position = position + r.RotateVector(p.ToMapVector()) * Constants.SCALE_DOWN_RATIO;
+
+ Icon = icon;
+
+ Color = light.GetOrDefault("LightColor", new FColor(0xFF, 0xFF, 0xFF, 0xFF));
+ Intensity = light.GetOrDefault("Intensity", 1.0f);
+
+ var radius = light.GetOrDefault("AttenuationRadius", 0.0f) * Constants.SCALE_DOWN_RATIO;
+ Linear = 4.5f / radius;
+ Quadratic = 75.0f / MathF.Pow(radius, 2);
+ }
+
+ public void SetupInstances()
+ {
+ var instanceMatrix = new [] {Transform.Matrix};
+ _matrixVbo = new BufferObject(instanceMatrix, BufferTarget.ArrayBuffer);
+ _vao.BindInstancing(); // VertexAttributePointer
+ }
+
+ public void Setup()
+ {
+ _handle = GL.CreateProgram();
+
+ _ebo = new BufferObject(Indices, BufferTarget.ElementArrayBuffer);
+ _vbo = new BufferObject(Vertices, BufferTarget.ArrayBuffer);
+ _vao = new VertexArrayObject(_vbo, _ebo);
+
+ _vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position
+ SetupInstances();
+ }
+
+ public abstract void Render(int i, Shader shader);
+
+ public void Render(Shader shader)
+ {
+ // GL.Disable(EnableCap.DepthTest);
+ _vao.Bind();
+
+ Icon?.Bind(TextureUnit.Texture0);
+ shader.SetUniform("uIcon", 0);
+ shader.SetUniform("uColor", Color);
+
+ GL.DrawArrays(PrimitiveType.Triangles, 0, Indices.Length);
+ // GL.Enable(EnableCap.DepthTest);
+ }
+
+ public void Dispose()
+ {
+ _ebo?.Dispose();
+ _vbo?.Dispose();
+ _vao?.Dispose();
+ GL.DeleteProgram(_handle);
+ }
+}
diff --git a/FModel/Views/Snooper/Material.cs b/FModel/Views/Snooper/Material.cs
index c7650b6d..06a2ae50 100644
--- a/FModel/Views/Snooper/Material.cs
+++ b/FModel/Views/Snooper/Material.cs
@@ -83,7 +83,7 @@ public class Material : IDisposable
}
{ // colors
- DiffuseColor = FillColors(numTexCoords, Diffuse, CMaterialParams2.DiffuseColors, new Vector4(0.5f));
+ DiffuseColor = FillColors(numTexCoords, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One);
EmissiveColor = FillColors(numTexCoords, Emissive, CMaterialParams2.EmissiveColors, Vector4.One);
}
@@ -208,10 +208,15 @@ public class Material : IDisposable
private const float _zero = 0.000001f; // doesn't actually work if _infinite is used as max value /shrug
private const float _infinite = 0.0f;
private const ImGuiSliderFlags _clamp = ImGuiSliderFlags.AlwaysClamp;
- public void ImGuiParameters()
+ private void PushStyle()
{
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3));
ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1));
+ }
+
+ public void ImGuiParameters()
+ {
+ PushStyle();
if (ImGui.BeginTable("parameters", 2))
{
SnimGui.Layout("Emissive Multiplier");ImGui.PushID(1);
@@ -253,7 +258,40 @@ public class Material : IDisposable
}
}
- public IntPtr? GetSelectedTexture()
+ public void ImGuiTextures(Dictionary icons, Model model)
+ {
+ PushStyle();
+ if (ImGui.BeginTable("material_textures", 2))
+ {
+ SnimGui.Layout("Channel");ImGui.PushID(1); ImGui.BeginDisabled(model.NumTexCoords < 2);
+ ImGui.DragInt("", ref SelectedChannel, _step, 0, model.NumTexCoords - 1, "UV %i", ImGuiSliderFlags.AlwaysClamp);
+ ImGui.EndDisabled();ImGui.PopID();SnimGui.Layout("Type");ImGui.PushID(2);
+ ImGui.Combo("texture_type", ref SelectedTexture, "Diffuse\0Normals\0Specular\0Ambient Occlusion\0Emissive\0");
+ ImGui.PopID();
+
+ switch (SelectedTexture)
+ {
+ case 0:
+ SnimGui.Layout("Color");ImGui.PushID(3);
+ ImGui.ColorEdit4("", ref DiffuseColor[SelectedChannel], ImGuiColorEditFlags.NoAlpha);
+ ImGui.PopID();
+ break;
+ case 4:
+ SnimGui.Layout("Color");ImGui.PushID(3);
+ ImGui.ColorEdit4("", ref EmissiveColor[SelectedChannel], ImGuiColorEditFlags.NoAlpha);
+ ImGui.PopID();
+ break;
+ }
+
+ ImGui.EndTable();
+ }
+ ImGui.PopStyleVar(2);
+
+ ImGui.Image(GetSelectedTexture() ?? icons["noimage"].GetPointer(), new Vector2(ImGui.GetContentRegionAvail().X), Vector2.Zero, Vector2.One, Vector4.One, new Vector4(1.0f, 1.0f, 1.0f, 0.25f));
+ ImGui.Spacing();
+ }
+
+ private IntPtr? GetSelectedTexture()
{
return SelectedTexture switch
{
diff --git a/FModel/Views/Snooper/PointLight.cs b/FModel/Views/Snooper/PointLight.cs
new file mode 100644
index 00000000..bad35c25
--- /dev/null
+++ b/FModel/Views/Snooper/PointLight.cs
@@ -0,0 +1,21 @@
+using CUE4Parse.UE4.Assets.Exports;
+using CUE4Parse.UE4.Objects.Core.Math;
+
+namespace FModel.Views.Snooper;
+
+public class PointLight : Light
+{
+ public PointLight(Texture icon, UObject point, FVector position) : base(icon, point, position)
+ {
+
+ }
+
+ 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);
+ shader.SetUniform($"uLights[{i}].Linear", Linear);
+ shader.SetUniform($"uLights[{i}].Quadratic", Quadratic);
+ }
+}
diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs
index 5c66ca55..79e97c86 100644
--- a/FModel/Views/Snooper/Renderer.cs
+++ b/FModel/Views/Snooper/Renderer.cs
@@ -24,9 +24,11 @@ public class Renderer : IDisposable
private readonly Grid _grid;
private Shader _shader;
private Shader _outline;
+ private Shader _light;
public bool ShowSkybox;
public bool ShowGrid;
+ public bool ShowLights;
public int VertexColor;
public PickingTexture Picking { get; }
@@ -49,6 +51,7 @@ public class Renderer : IDisposable
public Camera Load(CancellationToken cancellationToken, UObject export)
{
+ ShowLights = false;
return export switch
{
UStaticMesh st => LoadStaticMesh(st),
@@ -86,6 +89,7 @@ public class Renderer : IDisposable
_shader = new Shader();
_outline = new Shader("outline");
+ _light = new Shader("light");
Picking.Setup();
Cache.Setup();
@@ -101,17 +105,28 @@ public class Renderer : IDisposable
_shader.Render(viewMatrix, cam.Position, cam.Direction, projMatrix);
for (int i = 0; i < 6; i++)
- {
_shader.SetUniform($"bVertexColors[{i}]", i == VertexColor);
- }
- // render pass
+ // render model pass
foreach (var model in Cache.Models.Values)
{
if (!model.Show) continue;
model.Render(_shader);
}
+ { // light pass
+ var uNumLights = Cache.Lights.Count;
+ _shader.SetUniform("uNumLights", ShowLights ? uNumLights : 0);
+
+ if (ShowLights)
+ for (int i = 0; i < uNumLights; i++)
+ Cache.Lights[i].Render(i, _shader);
+
+ _light.Render(viewMatrix, projMatrix);
+ for (int i = 0; i < uNumLights; i++)
+ Cache.Lights[i].Render(_light);
+ }
+
// outline pass
if (Cache.Models.TryGetValue(Settings.SelectedModel, out var selected) && selected.Show)
{
@@ -188,6 +203,7 @@ public class Renderer : IDisposable
Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {i}/{length}");
WorldCamera(actor, ref cam);
+ // WorldLight(actor);
WorldMesh(actor, transform);
AdditionalWorlds(actor, transform.Matrix, cancellationToken);
}
@@ -209,6 +225,14 @@ public class Renderer : IDisposable
0.01f, far * 25f, Math.Max(5f, far / 10f));
}
+ private void WorldLight(UObject actor)
+ {
+ if (!actor.TryGetValue(out FPackageIndex lightComponent, "LightComponent") ||
+ lightComponent.Load() is not { } lightObject) return;
+
+ Cache.Lights.Add(new PointLight(Cache.Icons["pointlight"], lightObject, FVector.ZeroVector));
+ }
+
private void WorldMesh(UObject actor, Transform transform)
{
if (!actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent", "Mesh") ||
@@ -274,6 +298,17 @@ public class Renderer : IDisposable
}
Cache.Models[guid] = model;
}
+
+ if (actor.TryGetValue(out FPackageIndex treasureLight, "TreasureLight", "PointLight") &&
+ treasureLight.TryLoad(out var tl) && tl.Template.TryLoad(out tl))
+ {
+ Cache.Lights.Add(new PointLight(Cache.Icons["pointlight"], tl, t.Position));
+ }
+ if (actor.TryGetValue(out FPackageIndex spotLight, "SpotLight") &&
+ spotLight.TryLoad(out var sl) && sl.Template.TryLoad(out sl))
+ {
+ Cache.Lights.Add(new SpotLight(Cache.Icons["spotlight"], sl, t.Position));
+ }
}
private void AdditionalWorlds(UObject actor, Matrix4x4 relation, CancellationToken cancellationToken)
@@ -308,6 +343,7 @@ public class Renderer : IDisposable
_grid?.Dispose();
_shader?.Dispose();
_outline?.Dispose();
+ _light?.Dispose();
Picking?.Dispose();
Cache?.Dispose();
}
diff --git a/FModel/Views/Snooper/SnimGui.cs b/FModel/Views/Snooper/SnimGui.cs
index b32eafa1..4a41a3c5 100644
--- a/FModel/Views/Snooper/SnimGui.cs
+++ b/FModel/Views/Snooper/SnimGui.cs
@@ -63,7 +63,9 @@ public class SnimGui
ImGui.Checkbox("", ref s.Renderer.ShowSkybox);
ImGui.PopID();Layout("Grid");ImGui.PushID(2);
ImGui.Checkbox("", ref s.Renderer.ShowGrid);
- ImGui.PopID();Layout("Vertex Colors");ImGui.PushID(3);
+ ImGui.PopID();Layout("Lights");ImGui.PushID(3);
+ 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");
ImGui.PopID();
@@ -441,22 +443,7 @@ public class SnimGui
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.CollapsingHeader("Textures"))
{
- ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3));
- ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1));
- if (ImGui.BeginTable("material_textures", 2))
- {
- Layout("Channel");ImGui.PushID(1); ImGui.BeginDisabled(model.NumTexCoords < 2);
- ImGui.DragInt("", ref material.SelectedChannel, 1, 0, model.NumTexCoords - 1, "UV %i", ImGuiSliderFlags.AlwaysClamp);
- ImGui.EndDisabled();ImGui.PopID();Layout("Type");ImGui.PushID(2);
- ImGui.Combo("texture_type", ref material.SelectedTexture,
- "Diffuse\0Normals\0Specular\0Ambient Occlusion\0Emissive\0");
- ImGui.PopID();
- ImGui.EndTable();
- }
- ImGui.PopStyleVar(2);
-
- ImGui.Image(material.GetSelectedTexture() ?? icons["noimage"].GetPointer(), new Vector2(ImGui.GetContentRegionAvail().X), Vector2.Zero, Vector2.One, Vector4.One, new Vector4(1.0f, 1.0f, 1.0f, 0.25f));
- ImGui.Spacing();
+ material.ImGuiTextures(icons, model);
}
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs
index 9d72b865..d5d5718f 100644
--- a/FModel/Views/Snooper/Snooper.cs
+++ b/FModel/Views/Snooper/Snooper.cs
@@ -49,8 +49,7 @@ public class Snooper : GameWindow
if (clear)
{
_previousSpeed = 0f;
- Renderer.Cache.DisposeModels();
- Renderer.Cache.Models.Clear();
+ Renderer.Cache.Reset();
Renderer.Settings.Reset();
Renderer.Save();
}
diff --git a/FModel/Views/Snooper/SpotLight.cs b/FModel/Views/Snooper/SpotLight.cs
new file mode 100644
index 00000000..8f1ad9ef
--- /dev/null
+++ b/FModel/Views/Snooper/SpotLight.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Numerics;
+using CUE4Parse.UE4.Assets.Exports;
+using CUE4Parse.UE4.Objects.Core.Math;
+
+namespace FModel.Views.Snooper;
+
+public class SpotLight : PointLight
+{
+ public Vector3 Direction; // ???
+ public float Attenuation;
+ public float ConeAngle;
+
+ public SpotLight(Texture icon, UObject spot, FVector position) : base(icon, spot, position)
+ {
+ // var p = spot.GetOrDefault("RelativeLocation", FVector.ZeroVector);
+ // var r = spot.GetOrDefault("RelativeRotation", FRotator.ZeroRotator);
+
+ // Direction = position + r.UnrotateVector(p.ToMapVector()) * Constants.SCALE_DOWN_RATIO;
+ Attenuation = spot.GetOrDefault("AttenuationRadius", 0.0f) * Constants.SCALE_DOWN_RATIO;
+ ConeAngle = (spot.GetOrDefault("InnerConeAngle", 50f) + spot.GetOrDefault("OuterConeAngle", 60f)) / 2f;
+ ConeAngle = MathF.Cos(Helper.DegreesToRadians(ConeAngle));
+ }
+
+ public new void Render(int i, Shader shader)
+ {
+ base.Render(i, shader);
+ // shader.SetUniform($"uLights[{i}].Direction", Direction);
+ // shader.SetUniform($"uLights[{i}].Attenuation", Attenuation);
+ // shader.SetUniform($"uLights[{i}].ConeAngle", ConeAngle);
+ }
+}
diff --git a/FModel/Views/Snooper/VertexArrayObject.cs b/FModel/Views/Snooper/VertexArrayObject.cs
index f2bc3b53..ef27c198 100644
--- a/FModel/Views/Snooper/VertexArrayObject.cs
+++ b/FModel/Views/Snooper/VertexArrayObject.cs
@@ -36,6 +36,11 @@ public class VertexArrayObject : IDisposable where TVer
GL.BindVertexArray(_handle);
}
+ public void Unbind()
+ {
+ GL.BindVertexArray(0);
+ }
+
public unsafe void BindInstancing()
{
Bind();
@@ -58,11 +63,6 @@ public class VertexArrayObject : IDisposable where TVer
Unbind();
}
- public void Unbind()
- {
- GL.BindVertexArray(0);
- }
-
public void Dispose()
{
GL.DeleteVertexArray(_handle);