|
- //
- // Lol Engine
- //
- // Copyright © 2017—2019 Sam Hocevar <sam@hocevar.net>
- // © 2009—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
- //
- // Lol Engine is free software. It comes without any warranty, to
- // the extent permitted by applicable law. You can redistribute it
- // and/or modify it under the terms of the Do What the Fuck You Want
- // to Public License, Version 2, as published by the WTFPL Task Force.
- // See http://www.wtfpl.net/ for more details.
- //
-
- #include <lol/engine-internal.h>
-
- #include <cstdio>
- #include <string>
-
- //
- // The Imgui integration
- //
-
- using namespace lol;
-
- namespace
- {
- enum key_enum
- {
- LeftClick = 300,
- RightClick,
- MiddleClick,
- Focus,
- };
-
- enum axis_enum
- {
- Scroll,
- };
- }
-
- static gui* g_gui = nullptr;
-
- //-----------------------------------------------------------------------------
- gui::gui(ImFontAtlas *shared_font_atlas)
- {
- ImGui::CreateContext(shared_font_atlas);
-
- m_gamegroup = tickable::group::game::gui;
- m_drawgroup = tickable::group::draw::gui;
-
- // Build shader code -------------------------------------------------------
- ShaderVar out_vertex = ShaderVar::GetShaderOut(ShaderProgram::Vertex);
- ShaderVar out_pixel = ShaderVar::GetShaderOut(ShaderProgram::Pixel);
-
- ShaderVar pass_texcoord = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec2, "texcoord");
- ShaderVar pass_color = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec4, "color");
-
- ShaderVar in_position = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "position");
- ShaderVar in_texcoord = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "texcoord");
- ShaderVar in_color = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec4, "color");
-
- m_ortho.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::Mat4, "ortho");
- m_texture.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::sampler2D, "texture");
-
- ShaderBlock imgui_vertex("imgui_vertex");
- imgui_vertex
- << out_vertex << m_ortho << in_position
- << pass_texcoord << in_texcoord
- << pass_color << in_color;
- imgui_vertex.SetMainCode(lol::format(
- "%s = .5 * %s * vec4(%s, -1.0, 1.0);\n" "%s = %s;\n" "%s = %s;\n",
- out_vertex.tostring().c_str(),
- m_ortho.tostring().c_str(),
- in_position.tostring().c_str(),
- pass_texcoord.tostring().c_str(),
- in_texcoord.tostring().c_str(),
- pass_color.tostring().c_str(),
- in_color.tostring().c_str()));
-
- ShaderBlock imgui_pixel("imgui_pixel");
- imgui_pixel << m_texture << pass_texcoord << pass_color << out_pixel;
- imgui_pixel.SetMainCode(lol::format(
- "vec4 col = %s * texture2D(%s, %s);\n" "if (col.a == 0.0) discard;\n" "%s = col;\n",
- pass_color.tostring().c_str(),
- m_texture.tostring().c_str(),
- pass_texcoord.tostring().c_str(),
- out_pixel.tostring().c_str()));
-
- m_builder << ShaderProgram::Vertex << imgui_vertex
- << ShaderProgram::Pixel << imgui_pixel;
-
- // Input Setup -------------------------------------------------------------
- m_profile.register_default_keys();
-
- m_profile << InputProfile::MouseKey(key_enum::LeftClick, g_name_mouse_key_left);
- m_profile << InputProfile::MouseKey(key_enum::RightClick, g_name_mouse_key_right);
- m_profile << InputProfile::MouseKey(key_enum::MiddleClick, g_name_mouse_key_middle);
- m_profile << InputProfile::MouseKey(key_enum::Focus, g_name_mouse_key_in_screen);
-
- m_profile << InputProfile::MouseAxis(axis_enum::Scroll, g_name_mouse_axis_scroll);
-
- Ticker::Ref(m_controller = new Controller("ImGui_Controller"));
- m_controller->Init(m_profile);
- }
-
- gui::~gui()
- {
- ImGui::GetIO().Fonts->TexID = nullptr;
- Ticker::Unref(m_font);
- m_font = nullptr;
-
- ImGui::DestroyContext();
- }
-
- //-----------------------------------------------------------------------------
- void gui::init(ImFontAtlas *shared_font_atlas)
- {
- Ticker::Ref(g_gui = new gui(shared_font_atlas));
-
- ImGuiIO& io = ImGui::GetIO();
- //ImFont* font0 = io.Fonts->AddFontDefault();
-
- // Keyboard mapping; these are the only ones ImGui cares about, the
- // rest is just handled by the application.
- io.KeyMap[ImGuiKey_Tab] = (int)input::key::SC_Tab;
- io.KeyMap[ImGuiKey_LeftArrow] = (int)input::key::SC_Left;
- io.KeyMap[ImGuiKey_RightArrow] = (int)input::key::SC_Right;
- io.KeyMap[ImGuiKey_UpArrow] = (int)input::key::SC_Up;
- io.KeyMap[ImGuiKey_DownArrow] = (int)input::key::SC_Down;
- io.KeyMap[ImGuiKey_Home] = (int)input::key::SC_Home;
- io.KeyMap[ImGuiKey_End] = (int)input::key::SC_End;
- io.KeyMap[ImGuiKey_Delete] = (int)input::key::SC_Delete;
- io.KeyMap[ImGuiKey_Backspace] = (int)input::key::SC_Backspace;
- io.KeyMap[ImGuiKey_Enter] = (int)input::key::SC_Return;
- io.KeyMap[ImGuiKey_Escape] = (int)input::key::SC_Escape;
- io.KeyMap[ImGuiKey_A] = (int)input::key::SC_A;
- io.KeyMap[ImGuiKey_C] = (int)input::key::SC_C;
- io.KeyMap[ImGuiKey_V] = (int)input::key::SC_V;
- io.KeyMap[ImGuiKey_X] = (int)input::key::SC_X;
- io.KeyMap[ImGuiKey_Y] = (int)input::key::SC_Y;
- io.KeyMap[ImGuiKey_Z] = (int)input::key::SC_Z;
-
- // Func pointer
- io.RenderDrawListsFn = gui::static_render_draw_lists;
- io.SetClipboardTextFn = gui::static_set_clipboard;
- io.GetClipboardTextFn = gui::static_get_clipboard;
- io.ClipboardUserData = &g_gui->m_clipboard;
- }
-
- /* CALLBACKS
- void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
- {
- ImGuiIO& io = ImGui::GetIO();
- if (c > 0 && c < 0x10000)
- io.AddInputCharacter((unsigned short)c);
- }
-
- */
-
- void gui::shutdown()
- {
- ImGui::EndFrame();
-
- if (g_gui)
- {
- Ticker::Unref(g_gui);
- g_gui = nullptr;
- }
- }
-
- //-----------------------------------------------------------------------------
- std::string gui::clipboard()
- {
- return g_gui ? g_gui->m_clipboard : "";
- }
-
- void gui::static_set_clipboard(void *data, const char* text)
- {
- std::string *clipboard = (std::string *)data;
- *clipboard = text;
- }
- const char* gui::static_get_clipboard(void *data)
- {
- std::string *clipboard = (std::string *)data;
- return clipboard->c_str();
- }
-
- void gui::refresh_fonts()
- {
- if (g_gui->m_font)
- Ticker::Unref(g_gui->m_font);
-
- // Build texture
- unsigned char* pixels;
- ivec2 size;
- ImGuiIO& io = ImGui::GetIO();
- io.Fonts->GetTexDataAsRGBA32(&pixels, &size.x, &size.y);
-
- Image* image = new Image();
- image->Copy(pixels, size, PixelFormat::RGBA_8);
-
- Ticker::Ref(g_gui->m_font = new TextureImage("", image));
- }
-
- //-----------------------------------------------------------------------------
- void gui::tick_game(float seconds)
- {
- super::tick_game(seconds);
-
- auto keyboard = input::get()->keyboard();
- auto mouse = input::get()->mouse();
-
- ImGuiIO& io = ImGui::GetIO();
-
- // Init Texture
- if (!m_font)
- {
- refresh_fonts();
- }
-
- // Texture has been created
- if (m_font && m_font->GetTexture())
- {
- io.Fonts->TexID = (void *)m_font->GetTexture();
- }
-
- // Setup display size (every frame to accommodate for window resizing)
- auto video_size = vec2(Video::GetSize());
- io.DisplaySize = video_size;
-
- // Setup time step
- io.DeltaTime = seconds;
- io.MouseDrawCursor = true;
-
- // Update Keyboard
- io.KeyCtrl = m_controller->IsKeyPressed((int)input::key::SC_LCtrl)
- || m_controller->IsKeyPressed((int)input::key::SC_RCtrl);
- io.KeyShift = m_controller->IsKeyPressed((int)input::key::SC_LShift)
- || m_controller->IsKeyPressed((int)input::key::SC_RShift);
-
- for (input::key k : input::all_keys())
- io.KeysDown[(int)k] = m_controller->IsKeyPressed((int)k);
-
- // Update text input
- if (io.WantTextInput)
- {
- std::string text = keyboard->text();
- //text.case_change(io.KeyShift);
- for (auto ch : text)
- io.AddInputCharacter(ch);
- }
- keyboard->capture_text(io.WantTextInput);
-
- // Update mouse
- vec2 cursor = mouse->get_cursor_uv(0);
- cursor.y = 1.f - cursor.y;
-
- io.MousePos = cursor * video_size;
- //msg::debug("%.2f/%.2f\n", io.MousePos.x, io.MousePos.y);
- io.MouseWheel = m_controller->GetAxisValue(axis_enum::Scroll);
-
- io.MouseDown[0] = m_controller->IsKeyPressed(key_enum::LeftClick);
- io.MouseDown[1] = m_controller->IsKeyPressed(key_enum::RightClick);
- io.MouseDown[2] = m_controller->IsKeyPressed(key_enum::MiddleClick);
- // FIXME: handle key_enum::Focus?
-
- // Start the frame
- ImGui::NewFrame();
- }
-
- //-----------------------------------------------------------------------------
- void gui::tick_draw(float seconds, Scene &scene)
- {
- super::tick_draw(seconds, scene);
-
- scene.AddPrimitiveRenderer(this, std::make_shared<primitive>());
- }
-
- void gui::primitive::Render(Scene& scene, std::shared_ptr<PrimitiveSource> prim)
- {
- UNUSED(scene, prim);
-
- ImGui::Render();
- ImGui::EndFrame();
- }
-
- //// Data
- //static GLFWwindow* g_Window = NULL;
- //static double g_Time = 0.0f;
- //static bool g_MousePressed[3] = { false, false, false };
- //static float g_MouseWheel = 0.0f;
- //static GLuint g_FontTexture = 0;
-
- //-------------------------------------------------------------------------
- // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
- // If text or lines are blurry when integrating ImGui in your engine:
- // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
- //-------------------------------------------------------------------------
- void gui::static_render_draw_lists(ImDrawData* draw_data)
- {
- g_gui->render_draw_lists(draw_data);
- }
-
- void gui::render_draw_lists(ImDrawData* draw_data)
- {
- if (draw_data == nullptr)
- return;
-
- vec2 size = vec2(Video::GetSize());
- float alpha = 1.f;
- mat4 ortho = mat4::ortho(size.x * alpha, size.y * alpha, -1000.f, 1000.f)
- * mat4::lookat(vec3::axis_z, vec3::zero, vec3::axis_y)
- * mat4::scale(vec3::axis_x - vec3::axis_y - vec3::axis_z)
- * mat4::translate(-size.x * .5f * alpha, -size.y * .5f * alpha, 0.f);
-
- // Create shader
- if (!m_shader)
- {
- m_shader = Shader::Create(m_builder.GetName(), m_builder.Build());
- ASSERT(m_shader);
-
- m_ortho.m_uniform = m_shader->GetUniformLocation(m_ortho.m_var.tostring());
- m_texture.m_uniform = m_shader->GetUniformLocation(m_texture.m_var.tostring());
-
- m_attribs << m_shader->GetAttribLocation(VertexUsage::Position, 0)
- << m_shader->GetAttribLocation(VertexUsage::TexCoord, 0)
- << m_shader->GetAttribLocation(VertexUsage::Color, 0);
-
- m_vdecl = std::make_shared<VertexDeclaration>(
- VertexStream<vec2, vec2, u8vec4>(
- VertexUsage::Position,
- VertexUsage::TexCoord,
- VertexUsage::Color));
- }
-
- // Do not render without shader
- if (!m_shader)
- return;
-
- render_context rc(Scene::GetScene(0).get_renderer());
- rc.cull_mode(CullMode::Disabled);
- rc.depth_func(DepthFunc::Disabled);
- rc.scissor_mode(ScissorMode::Enabled);
-
- m_shader->Bind();
-
- // Register uniforms
- m_shader->SetUniform(m_ortho, ortho);
-
- for (int n = 0; n < draw_data->CmdListsCount; n++)
- {
- auto const &command_list = *draw_data->CmdLists[n];
- /*const unsigned char* vtx_buffer = (const unsigned char*)&command_list.VtxBuffer.front();*/
-
- struct Vertex
- {
- vec2 pos, tex;
- u8vec4 color;
- };
-
- auto vbo = std::make_shared<VertexBuffer>(command_list.VtxBuffer.Size * sizeof(ImDrawVert));
- ImDrawVert *vert = (ImDrawVert *)vbo->Lock(0, 0);
- memcpy(vert, command_list.VtxBuffer.Data, command_list.VtxBuffer.Size * sizeof(ImDrawVert));
- vbo->Unlock();
-
- auto ibo = std::make_shared<IndexBuffer>(command_list.IdxBuffer.Size * sizeof(ImDrawIdx));
- ImDrawIdx *indices = (ImDrawIdx *)ibo->Lock(0, 0);
- memcpy(indices, command_list.IdxBuffer.Data, command_list.IdxBuffer.Size * sizeof(ImDrawIdx));
- ibo->Unlock();
-
- ibo->Bind();
- m_vdecl->Bind();
- m_vdecl->SetStream(vbo, m_attribs[0], m_attribs[1], m_attribs[2]);
-
- const ImDrawIdx* idx_buffer_offset = 0;
- for (int cmd_i = 0; cmd_i < command_list.CmdBuffer.Size; cmd_i++)
- {
- auto const &command = command_list.CmdBuffer[cmd_i];
- Texture* texture = (Texture*)command.TextureId;
- if (texture)
- {
- texture->Bind();
- m_shader->SetUniform(m_texture, texture->GetTextureUniform(), 0);
- }
-
- rc.scissor_rect(command.ClipRect);
-
- #ifdef SHOW_IMGUI_DEBUG
- //-----------------------------------------------------------------
- //<Debug render> --------------------------------------------------
- //-----------------------------------------------------------------
- //Doesn't work anymore ......
- static uint32_t idx_buffer_offset_i = 0;
- if (cmd_i == 0)
- idx_buffer_offset_i = 0;
-
- float mod = -200.f;
- vec3 off = vec3(vec2(-size.x, -size.y), 0.f);
- vec3 pos[4] = {
- (1.f / mod) * (off + vec3(0.f)),
- (1.f / mod) * (off + size.x * vec3::axis_x),
- (1.f / mod) * (off + size.x * vec3::axis_x + size.y * vec3::axis_y),
- (1.f / mod) * (off + size.y * vec3::axis_y)
- };
- for (int i = 0; i < 4; ++i)
- Debug::DrawLine(pos[i], pos[(i + 1) % 4], Color::white);
- ImDrawVert* buf = vert;
- for (uint16_t i = 0; i < command.ElemCount; i += 3)
- {
- uint16_t ib = indices[idx_buffer_offset_i + i];
- vec2 pos[3];
- pos[0] = vec2(buf[ib + 0].pos.x, buf[ib + 0].pos.y);
- pos[1] = vec2(buf[ib + 1].pos.x, buf[ib + 1].pos.y);
- pos[2] = vec2(buf[ib + 2].pos.x, buf[ib + 2].pos.y);
- vec4 col[3];
- col[0] = vec4(Color::FromRGBA32(buf[ib + 0].col).arg, 1.f);
- col[1] = vec4(Color::FromRGBA32(buf[ib + 1].col).arg, 1.f);
- col[2] = vec4(Color::FromRGBA32(buf[ib + 2].col).arg, 1.f);
- Debug::DrawLine((off + vec3(pos[0], 0.f)) / mod, (off + vec3(pos[1], 0.f)) / mod, col[0]);
- Debug::DrawLine((off + vec3(pos[1], 0.f)) / mod, (off + vec3(pos[2], 0.f)) / mod, col[1]);
- Debug::DrawLine((off + vec3(pos[2], 0.f)) / mod, (off + vec3(pos[0], 0.f)) / mod, col[2]);
- }
- idx_buffer_offset_i += command.ElemCount;
-
- //-----------------------------------------------------------------
- //<\Debug render> -------------------------------------------------
- //-----------------------------------------------------------------
- #endif //SHOW_IMGUI_DEBUG
- //Debug::DrawLine(vec2::zero, vec2::axis_x /*, Color::green*/);
-
- m_vdecl->DrawIndexedElements(MeshPrimitive::Triangles, command.ElemCount, (const short*)idx_buffer_offset);
-
- idx_buffer_offset += command.ElemCount;
- }
-
- m_vdecl->Unbind();
- ibo->Unbind();
- }
-
- m_shader->Unbind();
- }
|