Skip to content

Instantly share code, notes, and snippets.

@jose-villegas
Last active November 8, 2015 01:56
Show Gist options
  • Save jose-villegas/2a64944731720fffd78a to your computer and use it in GitHub Desktop.
Save jose-villegas/2a64944731720fffd78a to your computer and use it in GitHub Desktop.
#include "stdafx.h"
#include "interface.h"
#ifdef _WIN32
#undef APIENTRY
#define GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WGL
#include <GLFW/glfw3native.h>
#endif
static oglplus::Context gl;
double Interface::time = 0;
float Interface::mouseWheel = 0;
GLFWwindow * Interface::glfwWindow = nullptr;
bool Interface::mousePressed[3] = {false, false, false};
std::unique_ptr<Interface::DeviceObjects> Interface::deviceObjects = nullptr;
Interface::Interface()
{
}
Interface::~Interface()
{
}
void Interface::Initialize(const RenderWindow &activeWindow,
bool instantCallbacks /* = true */)
{
glfwWindow = activeWindow.Handler();
ImGuiIO &io = ImGui::GetIO();
// Keyboard mapping. ImGui will use those indices to peek
// into the io.KeyDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
io.RenderDrawListsFn = RenderDrawList;
io.SetClipboardTextFn = SetClipboardText;
io.GetClipboardTextFn = GetClipboardText;
#ifdef _WIN32
io.ImeWindowHandle = glfwGetWin32Window(glfwWindow);
#endif
if (instantCallbacks)
{
glfwSetMouseButtonCallback(glfwWindow, MouseButtonCallback);
glfwSetScrollCallback(glfwWindow, ScrollCallback);
glfwSetKeyCallback(glfwWindow, KeyCallback);
glfwSetCharCallback(glfwWindow, CharCallback);
}
}
void Interface::Render() const
{
ImGui::Render();
}
void Interface::NewFrame()
{
if (!deviceObjects)
{
CreateDeviceObjects();
}
auto &io = ImGui::GetIO();
// setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
glfwGetWindowSize(glfwWindow, &w, &h);
glfwGetFramebufferSize(glfwWindow, &display_w, &display_h);
io.DisplaySize = ImVec2(static_cast<float>(w), static_cast<float>(h));
io.DisplayFramebufferScale = ImVec2(static_cast<float>(display_w) / w,
static_cast<float>(display_h) / h);
// setup time step
auto current_time = glfwGetTime();
io.DeltaTime = time > 0.0
? static_cast<float>(current_time - time)
: static_cast<float>(1.0f / 60.0f);
time = current_time;
// setup inputs
// (we already got mouse wheel, keyboard keys
// & characters from glfw callbacks polled in glfwPollEvents())
if (glfwGetWindowAttrib(glfwWindow, GLFW_FOCUSED))
{
// Mouse position in screen coordinates
// (set to -1,-1 if no mouse / on another screen, etc.)
double mouse_x, mouse_y;
glfwGetCursorPos(glfwWindow, &mouse_x, &mouse_y);
io.MousePos = ImVec2(static_cast<float>(mouse_x),
static_cast<float>(mouse_y));
}
else
{
io.MousePos = ImVec2(-1, -1);
}
for (auto i = 0; i < 3; i++)
{
io.MouseDown[i] = mousePressed[i] || glfwGetMouseButton(glfwWindow, i)
!= 0;
// If a mouse press event came, always pass it as
// "this frame", so we don't miss click-release events
// that are shorter than 1 frame.
mousePressed[i] = false;
}
io.MouseWheel = mouseWheel;
mouseWheel = 0.0f;
// Hide OS mouse cursor if ImGui is drawing it
glfwSetInputMode(glfwWindow, GLFW_CURSOR, io.MouseDrawCursor
? GLFW_CURSOR_HIDDEN
: GLFW_CURSOR_NORMAL);
// Start the frame
ImGui::NewFrame();
}
void Interface::Terminate()
{
delete deviceObjects.release();
ImGui::GetIO().Fonts->TexID = nullptr;
ImGui::Shutdown();
}
void Interface::InvalidateDeviceObjects()
{
}
void Interface::CreateFontsTexture()
{
if (!deviceObjects)
{
return;
}
using namespace oglplus;
// get io component
auto &io = ImGui::GetIO();
// build texture atlas
unsigned char * pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// create a texture
// bind and pass data
gl.Bound(TextureTarget::_2D, deviceObjects->atlasFontTex)
.MinFilter(TextureMinFilter::Linear)
.MagFilter(TextureMagFilter::Linear)
.Image2D(0, PixelDataInternalFormat::RGBA, width, height, 0,
PixelDataFormat::RGBA, PixelDataType::UnsignedByte, pixels);
// store identifier
io.Fonts->TexID = reinterpret_cast<void *>(static_cast<intptr_t>
(GetName(deviceObjects->atlasFontTex)));
// clean up
io.Fonts->ClearInputData();
io.Fonts->ClearTexData();
}
void Interface::CreateDeviceObjects()
{
if (!deviceObjects)
{
deviceObjects = std::make_unique<DeviceObjects>
(std::move(DeviceObjects()));
}
using namespace oglplus;
// backup previous gl state
auto prevTexture = Texture::Binding(TextureTarget::_2D);
auto prevArrayBuffer = Buffer::Binding(BufferTarget::Array);
auto prevVertexArray = VertexArray::Binding();
const std::string vertexShaderSource =
"#version 330\n"
"uniform mat4 ProjMtx;\n"
"layout(location = 0) in vec2 Position;\n"
"layout(location = 1) in vec2 UV;\n"
"layout(location = 2) in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const std::string fragmentShaderSource =
"#version 330\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
"}\n";
// set shader sources
deviceObjects->vertexShader.Source(vertexShaderSource);
deviceObjects->fragmentShader.Source(fragmentShaderSource);
// compile shader sources
deviceObjects->vertexShader.Compile();
deviceObjects->fragmentShader.Compile();
// create shader program and attach shader sources
deviceObjects->shaderProgram
.AttachShader(deviceObjects->vertexShader);
deviceObjects->shaderProgram
.AttachShader(deviceObjects->fragmentShader);
// finally link the program
deviceObjects->shaderProgram.Link();
// extract uniforms
deviceObjects->texture.Assign(deviceObjects->shaderProgram)
.BindTo("Texture");
deviceObjects->projectionMatrix.Assign(deviceObjects->shaderProgram)
.BindTo("ProjMtx");
// generate vertex and element buffer
deviceObjects->vao.Bind();
deviceObjects->buffers[1].Bind(BufferTarget::ElementArray);
deviceObjects->buffers[0].Bind(BufferTarget::Array);
// enable vertex attribs
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
VertexArrayAttrib(0).Enable()
.Pointer(2, DataType::Float, false, sizeof(ImDrawVert),
reinterpret_cast<GLvoid *>(OFFSETOF(ImDrawVert, pos)));
VertexArrayAttrib(1).Enable()
.Pointer(2, DataType::Float, false, sizeof(ImDrawVert),
reinterpret_cast<GLvoid *>(OFFSETOF(ImDrawVert, uv)));
VertexArrayAttrib(2).Enable()
.Pointer(4, DataType::UnsignedByte, true, sizeof(ImDrawVert),
reinterpret_cast<GLvoid *>(OFFSETOF(ImDrawVert, col)));
#undef OFFSETOF
// create fonts atlas texture
CreateFontsTexture();
// recover previous gl state
Texture::Bind(TextureTarget::_2D, prevTexture);
Buffer::Bind(BufferTarget::Array, prevArrayBuffer);
VertexArray::Bind(prevVertexArray);
}
void Interface::RenderDrawList(ImDrawData * drawData)
{
using namespace oglplus;
// backup bound objects
auto prevElementBuffer = Buffer::Binding(BufferTarget::ElementArray);
auto prevArrayBuffer = Buffer::Binding(BufferTarget::Array);
auto prevTexture = Texture::Binding(TextureTarget::_2D);
auto prevVertexArray = VertexArray::Binding();
auto prevProgram = Program::Binding();
// blending state
auto blendSrcAlpha = context::BlendingState::BlendFuncSrcAlpha();
auto blendDstAlpha = context::BlendingState::BlendFuncDstAlpha();
auto blendAlpha = context::BlendingState::BlendEquationAlpha();
auto blendRGB = context::BlendingState::BlendEquationRGB();
auto blendSrcRGB = context::BlendingState::BlendFuncSrcRGB();
auto blendDstRGB = context::BlendingState::BlendFuncDstRGB();
// enabled capabilities
auto scissorTestEnabled = gl.IsEnabled(Capability::ScissorTest);
auto depthTestEnabled = gl.IsEnabled(Capability::DepthTest);
auto cullEnabled = gl.IsEnabled(Capability::CullFace);
auto blendEnabled = gl.IsEnabled(Capability::Blend);
// set up render state
gl.Enable(Capability::ScissorTest);
gl.Disable(Capability::DepthTest);
gl.Disable(Capability::CullFace);
gl.Enable(Capability::Blend);
gl.BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
gl.BlendEquation(BlendEquation::Add);
// activate default texture
Texture::Active(0);
// Handle cases of screen coordinates != from
// framebuffer coordinates (e.g. retina displays)
ImGuiIO &io = ImGui::GetIO();
float fBufferHeight = io.DisplaySize.y * io.DisplayFramebufferScale.y;
drawData->ScaleClipRects(io.DisplayFramebufferScale);
// Setup orthographic projection matrix
static glm::mat4x4 orthoProjection =
{
{7.7f, 0.0f, 0.0f, 0.0f},
{0.0f, 7.7f, 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{ -1.0f, 1.0f, 0.0f, 1.0f},
};
orthoProjection[0][0] = 2.0f / io.DisplaySize.x;
orthoProjection[1][1] = 2.0f / -io.DisplaySize.y;
// startup program
deviceObjects->shaderProgram.Use();
deviceObjects->texture.Set(0); // current active texture
deviceObjects->projectionMatrix.Set(orthoProjection);
deviceObjects->vao.Bind();
for (auto index = 0; index < drawData->CmdListsCount; index++)
{
const ImDrawList * cmdList = drawData->CmdLists[index];
const ImDrawIdx * idxBufferOffset = nullptr;
// vertex buffer data
deviceObjects->buffers[0].Bind(BufferTarget::Array);
Buffer::Data(BufferTarget::Array, static_cast<GLsizeiptr>
(cmdList->VtxBuffer.size() * sizeof(ImDrawVert)),
&cmdList->VtxBuffer[0], BufferUsage::StreamDraw);
// element buffer data
deviceObjects->buffers[1].Bind(BufferTarget::ElementArray);
Buffer::Data(BufferTarget::ElementArray, static_cast<size_t>
(cmdList->IdxBuffer.size()) * sizeof(ImDrawIdx),
&cmdList->IdxBuffer.front(), BufferUsage::StreamDraw);
for (auto pcmd = cmdList->CmdBuffer.begin();
pcmd != cmdList->CmdBuffer.end(); pcmd++)
{
if (pcmd->UserCallback)
{
pcmd->UserCallback(cmdList, pcmd);
}
else
{
Texture::Bind(TextureTarget::_2D, TextureName(static_cast
<GLuint>(reinterpret_cast<intptr_t>
(pcmd->TextureId))));
gl.Scissor(static_cast<int>(pcmd->ClipRect.x),
static_cast<int>(fBufferHeight - pcmd->ClipRect.w),
static_cast<int>(pcmd->ClipRect.z - pcmd->ClipRect.x),
static_cast<int>(pcmd->ClipRect.w - pcmd->ClipRect.y));
gl.DrawElements(PrimitiveType::Triangles,
pcmd->ElemCount, idxBufferOffset);
}
idxBufferOffset += pcmd->ElemCount;
}
}
// restore modified GL state
Program::Bind(prevProgram);
Texture::Bind(TextureTarget::_2D, prevTexture);
Buffer::Bind(BufferTarget::Array, prevArrayBuffer);
Buffer::Bind(BufferTarget::ElementArray, prevElementBuffer);
VertexArray::Bind(prevVertexArray);
gl.BlendEquationSeparate(blendRGB, blendAlpha);
gl.BlendFuncSeparate(blendSrcRGB, blendDstRGB, blendSrcAlpha, blendDstAlpha);
// gl flags
blendEnabled
? gl.Enable(Capability::Blend)
: gl.Disable(Capability::Blend);
cullEnabled
? gl.Enable(Capability::CullFace)
: gl.Disable(Capability::CullFace);
depthTestEnabled
? gl.Enable(Capability::DepthTest)
: gl.Disable(Capability::DepthTest);
scissorTestEnabled
? gl.Enable(Capability::ScissorTest)
: gl.Disable(Capability::ScissorTest);
}
void Interface::MouseButtonCallback(GLFWwindow * window, int button, int action,
int mods)
{
if (action == GLFW_PRESS && button >= 0 && button < 3)
{ mousePressed[button] = true; }
}
void Interface::ScrollCallback(GLFWwindow * window, double xoffset,
double yoffset)
{
// Use fractional mouse wheel, 1.0 unit 5 lines.
mouseWheel += static_cast<float>(yoffset);
}
void Interface::KeyCallback(GLFWwindow * window, int key, int scancode,
int action, int mods)
{
auto &io = ImGui::GetIO();
if (action == GLFW_PRESS)
{ io.KeysDown[key] = true; }
if (action == GLFW_RELEASE)
{ io.KeysDown[key] = false; }
(void)mods; // Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] ||
io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] ||
io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] ||
io.KeysDown[GLFW_KEY_RIGHT_ALT];
}
void Interface::CharCallback(GLFWwindow * window, unsigned c)
{
auto &io = ImGui::GetIO();
if (c > 0 && c < 0x10000)
{ io.AddInputCharacter(static_cast<unsigned short>(c)); }
}
void Interface::SetClipboardText(const char * text)
{
glfwSetClipboardString(glfwWindow, text);
}
const char * Interface::GetClipboardText()
{
return glfwGetClipboardString(glfwWindow);
}
#pragma once
#include "render_window.h"
class Interface
{
public:
Interface();
virtual ~Interface();
void Initialize(const RenderWindow &activeWindow,
bool instantCallbacks = true);
void Terminate();
void Render() const;
void NewFrame();
protected:
// application specific UI code
virtual void Draw() = 0;
private:
struct DeviceObjects
{
oglplus::Texture atlasFontTex;
// shader program and sources
oglplus::Program shaderProgram;
oglplus::VertexShader vertexShader;
oglplus::FragmentShader fragmentShader;
// uniforms and attribs for shader program
oglplus::UniformSampler texture;
oglplus::Uniform<glm::mat4> projectionMatrix;
// buffers
oglplus::Buffer buffers[2];
oglplus::VertexArray vao;
};
static GLFWwindow * glfwWindow;
static double time;
static float mouseWheel;
static bool mousePressed[3];
static std::unique_ptr<DeviceObjects> deviceObjects;
void InvalidateDeviceObjects();
void CreateFontsTexture();
void CreateDeviceObjects();
static void RenderDrawList(ImDrawData * drawData);
static void MouseButtonCallback(GLFWwindow * window, int button,
int action, int mods);
static void ScrollCallback(GLFWwindow * window, double xoffset,
double yoffset);
static void KeyCallback(GLFWwindow * window, int key, int scancode,
int action, int mods);
static void CharCallback(GLFWwindow * window, unsigned int c);
static void SetClipboardText(const char * text);
static const char * GetClipboardText();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment