Files
Voile/DaggerFramework/Source/Utils/ImGuiRenderLayer.cs
2023-09-13 15:34:10 +02:00

320 lines
11 KiB
C#

using System.Numerics;
using ImGuiNET;
using Raylib_cs;
using DaggerFramework.Rendering;
namespace DaggerFramework.SceneGraph
{
public class ImGuiRenderLayer : Layer
{
protected override void OnDraw(Renderer renderer)
{
Layout();
_controller.Draw(renderer);
}
protected virtual void Layout() { }
protected override void OnStart()
{
_controller = new ImGuiController();
_controller.Load(Scene.Renderer.WindowSize);
}
protected override void OnUpdate(double dt)
{
_controller.Update(dt, Input);
}
private ImGuiController _controller;
}
public class ImGuiController : IDisposable, IDrawable
{
public ImGuiController()
{
_context = ImGui.CreateContext();
ImGui.SetCurrentContext(_context);
}
public void Dispose()
{
ImGui.DestroyContext();
}
public void Load(Vector2 size)
{
ImGuiIOPtr io = ImGui.GetIO();
io.Fonts.AddFontDefault();
Resize(size);
LoadFontTexture();
SetupInput();
ImGui.NewFrame();
}
unsafe void LoadFontTexture()
{
ImGuiIOPtr io = ImGui.GetIO();
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height);
// TODO: use engine API instead of Raylib.
Image image = new Image
{
data = pixels,
width = width,
height = height,
mipmaps = 1,
format = PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
};
_fontTexture = Raylib.LoadTextureFromImage(image);
io.Fonts.SetTexID(new IntPtr(_fontTexture.id));
io.Fonts.ClearTexData();
}
private void SetupInput()
{
ImGuiIOPtr io = ImGui.GetIO();
// Setup config flags
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
// Setup back-end capabilities flags
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[(int)ImGuiKey.Tab] = (int)KeyboardKey.Tab;
io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)KeyboardKey.Left;
io.KeyMap[(int)ImGuiKey.RightArrow] = (int)KeyboardKey.Right;
io.KeyMap[(int)ImGuiKey.UpArrow] = (int)KeyboardKey.Up;
io.KeyMap[(int)ImGuiKey.DownArrow] = (int)KeyboardKey.Down;
io.KeyMap[(int)ImGuiKey.PageUp] = (int)KeyboardKey.PageUp;
io.KeyMap[(int)ImGuiKey.PageDown] = (int)KeyboardKey.PageDown;
io.KeyMap[(int)ImGuiKey.Home] = (int)KeyboardKey.Home;
io.KeyMap[(int)ImGuiKey.End] = (int)KeyboardKey.End;
io.KeyMap[(int)ImGuiKey.Insert] = (int)KeyboardKey.Insert;
io.KeyMap[(int)ImGuiKey.Delete] = (int)KeyboardKey.Delete;
io.KeyMap[(int)ImGuiKey.Backspace] = (int)KeyboardKey.Backspace;
io.KeyMap[(int)ImGuiKey.Space] = (int)KeyboardKey.Spacebar;
io.KeyMap[(int)ImGuiKey.Enter] = (int)KeyboardKey.Enter;
io.KeyMap[(int)ImGuiKey.Escape] = (int)KeyboardKey.Escape;
io.KeyMap[(int)ImGuiKey.A] = (int)KeyboardKey.A;
io.KeyMap[(int)ImGuiKey.C] = (int)KeyboardKey.C;
io.KeyMap[(int)ImGuiKey.V] = (int)KeyboardKey.V;
io.KeyMap[(int)ImGuiKey.X] = (int)KeyboardKey.X;
io.KeyMap[(int)ImGuiKey.Y] = (int)KeyboardKey.Y;
io.KeyMap[(int)ImGuiKey.Z] = (int)KeyboardKey.Z;
}
public void Resize(Vector2 size)
{
ImGuiIOPtr io = ImGui.GetIO();
io.DisplaySize = size / _scaleFactor;
}
public void Update(double dt, InputHandler input)
{
ImGuiIOPtr io = ImGui.GetIO();
io.DisplayFramebufferScale = Vector2.One;
io.DeltaTime = (float)dt;
UpdateKeyboard(input);
UpdateMouse(input);
ImGui.NewFrame();
}
private void UpdateKeyboard(InputHandler input)
{
ImGuiIOPtr io = ImGui.GetIO();
// Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[(int)KeyboardKey.LeftControl] || io.KeysDown[(int)KeyboardKey.RightControl];
io.KeyShift = io.KeysDown[(int)KeyboardKey.LeftShift] || io.KeysDown[(int)KeyboardKey.RightShift];
io.KeyAlt = io.KeysDown[(int)KeyboardKey.LeftAlt] || io.KeysDown[(int)KeyboardKey.RightAlt];
io.KeySuper = io.KeysDown[(int)KeyboardKey.LeftSuper] || io.KeysDown[(int)KeyboardKey.RightSuper];
// Key states
for (int i = (int)KeyboardKey.Spacebar; i < (int)KeyboardKey.KBMenu + 1; i++)
{
io.KeysDown[i] = input.IsKeyboardKeyDown((KeyboardKey)i);
}
// Key input
int keyPressed = input.GetCharPressed();
if (keyPressed != 0)
{
io.AddInputCharacter((uint)keyPressed);
}
}
private void UpdateMouse(InputHandler input)
{
ImGuiIOPtr io = ImGui.GetIO();
// Store button states
for (int i = 0; i < io.MouseDown.Count; i++)
{
io.MouseDown[i] = input.IsMouseButtonDown((MouseButton)i);
}
// Mouse scroll
io.MouseWheel += input.GetMouseWheelMovement();
// Mouse position
Vector2 mousePosition = io.MousePos;
// TODO:
// bool focused = Raylib.IsWindowFocused();
if (io.WantSetMousePos)
{
input.SetMousePosition(mousePosition);
}
else
{
io.MousePos = input.GetMousePosition();
}
// Mouse cursor state
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) == 0 || input.IsCursorHidden())
{
ImGuiMouseCursor cursor = ImGui.GetMouseCursor();
if (cursor == ImGuiMouseCursor.None || io.MouseDrawCursor)
{
input.HideCursor();
}
else
{
input.ShowCursor();
}
}
}
private void Render()
{
ImGui.Render();
}
private void RenderCommandLists(ImDrawDataPtr data)
{
// Scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fbWidth = (int)(data.DisplaySize.X * data.FramebufferScale.X);
int fbHeight = (int)(data.DisplaySize.Y * data.FramebufferScale.Y);
// Avoid rendering if display is minimized or if the command list is empty
if (fbWidth <= 0 || fbHeight <= 0 || data.CmdListsCount == 0)
{
return;
}
Rlgl.rlDrawRenderBatchActive();
Rlgl.rlDisableBackfaceCulling();
Rlgl.rlEnableScissorTest();
data.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
for (int n = 0; n < data.CmdListsCount; n++)
{
int idxOffset = 0;
ImDrawListPtr cmdList = data.CmdListsRange[n];
// Vertex buffer and index buffer generated by DearImGui
ImPtrVector<ImDrawVertPtr> vtxBuffer = cmdList.VtxBuffer;
ImVector<ushort> idxBuffer = cmdList.IdxBuffer;
for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++)
{
ImDrawCmdPtr pcmd = cmdList.CmdBuffer[cmdi];
// Scissor rect
Vector2 pos = data.DisplayPos;
int rectX = (int)((pcmd.ClipRect.X - pos.X) * data.FramebufferScale.X);
int rectY = (int)((pcmd.ClipRect.Y - pos.Y) * data.FramebufferScale.Y);
int rectW = (int)((pcmd.ClipRect.Z - rectX) * data.FramebufferScale.Y);
int rectH = (int)((pcmd.ClipRect.W - rectY) * data.FramebufferScale.Y);
Rlgl.rlScissor(rectX, Raylib.GetScreenHeight() - (rectY + rectH), rectW, rectH);
if (pcmd.UserCallback != IntPtr.Zero)
{
// pcmd.UserCallback(cmdList, pcmd);
idxOffset += (int)pcmd.ElemCount;
}
else
{
DrawTriangles(pcmd.ElemCount, idxOffset, (int)pcmd.VtxOffset, idxBuffer, vtxBuffer, pcmd.TextureId);
idxOffset += (int)pcmd.ElemCount;
Rlgl.rlDrawRenderBatchActive();
}
}
}
Rlgl.rlSetTexture(0);
Rlgl.rlDisableScissorTest();
Rlgl.rlEnableBackfaceCulling();
}
private Color GetColor(uint hexValue)
{
Color color = new Color();
color.R = (byte)(hexValue & 0xFF) / 255f;
color.G = (byte)((hexValue >> 8) & 0xFF) / 255f;
color.B = (byte)((hexValue >> 16) & 0xFF) / 255f;
color.A = (byte)((hexValue >> 24) & 0xFF) / 255f;
return color;
}
void DrawTriangleVertex(ImDrawVertPtr idxVert)
{
Color c = GetColor(idxVert.col);
Rlgl.rlColor4ub((byte)Math.Round(c.R * 255f), (byte)Math.Round(c.G * 255f), (byte)Math.Round(c.B * 255f), (byte)Math.Round(c.A * 255f));
Rlgl.rlTexCoord2f(idxVert.uv.X, idxVert.uv.Y);
Rlgl.rlVertex2f(idxVert.pos.X, idxVert.pos.Y);
}
// Draw the imgui triangle data
private void DrawTriangles(uint count, int idxOffset, int vtxOffset, ImVector<ushort> idxBuffer, ImPtrVector<ImDrawVertPtr> idxVert, IntPtr textureId)
{
ushort index = 0;
ImDrawVertPtr vertex;
if (Rlgl.rlCheckRenderBatchLimit((int)count * 3))
{
Rlgl.rlDrawRenderBatchActive();
}
Rlgl.rlBegin(DrawMode.TRIANGLES);
Rlgl.rlSetTexture((uint)textureId);
for (int i = 0; i <= (count - 3); i += 3)
{
index = idxBuffer[idxOffset + i];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
index = idxBuffer[idxOffset + i + 1];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
index = idxBuffer[idxOffset + i + 2];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
}
Rlgl.rlEnd();
}
public void Draw(Renderer renderer)
{
ImGui.Render();
RenderCommandLists(ImGui.GetDrawData());
}
private IntPtr _context;
private Texture2D _fontTexture;
private Vector2 _scaleFactor = Vector2.One;
}
}