picking texture, not pixel perfect but good enough

This commit is contained in:
4sval 2022-11-02 23:16:38 +01:00
parent a267c6233f
commit cc89becb4c
13 changed files with 279 additions and 52 deletions

@ -1 +1 @@
Subproject commit 0c633f3d3ad6f1eed9df9cee60917e1e169a0101
Subproject commit 00bc31fa4cc570efd3f9d54694feb16a8d21251c

View File

@ -103,6 +103,8 @@
<None Remove="Resources\framebuffer.vert" />
<None Remove="Resources\outline.frag" />
<None Remove="Resources\outline.vert" />
<None Remove="Resources\picking.frag" />
<None Remove="Resources\picking.vert" />
</ItemGroup>
<ItemGroup>
@ -122,6 +124,8 @@
<EmbeddedResource Include="Resources\framebuffer.vert" />
<EmbeddedResource Include="Resources\outline.frag" />
<EmbeddedResource Include="Resources\outline.vert" />
<EmbeddedResource Include="Resources\picking.frag" />
<EmbeddedResource Include="Resources\picking.vert" />
</ItemGroup>
<ItemGroup>

View File

@ -32,7 +32,7 @@ public class ImGuiController : IDisposable
private int _windowWidth;
private int _windowHeight;
private System.Numerics.Vector2 _scaleFactor = System.Numerics.Vector2.One;
private readonly System.Numerics.Vector2 _scaleFactor = System.Numerics.Vector2.One;
private static bool KHRDebugAvailable = false;

View File

@ -0,0 +1,13 @@
#version 330
uniform uint uA;
uniform uint uB;
uniform uint uC;
uniform uint uD;
out uvec4 FragColor;
void main()
{
FragColor = uvec4(uA, uB, uC, uD);
}

View File

@ -0,0 +1,15 @@
#version 330 core
layout (location = 1) in vec3 vPos;
layout (location = 8) in mat4 vInstanceMatrix;
layout (location = 12) in vec3 vMorphTarget;
uniform mat4 uView;
uniform mat4 uProjection;
uniform float uMorphTime;
void main()
{
vec3 pos = mix(vPos, vMorphTarget, uMorphTime);
gl_Position = uProjection * uView * vInstanceMatrix * vec4(pos, 1.0);
}

View File

@ -9,8 +9,8 @@ public class FramebufferObject : IDisposable
private int _framebufferHandle;
private int _postProcessingHandle;
private readonly int _width;
private readonly int _height;
private int _width;
private int _height;
private readonly RenderbufferObject _renderbuffer;
private BufferObject<uint> _ebo;
@ -43,7 +43,7 @@ public class FramebufferObject : IDisposable
public void Setup()
{
_framebufferHandle = GL.GenFramebuffer();
Bind(_framebufferHandle);
Bind();
_framebufferTexture = new Texture((uint) _width, (uint) _height);
@ -102,6 +102,17 @@ public class FramebufferObject : IDisposable
public IntPtr GetPointer() => _postProcessingTexture.GetPointer();
public void WindowResized(int width, int height)
{
_width = width;
_height = height;
_renderbuffer.WindowResized(width, height);
_framebufferTexture.WindowResized(width, height);
_postProcessingTexture.WindowResized(width, height);
}
public void Dispose()
{
_vao.Dispose();

View File

@ -266,6 +266,17 @@ public class Model : IDisposable
}
}
public void SimpleRender(Shader shader)
{
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
for (int section = 0; section < Sections.Length; section++)
{
Sections[section].Render(TransformsCount);
}
_vao.Unbind();
}
public void Outline(Shader shader)
{
GL.StencilMask(0x00);

View File

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using Vector2 = System.Numerics.Vector2;
namespace FModel.Views.Snooper;
public class PickingTexture : IDisposable
{
private int _width;
private int _height;
private int _framebufferHandle;
private Shader _shader;
private int _pickingTexture;
private int _depthTexture;
public PickingTexture(int width, int height)
{
_width = width;
_height = height;
}
public void Setup()
{
_framebufferHandle = GL.GenFramebuffer();
Bind();
_shader = new Shader("picking");
_pickingTexture = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, _pickingTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32ui, _width, _height, 0, PixelFormat.RgbaInteger, PixelType.UnsignedInt, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Nearest);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _pickingTexture, 0);
_depthTexture = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, _depthTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent, _width, _height, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, TextureTarget.Texture2D, _depthTexture, 0);
var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
if (status != FramebufferErrorCode.FramebufferComplete)
{
throw new Exception($"Framebuffer failed to bind with error: {GL.GetProgramInfoLog(_framebufferHandle)}");
}
GL.BindTexture(TextureTarget.Texture2D, 0);
Bind(0);
}
public void Render(Matrix4 viewMatrix, Matrix4 projMatrix, IDictionary<FGuid,Model> models)
{
Bind();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
_shader.Use();
_shader.SetUniform("uView", viewMatrix);
_shader.SetUniform("uProjection", projMatrix);
foreach ((FGuid guid, Model model) in models)
{
_shader.SetUniform("uA", guid.A);
_shader.SetUniform("uB", guid.B);
_shader.SetUniform("uC", guid.C);
_shader.SetUniform("uD", guid.D);
if (!model.Show) continue;
model.SimpleRender(_shader);
}
Bind(0);
}
public void Bind() => Bind(_framebufferHandle);
public void Bind(int handle)
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle);
}
public FGuid ReadPixel(Vector2 mousePos, Vector2 windowPos, Vector2 windowSize)
{
Bind();
FGuid pixel = default;
var scaleX = windowSize.X / _width;
var scaleY = windowSize.Y / _height;
var x = Convert.ToInt32((mousePos.X - windowPos.X) / scaleX);
var y = -Convert.ToInt32((mousePos.Y - windowPos.Y) / scaleY);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.ReadPixels(x, y, 1, 1, PixelFormat.RgbaInteger, PixelType.UnsignedInt, ref pixel);
GL.ReadBuffer(ReadBufferMode.None);
Bind(0);
return pixel;
}
public void WindowResized(int width, int height)
{
_width = width;
_height = height;
GL.BindTexture(TextureTarget.Texture2D, _pickingTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32ui, _width, _height, 0, PixelFormat.RgbaInteger, PixelType.UnsignedInt, IntPtr.Zero);
GL.BindTexture(TextureTarget.Texture2D, _depthTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent, _width, _height, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero);
}
public void Dispose()
{
_shader?.Dispose();
GL.DeleteTexture(_pickingTexture);
GL.DeleteTexture(_depthTexture);
GL.DeleteFramebuffer(_framebufferHandle);
}
}

View File

@ -7,8 +7,8 @@ public class RenderbufferObject : IDisposable
{
private int _handle;
private readonly int _width;
private readonly int _height;
private int _width;
private int _height;
public RenderbufferObject(int width, int height)
{
@ -25,6 +25,15 @@ public class RenderbufferObject : IDisposable
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, _handle);
}
public void WindowResized(int width, int height)
{
_width = width;
_height = height;
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _handle);
GL.RenderbufferStorageMultisample(RenderbufferTarget.Renderbuffer, Constants.SAMPLES_COUNT, RenderbufferStorage.Depth24Stencil8, _width, _height);
}
public void Dispose()
{
GL.DeleteRenderbuffer(_handle);

View File

@ -22,11 +22,13 @@ public class Renderer : IDisposable
private Vector3 _diffuseLight;
private Vector3 _specularLight;
public PickingTexture Picking { get; }
public Cache Cache { get; }
public Options Settings { get; }
public Renderer()
public Renderer(int width, int height)
{
Picking = new PickingTexture(width, height);
Cache = new Cache();
Settings = new Options();
}
@ -60,6 +62,7 @@ public class Renderer : IDisposable
_diffuseLight = new Vector3(0.75f);
_specularLight = new Vector3(0.5f);
Picking.Setup();
Cache.Setup();
}
@ -85,6 +88,8 @@ public class Renderer : IDisposable
Cache.Render(_shader);
GL.Enable(EnableCap.StencilTest);
Cache.Outline(_outline);
Picking.Render(viewMatrix, projMatrix, Cache.Models);
}
private Camera SetupCamera(FBox box)
@ -222,6 +227,7 @@ public class Renderer : IDisposable
{
_shader.Dispose();
_outline.Dispose();
Picking.Dispose();
Cache.Dispose();
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Windows;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Framework;
using ImGuiNET;
@ -449,15 +448,20 @@ public class SnimGui
_viewportFocus = true;
s.CursorState = CursorState.Grabbed;
}
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
{
var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size);
s.Renderer.Settings.SelectModel(guid);
}
}
// this can't be inside IsItemHovered! read it as
// if left mouse button was pressed while hovering the viewport
// move camera until left mouse button is released
// no matter where mouse position end up
var io = ImGui.GetIO();
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus)
{
var io = ImGui.GetIO();
var delta = io.MouseDelta * lookSensitivity;
s.Camera.ModifyDirection(delta.X, delta.Y);
}

View File

@ -13,7 +13,7 @@ namespace FModel.Views.Snooper;
public class Snooper : GameWindow
{
public Camera Camera;
public FramebufferObject Framebuffer;
public readonly FramebufferObject Framebuffer;
public readonly Renderer Renderer;
private readonly Skybox _skybox;
@ -26,8 +26,8 @@ public class Snooper : GameWindow
public Snooper(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings)
{
Framebuffer = new FramebufferObject(Size);
Renderer = new Renderer();
Framebuffer = new FramebufferObject(ClientSize);
Renderer = new Renderer(ClientSize.X, ClientSize.Y);
_skybox = new Skybox();
_grid = new Grid();
@ -87,9 +87,10 @@ public class Snooper : GameWindow
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
Framebuffer.Setup();
Renderer.Setup();
_skybox.Setup();
_grid.Setup();
Renderer.Setup();
_init = true;
}
@ -177,9 +178,10 @@ public class Snooper : GameWindow
GL.Viewport(0, 0, e.Width, e.Height);
Framebuffer = new FramebufferObject(e.Size);
Framebuffer.Setup();
Camera.AspectRatio = e.Width / (float) e.Height;
Framebuffer.WindowResized(e.Width, e.Height);
Renderer.Picking.WindowResized(e.Width, e.Height);
_gui.Controller.WindowResized(e.Width, e.Height);
}
@ -187,9 +189,11 @@ public class Snooper : GameWindow
{
base.Dispose(disposing);
Framebuffer?.Dispose();
Renderer?.Dispose();
_skybox?.Dispose();
_grid?.Dispose();
Renderer?.Dispose();
_gui?.Controller.Dispose();
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Windows;
using CUE4Parse.UE4.Assets.Exports.Texture;
using OpenTK.Graphics.OpenGL4;
@ -12,6 +12,7 @@ public class Texture : IDisposable
{
private readonly int _handle;
private readonly TextureType _type;
private readonly TextureTarget _target;
public readonly string Type;
public readonly string Name;
@ -19,14 +20,20 @@ public class Texture : IDisposable
public readonly EPixelFormat Format;
public readonly uint ImportedWidth;
public readonly uint ImportedHeight;
public readonly int Width;
public readonly int Height;
public int Width;
public int Height;
public string Label;
public Texture(TextureType type)
{
_handle = GL.GenTexture();
_type = type;
_target = _type switch
{
TextureType.Cubemap => TextureTarget.TextureCubeMap,
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
};
Label = "(?) Click to Copy Path";
}
@ -38,12 +45,12 @@ public class Texture : IDisposable
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
GL.TexParameter(TextureTarget.Texture2DMultisample, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2DMultisample, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2DMultisample, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2DMultisample, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2DMultisample, _handle, 0);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
}
public Texture(int width, int height) : this(TextureType.Framebuffer)
@ -52,14 +59,14 @@ public class Texture : IDisposable
Height = height;
Bind(TextureUnit.Texture0);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _handle, 0);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
}
public Texture(byte[] data, int width, int height, UTexture2D texture2D) : this(TextureType.Normal)
@ -74,11 +81,11 @@ public class Texture : IDisposable
Height = height;
Bind(TextureUnit.Texture0);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, 8);
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
}
@ -104,39 +111,34 @@ public class Texture : IDisposable
});
}
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
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);
}
public Texture(uint width, uint height, IntPtr data) : this(TextureType.Normal)
{
Width = (int) width;
Height = (int) height;
Bind(TextureTarget.Texture2D);
Bind(_target);
GL.TexStorage2D(TextureTarget2d.Texture2D, 1, SizedInternalFormat.Rgba8, Width, Height);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);
GL.TexSubImage2D(_target, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);
var repeat = (int) TextureWrapMode.Repeat;
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, ref repeat);
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, ref repeat);
GL.TexParameterI(_target, TextureParameterName.TextureWrapS, ref repeat);
GL.TexParameterI(_target, TextureParameterName.TextureWrapT, ref repeat);
var zero = 1 - 1;
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, ref zero);
GL.TexParameterI(_target, TextureParameterName.TextureMaxLevel, ref zero);
}
public void Bind(TextureUnit textureSlot)
{
GL.ActiveTexture(textureSlot);
Bind(_type switch
{
TextureType.Cubemap => TextureTarget.TextureCubeMap,
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
});
Bind(_target);
}
public void Bind(TextureTarget target)
@ -144,8 +146,34 @@ public class Texture : IDisposable
GL.BindTexture(target, _handle);
}
public void Bind()
{
GL.BindTexture(_target, _handle);
}
public IntPtr GetPointer() => (IntPtr) _handle;
public void WindowResized(int width, int height)
{
Width = width;
Height = height;
Bind();
switch (_type)
{
case TextureType.MsaaFramebuffer:
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
break;
case TextureType.Framebuffer:
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
break;
default:
throw new NotSupportedException();
}
}
public void Dispose()
{
GL.DeleteTexture(_handle);