Last active
November 8, 2015 01:56
-
-
Save jose-villegas/2a64944731720fffd78a to your computer and use it in GitHub Desktop.
This file contains 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
#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); | |
} |
This file contains 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
#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