330 lines
11 KiB
C#
330 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);
|
|
}
|
|
|
|
protected override void OnBeginDraw(Renderer renderer)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
protected override void OnEndDraw(Renderer renderer)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
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;
|
|
}
|
|
} |