Skip to content

Instantly share code, notes, and snippets.

@lostkagamine
Last active November 3, 2021 12:26
Show Gist options
  • Save lostkagamine/45f8033fd6f39cf41bb74182d55077dd to your computer and use it in GitHub Desktop.
Save lostkagamine/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.
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);
}
}
}
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