Last active
November 3, 2021 12:26
-
-
Save snightshade/45f8033fd6f39cf41bb74182d55077dd to your computer and use it in GitHub Desktop.
Port of imgui_impl_opengl3.cpp to C# (.NET 5, OpenTK 4). Compatible as far back as OpenGL 3.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using OpenTK.Graphics.OpenGL; | |
namespace GLExperiments | |
{ | |
public class GLVertexArrayObject | |
{ | |
public int GLReference; | |
public GLVertexArrayObject() | |
{ | |
GLReference = GL.GenVertexArray(); | |
} | |
public void Bind() | |
{ | |
GL.BindVertexArray(GLReference); | |
} | |
} | |
public class GLBuffer<T> where T : struct | |
{ | |
public int GLReference; | |
public GLBuffer() | |
{ | |
GLReference = GL.GenBuffer(); | |
} | |
public void Bind(BufferTarget t) | |
{ | |
GL.BindBuffer(t, GLReference); | |
} | |
public void BindAndBuffer(BufferTarget t, T[] data, | |
BufferUsageHint hint = BufferUsageHint.DynamicDraw) | |
{ | |
Bind(t); | |
GL.BufferData<T>(t, data.Length, data, hint); | |
} | |
} | |
public class GLTexture | |
{ | |
public int GLReference; | |
public GLTexture() | |
{ | |
GLReference = GL.GenTexture(); | |
} | |
public void Bind(TextureTarget t) | |
{ | |
GL.BindTexture(t, GLReference); | |
} | |
public static void SetMinFilter(TextureTarget t, TextureMinFilter f) | |
{ | |
var fil = (int)f; | |
GL.TexParameterI(t, TextureParameterName.TextureMinFilter, ref fil); | |
} | |
public static void SetMagFilter(TextureTarget t, TextureMagFilter f) | |
{ | |
var fil = (int)f; | |
GL.TexParameterI(t, TextureParameterName.TextureMagFilter, ref fil); | |
} | |
} | |
public enum GLShaderType | |
{ | |
Fragment, | |
Vertex | |
} | |
public class GLShader | |
{ | |
public int GLReference; | |
public GLShader(GLShaderType type) | |
{ | |
var tkType = type switch | |
{ | |
GLShaderType.Fragment => ShaderType.FragmentShader, | |
GLShaderType.Vertex => ShaderType.VertexShader, | |
_ => throw new Exception("Bad shader type") | |
}; | |
GLReference = GL.CreateShader(tkType); | |
} | |
public void Compile(string source) | |
{ | |
GL.ShaderSource(GLReference, source); | |
GL.CompileShader(GLReference); | |
GL.GetShader(GLReference, ShaderParameter.CompileStatus, out int ok); | |
if (ok != 1) | |
{ | |
var log = GL.GetShaderInfoLog(GLReference); | |
Console.WriteLine("-- SHADER COMPILATION FAILURE --"); | |
Console.WriteLine(log.Trim()); | |
throw new Exception("Shader compilation failure! Check stdout for info!"); | |
} | |
} | |
} | |
public class GLShaderProgram | |
{ | |
public int GLReference; | |
public GLShaderProgram() | |
{ | |
GLReference = GL.CreateProgram(); | |
} | |
public GLShaderProgram Attach(GLShader shader) | |
{ | |
GL.AttachShader(GLReference, shader.GLReference); | |
return this; | |
} | |
public GLShaderProgram Link() | |
{ | |
GL.LinkProgram(GLReference); | |
GL.GetProgram(GLReference, GetProgramParameterName.LinkStatus, out int ok); | |
if (ok != 1) | |
{ | |
var a = GL.GetProgramInfoLog(GLReference); | |
Console.WriteLine("-- SHADER PROGRAM LINK FAILURE --"); | |
Console.WriteLine(a.Trim()); | |
throw new Exception("Shader program link failure! See stdout for info."); | |
} | |
return this; | |
} | |
public void Use() | |
{ | |
GL.UseProgram(GLReference); | |
} | |
public void Uniform4(string uniform, OpenTK.Mathematics.Vector4 vec4) | |
{ | |
var loc = GL.GetUniformLocation(GLReference, uniform); | |
GL.Uniform4(loc, vec4); | |
} | |
public void UniformMatrix4(string uniform, ref OpenTK.Mathematics.Matrix4 m) | |
{ | |
var loc = GL.GetUniformLocation(GLReference, uniform); | |
GL.UniformMatrix4(loc, false, ref m); | |
} | |
public void Uniform1i(string uniform, int val) | |
{ | |
var loc = GL.GetUniformLocation(GLReference, uniform); | |
GL.Uniform1(loc, val); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading.Tasks; | |
using ImGuiNET; | |
using OpenTK.Graphics.OpenGL; | |
using OpenTK.Mathematics; | |
// https://github.com/ocornut/imgui/blob/v1.74/examples/imgui_impl_opengl3.cpp | |
namespace GLExperiments | |
{ | |
public class ImplOpenGL3 | |
{ | |
public static GLShaderProgram Program; | |
public static GLVertexArrayObject VAO; | |
public static GLBuffer<ImDrawVert> VBO; | |
public static GLBuffer<ushort> EBO; | |
public static GLTexture FontTexture; | |
public const string VertexSource = @"#version 130 core | |
in vec2 position; | |
in vec2 uv; | |
in vec4 colour; | |
uniform mat4 Projection; | |
out vec2 frag_uv; | |
out vec4 frag_colour; | |
void main() | |
{ | |
frag_uv = uv; | |
frag_colour = colour; | |
gl_Position = Projection * vec4(position.xy, 0, 1); | |
}"; | |
public const string FragmentSource = @"#version 130 core | |
in vec2 frag_uv; | |
in vec4 frag_colour; | |
uniform sampler2D Texture; | |
out vec4 outColour; | |
void main() | |
{ | |
outColour = frag_colour * texture(Texture, frag_uv.xy); | |
}"; | |
public static unsafe void Init() | |
{ | |
var vert = new GLShader(GLShaderType.Vertex); | |
var frag = new GLShader(GLShaderType.Fragment); | |
vert.Compile(VertexSource); | |
frag.Compile(FragmentSource); | |
Program = new GLShaderProgram() | |
.Attach(vert) | |
.Attach(frag) | |
.Link(); | |
VAO = new GLVertexArrayObject(); | |
VBO = new GLBuffer<ImDrawVert>(); | |
EBO = new GLBuffer<ushort>(); | |
var io = ImGui.GetIO(); | |
var glVerMaj = GL.GetInteger(GetPName.MajorVersion); | |
var glVerMin = GL.GetInteger(GetPName.MinorVersion); | |
var verInt = (glVerMaj * 100) + (glVerMin * 10); | |
var vendor = GL.GetString(StringName.Vendor); | |
var csName = $"CSHARP-gl3 opengl/{verInt} vendor/{vendor}"; | |
var backendRendererName = Marshal.StringToHGlobalAnsi(csName); | |
(io.NativePtr)->BackendRendererName = (byte*)backendRendererName; | |
CreateFontTexture(); | |
} | |
public static unsafe void SetupRenderState(ImDrawDataPtr drawData, int fbWidth, int fbHeight) | |
{ | |
GL.Enable(EnableCap.Blend); | |
GL.BlendEquation(BlendEquationMode.FuncAdd); | |
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); | |
GL.Disable(EnableCap.CullFace); | |
GL.Disable(EnableCap.DepthTest); | |
GL.Enable(EnableCap.ScissorTest); | |
GL.Viewport(0, 0, fbWidth, fbHeight); | |
VAO.Bind(); | |
var io = ImGui.GetIO(); | |
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset; | |
var left = drawData.DisplayPos.X; | |
var right = drawData.DisplayPos.X + drawData.DisplaySize.X; | |
var top = drawData.DisplayPos.Y; | |
var bottom = drawData.DisplayPos.Y + drawData.DisplaySize.Y; | |
var matrix = Matrix4.CreateOrthographicOffCenter( | |
left, | |
right, | |
bottom, | |
top, | |
-1.0f, | |
1.0f); | |
Program.Use(); | |
Program.UniformMatrix4("Projection", ref matrix); | |
Program.Uniform1i("Texture", 0); | |
VBO.Bind(BufferTarget.ArrayBuffer); | |
EBO.Bind(BufferTarget.ElementArrayBuffer); | |
var attribLocationVtxPos = GL.GetAttribLocation(Program.GLReference, "position"); | |
var attribLocationVtxUv = GL.GetAttribLocation(Program.GLReference, "uv"); | |
var attribLocationVtxCol = GL.GetAttribLocation(Program.GLReference, "colour"); | |
GL.EnableVertexAttribArray(attribLocationVtxPos); | |
GL.EnableVertexAttribArray(attribLocationVtxUv); | |
GL.EnableVertexAttribArray(attribLocationVtxCol); | |
GL.VertexAttribPointer(attribLocationVtxPos, 2, VertexAttribPointerType.Float, | |
false, sizeof(ImDrawVert), Marshal.OffsetOf<ImDrawVert>("pos")); | |
GL.VertexAttribPointer(attribLocationVtxUv, 2, VertexAttribPointerType.Float, | |
false, sizeof(ImDrawVert), Marshal.OffsetOf<ImDrawVert>("uv")); | |
GL.VertexAttribPointer(attribLocationVtxCol, 4, VertexAttribPointerType.UnsignedByte, | |
true, sizeof(ImDrawVert), Marshal.OffsetOf<ImDrawVert>("col")); | |
} | |
public static unsafe void CreateFontTexture() | |
{ | |
var io = ImGui.GetIO(); | |
byte* pixels; | |
int tw, th; | |
io.Fonts.GetTexDataAsRGBA32(out pixels, out tw, out th); | |
FontTexture = new GLTexture(); | |
FontTexture.Bind(TextureTarget.Texture2D); | |
GLTexture.SetMinFilter(TextureTarget.Texture2D, TextureMinFilter.Linear); | |
GLTexture.SetMagFilter(TextureTarget.Texture2D, TextureMagFilter.Linear); | |
GL.TexImage2D(TextureTarget.Texture2D, | |
0, PixelInternalFormat.Rgba, | |
tw, th, 0, PixelFormat.Rgba, | |
PixelType.UnsignedByte, new IntPtr(pixels)); | |
io.Fonts.SetTexID(new IntPtr(FontTexture.GLReference)); | |
} | |
public static unsafe void Render() | |
{ | |
// DRAW IMGUI | |
var drawData = ImGui.GetDrawData(); | |
var fbWidth = (int)(drawData.DisplaySize.X * drawData.FramebufferScale.X); | |
var fbHeight = (int)(drawData.DisplaySize.Y * drawData.FramebufferScale.Y); | |
if (fbWidth <= 0 || fbHeight <= 0) | |
{ | |
return; | |
} | |
SetupRenderState(drawData, fbWidth, fbHeight); | |
var clipOff = drawData.DisplayPos; | |
var clipScale = drawData.FramebufferScale; | |
for (int n=0; n<drawData.CmdListsCount; n++) | |
{ | |
ImDrawListPtr cmdList = drawData.CmdListsRange[n]; | |
GL.BufferData(BufferTarget.ArrayBuffer, | |
cmdList.VtxBuffer.Size * sizeof(ImDrawVert), | |
cmdList.VtxBuffer.Data, | |
BufferUsageHint.StreamDraw); | |
GL.BufferData(BufferTarget.ElementArrayBuffer, | |
cmdList.IdxBuffer.Size * sizeof(ushort), | |
cmdList.IdxBuffer.Data, | |
BufferUsageHint.StreamDraw); | |
for (int i=0; i<cmdList.CmdBuffer.Size; i++) | |
{ | |
var pcmd = cmdList.CmdBuffer[i]; | |
var clipRect = new Vector4(); | |
clipRect.X = (pcmd.ClipRect.X - clipOff.X) * clipScale.X; | |
clipRect.Y = (pcmd.ClipRect.Y - clipOff.Y) * clipScale.Y; | |
clipRect.Z = (pcmd.ClipRect.Z - clipOff.X) * clipScale.X; | |
clipRect.W = (pcmd.ClipRect.W - clipOff.Y) * clipScale.Y; | |
if (clipRect.X < fbWidth && | |
clipRect.Y < fbHeight && | |
clipRect.Z >= 0.0f && | |
clipRect.W >= 0.0f) | |
{ | |
var scissorRect = new Vector4(); | |
scissorRect.X = clipRect.X; | |
scissorRect.Y = (fbHeight - clipRect.W); | |
scissorRect.Z = (clipRect.Z - clipRect.X); | |
scissorRect.W = (clipRect.W - clipRect.Y); | |
GL.Scissor((int)scissorRect.X, | |
(int)scissorRect.Y, | |
(int)scissorRect.Z, | |
(int)scissorRect.W); | |
GL.ActiveTexture(TextureUnit.Texture0); | |
GL.BindTexture(TextureTarget.Texture2D, | |
pcmd.TextureId.ToInt32()); | |
var t = (int)(pcmd.IdxOffset * sizeof(ushort)); | |
GL.DrawElementsBaseVertex(PrimitiveType.Triangles, | |
(int)pcmd.ElemCount, | |
DrawElementsType.UnsignedShort, | |
new IntPtr(t), | |
(int)pcmd.VtxOffset); | |
} | |
} | |
} | |
GL.Disable(EnableCap.ScissorTest); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment