443 lines
15 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2017—2019 Sam Hocevar <sam@hocevar.net>
  5. // © 2009—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
  6. //
  7. // Lol Engine is free software. It comes without any warranty, to
  8. // the extent permitted by applicable law. You can redistribute it
  9. // and/or modify it under the terms of the Do What the Fuck You Want
  10. // to Public License, Version 2, as published by the WTFPL Task Force.
  11. // See http://www.wtfpl.net/ for more details.
  12. //
  13. #include <lol/engine-internal.h>
  14. #include <cstdio>
  15. #include <string>
  16. //
  17. // The Imgui integration
  18. //
  19. using namespace lol;
  20. namespace
  21. {
  22. enum key_enum
  23. {
  24. LeftClick = 300,
  25. RightClick,
  26. MiddleClick,
  27. Focus,
  28. };
  29. enum axis_enum
  30. {
  31. Scroll,
  32. };
  33. }
  34. static gui* g_gui = nullptr;
  35. //-----------------------------------------------------------------------------
  36. gui::gui(ImFontAtlas *shared_font_atlas)
  37. {
  38. ImGui::CreateContext(shared_font_atlas);
  39. m_gamegroup = tickable::group::game::gui;
  40. m_drawgroup = tickable::group::draw::gui;
  41. // Build shader code -------------------------------------------------------
  42. ShaderVar out_vertex = ShaderVar::GetShaderOut(ShaderProgram::Vertex);
  43. ShaderVar out_pixel = ShaderVar::GetShaderOut(ShaderProgram::Pixel);
  44. ShaderVar pass_texcoord = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec2, "texcoord");
  45. ShaderVar pass_color = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec4, "color");
  46. ShaderVar in_position = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "position");
  47. ShaderVar in_texcoord = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "texcoord");
  48. ShaderVar in_color = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec4, "color");
  49. m_ortho.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::Mat4, "ortho");
  50. m_texture.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::sampler2D, "texture");
  51. ShaderBlock imgui_vertex("imgui_vertex");
  52. imgui_vertex
  53. << out_vertex << m_ortho << in_position
  54. << pass_texcoord << in_texcoord
  55. << pass_color << in_color;
  56. imgui_vertex.SetMainCode(lol::format(
  57. "%s = .5 * %s * vec4(%s, -1.0, 1.0);\n" "%s = %s;\n" "%s = %s;\n",
  58. out_vertex.tostring().c_str(),
  59. m_ortho.tostring().c_str(),
  60. in_position.tostring().c_str(),
  61. pass_texcoord.tostring().c_str(),
  62. in_texcoord.tostring().c_str(),
  63. pass_color.tostring().c_str(),
  64. in_color.tostring().c_str()));
  65. ShaderBlock imgui_pixel("imgui_pixel");
  66. imgui_pixel << m_texture << pass_texcoord << pass_color << out_pixel;
  67. imgui_pixel.SetMainCode(lol::format(
  68. "vec4 col = %s * texture2D(%s, %s);\n" "if (col.a == 0.0) discard;\n" "%s = col;\n",
  69. pass_color.tostring().c_str(),
  70. m_texture.tostring().c_str(),
  71. pass_texcoord.tostring().c_str(),
  72. out_pixel.tostring().c_str()));
  73. m_builder << ShaderProgram::Vertex << imgui_vertex
  74. << ShaderProgram::Pixel << imgui_pixel;
  75. // Input Setup -------------------------------------------------------------
  76. m_profile.register_default_keys();
  77. m_profile << InputProfile::MouseKey(key_enum::LeftClick, g_name_mouse_key_left);
  78. m_profile << InputProfile::MouseKey(key_enum::RightClick, g_name_mouse_key_right);
  79. m_profile << InputProfile::MouseKey(key_enum::MiddleClick, g_name_mouse_key_middle);
  80. m_profile << InputProfile::MouseKey(key_enum::Focus, g_name_mouse_key_in_screen);
  81. m_profile << InputProfile::MouseAxis(axis_enum::Scroll, g_name_mouse_axis_scroll);
  82. Ticker::Ref(m_controller = new Controller("ImGui_Controller"));
  83. m_controller->Init(m_profile);
  84. }
  85. gui::~gui()
  86. {
  87. ImGui::GetIO().Fonts->TexID = nullptr;
  88. Ticker::Unref(m_font);
  89. m_font = nullptr;
  90. ImGui::DestroyContext();
  91. }
  92. //-----------------------------------------------------------------------------
  93. void gui::init(ImFontAtlas *shared_font_atlas)
  94. {
  95. Ticker::Ref(g_gui = new gui(shared_font_atlas));
  96. ImGuiIO& io = ImGui::GetIO();
  97. //ImFont* font0 = io.Fonts->AddFontDefault();
  98. // Keyboard mapping; these are the only ones ImGui cares about, the
  99. // rest is just handled by the application.
  100. io.KeyMap[ImGuiKey_Tab] = (int)input::key::SC_Tab;
  101. io.KeyMap[ImGuiKey_LeftArrow] = (int)input::key::SC_Left;
  102. io.KeyMap[ImGuiKey_RightArrow] = (int)input::key::SC_Right;
  103. io.KeyMap[ImGuiKey_UpArrow] = (int)input::key::SC_Up;
  104. io.KeyMap[ImGuiKey_DownArrow] = (int)input::key::SC_Down;
  105. io.KeyMap[ImGuiKey_Home] = (int)input::key::SC_Home;
  106. io.KeyMap[ImGuiKey_End] = (int)input::key::SC_End;
  107. io.KeyMap[ImGuiKey_Delete] = (int)input::key::SC_Delete;
  108. io.KeyMap[ImGuiKey_Backspace] = (int)input::key::SC_Backspace;
  109. io.KeyMap[ImGuiKey_Enter] = (int)input::key::SC_Return;
  110. io.KeyMap[ImGuiKey_Escape] = (int)input::key::SC_Escape;
  111. io.KeyMap[ImGuiKey_A] = (int)input::key::SC_A;
  112. io.KeyMap[ImGuiKey_C] = (int)input::key::SC_C;
  113. io.KeyMap[ImGuiKey_V] = (int)input::key::SC_V;
  114. io.KeyMap[ImGuiKey_X] = (int)input::key::SC_X;
  115. io.KeyMap[ImGuiKey_Y] = (int)input::key::SC_Y;
  116. io.KeyMap[ImGuiKey_Z] = (int)input::key::SC_Z;
  117. // Func pointer
  118. io.RenderDrawListsFn = gui::static_render_draw_lists;
  119. io.SetClipboardTextFn = gui::static_set_clipboard;
  120. io.GetClipboardTextFn = gui::static_get_clipboard;
  121. io.ClipboardUserData = &g_gui->m_clipboard;
  122. }
  123. /* CALLBACKS
  124. void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
  125. {
  126. ImGuiIO& io = ImGui::GetIO();
  127. if (c > 0 && c < 0x10000)
  128. io.AddInputCharacter((unsigned short)c);
  129. }
  130. */
  131. void gui::shutdown()
  132. {
  133. ImGui::EndFrame();
  134. if (g_gui)
  135. {
  136. Ticker::Unref(g_gui);
  137. g_gui = nullptr;
  138. }
  139. }
  140. //-----------------------------------------------------------------------------
  141. std::string gui::clipboard()
  142. {
  143. return g_gui ? g_gui->m_clipboard : "";
  144. }
  145. void gui::static_set_clipboard(void *data, const char* text)
  146. {
  147. std::string *clipboard = (std::string *)data;
  148. *clipboard = text;
  149. }
  150. const char* gui::static_get_clipboard(void *data)
  151. {
  152. std::string *clipboard = (std::string *)data;
  153. return clipboard->c_str();
  154. }
  155. void gui::refresh_fonts()
  156. {
  157. if (g_gui->m_font)
  158. Ticker::Unref(g_gui->m_font);
  159. // Build texture
  160. unsigned char* pixels;
  161. ivec2 size;
  162. ImGuiIO& io = ImGui::GetIO();
  163. io.Fonts->GetTexDataAsRGBA32(&pixels, &size.x, &size.y);
  164. Image* image = new Image();
  165. image->Copy(pixels, size, PixelFormat::RGBA_8);
  166. Ticker::Ref(g_gui->m_font = new TextureImage("", image));
  167. }
  168. //-----------------------------------------------------------------------------
  169. void gui::tick_game(float seconds)
  170. {
  171. super::tick_game(seconds);
  172. auto keyboard = input::get()->keyboard();
  173. auto mouse = input::get()->mouse();
  174. ImGuiIO& io = ImGui::GetIO();
  175. // Init Texture
  176. if (!m_font)
  177. {
  178. refresh_fonts();
  179. }
  180. // Texture has been created
  181. if (m_font && m_font->GetTexture())
  182. {
  183. io.Fonts->TexID = (void *)m_font->GetTexture();
  184. }
  185. // Setup display size (every frame to accommodate for window resizing)
  186. auto video_size = vec2(Video::GetSize());
  187. io.DisplaySize = video_size;
  188. // Setup time step
  189. io.DeltaTime = seconds;
  190. io.MouseDrawCursor = true;
  191. // Update Keyboard
  192. io.KeyCtrl = m_controller->IsKeyPressed((int)input::key::SC_LCtrl)
  193. || m_controller->IsKeyPressed((int)input::key::SC_RCtrl);
  194. io.KeyShift = m_controller->IsKeyPressed((int)input::key::SC_LShift)
  195. || m_controller->IsKeyPressed((int)input::key::SC_RShift);
  196. for (input::key k : input::all_keys())
  197. io.KeysDown[(int)k] = m_controller->IsKeyPressed((int)k);
  198. // Update text input
  199. if (io.WantTextInput)
  200. {
  201. std::string text = keyboard->text();
  202. //text.case_change(io.KeyShift);
  203. for (auto ch : text)
  204. io.AddInputCharacter(ch);
  205. }
  206. keyboard->capture_text(io.WantTextInput);
  207. // Update mouse
  208. vec2 cursor = mouse->get_cursor_uv(0);
  209. cursor.y = 1.f - cursor.y;
  210. io.MousePos = cursor * video_size;
  211. //msg::debug("%.2f/%.2f\n", io.MousePos.x, io.MousePos.y);
  212. io.MouseWheel = m_controller->GetAxisValue(axis_enum::Scroll);
  213. io.MouseDown[0] = m_controller->IsKeyPressed(key_enum::LeftClick);
  214. io.MouseDown[1] = m_controller->IsKeyPressed(key_enum::RightClick);
  215. io.MouseDown[2] = m_controller->IsKeyPressed(key_enum::MiddleClick);
  216. // FIXME: handle key_enum::Focus?
  217. // Start the frame
  218. ImGui::NewFrame();
  219. }
  220. //-----------------------------------------------------------------------------
  221. void gui::tick_draw(float seconds, Scene &scene)
  222. {
  223. super::tick_draw(seconds, scene);
  224. scene.AddPrimitiveRenderer(this, std::make_shared<primitive>());
  225. }
  226. void gui::primitive::Render(Scene& scene, std::shared_ptr<PrimitiveSource> prim)
  227. {
  228. UNUSED(scene, prim);
  229. ImGui::Render();
  230. ImGui::EndFrame();
  231. }
  232. //// Data
  233. //static GLFWwindow* g_Window = NULL;
  234. //static double g_Time = 0.0f;
  235. //static bool g_MousePressed[3] = { false, false, false };
  236. //static float g_MouseWheel = 0.0f;
  237. //static GLuint g_FontTexture = 0;
  238. //-------------------------------------------------------------------------
  239. // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
  240. // If text or lines are blurry when integrating ImGui in your engine:
  241. // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
  242. //-------------------------------------------------------------------------
  243. void gui::static_render_draw_lists(ImDrawData* draw_data)
  244. {
  245. g_gui->render_draw_lists(draw_data);
  246. }
  247. void gui::render_draw_lists(ImDrawData* draw_data)
  248. {
  249. if (draw_data == nullptr)
  250. return;
  251. vec2 size = vec2(Video::GetSize());
  252. float alpha = 1.f;
  253. mat4 ortho = mat4::ortho(size.x * alpha, size.y * alpha, -1000.f, 1000.f)
  254. * mat4::lookat(vec3::axis_z, vec3::zero, vec3::axis_y)
  255. * mat4::scale(vec3::axis_x - vec3::axis_y - vec3::axis_z)
  256. * mat4::translate(-size.x * .5f * alpha, -size.y * .5f * alpha, 0.f);
  257. // Create shader
  258. if (!m_shader)
  259. {
  260. m_shader = Shader::Create(m_builder.GetName(), m_builder.Build());
  261. ASSERT(m_shader);
  262. m_ortho.m_uniform = m_shader->GetUniformLocation(m_ortho.m_var.tostring());
  263. m_texture.m_uniform = m_shader->GetUniformLocation(m_texture.m_var.tostring());
  264. m_attribs << m_shader->GetAttribLocation(VertexUsage::Position, 0)
  265. << m_shader->GetAttribLocation(VertexUsage::TexCoord, 0)
  266. << m_shader->GetAttribLocation(VertexUsage::Color, 0);
  267. m_vdecl = std::make_shared<VertexDeclaration>(
  268. VertexStream<vec2, vec2, u8vec4>(
  269. VertexUsage::Position,
  270. VertexUsage::TexCoord,
  271. VertexUsage::Color));
  272. }
  273. // Do not render without shader
  274. if (!m_shader)
  275. return;
  276. render_context rc(Scene::GetScene(0).get_renderer());
  277. rc.cull_mode(CullMode::Disabled);
  278. rc.depth_func(DepthFunc::Disabled);
  279. rc.scissor_mode(ScissorMode::Enabled);
  280. m_shader->Bind();
  281. // Register uniforms
  282. m_shader->SetUniform(m_ortho, ortho);
  283. for (int n = 0; n < draw_data->CmdListsCount; n++)
  284. {
  285. auto const &command_list = *draw_data->CmdLists[n];
  286. /*const unsigned char* vtx_buffer = (const unsigned char*)&command_list.VtxBuffer.front();*/
  287. struct Vertex
  288. {
  289. vec2 pos, tex;
  290. u8vec4 color;
  291. };
  292. auto vbo = std::make_shared<VertexBuffer>(command_list.VtxBuffer.Size * sizeof(ImDrawVert));
  293. ImDrawVert *vert = (ImDrawVert *)vbo->Lock(0, 0);
  294. memcpy(vert, command_list.VtxBuffer.Data, command_list.VtxBuffer.Size * sizeof(ImDrawVert));
  295. vbo->Unlock();
  296. auto ibo = std::make_shared<IndexBuffer>(command_list.IdxBuffer.Size * sizeof(ImDrawIdx));
  297. ImDrawIdx *indices = (ImDrawIdx *)ibo->Lock(0, 0);
  298. memcpy(indices, command_list.IdxBuffer.Data, command_list.IdxBuffer.Size * sizeof(ImDrawIdx));
  299. ibo->Unlock();
  300. ibo->Bind();
  301. m_vdecl->Bind();
  302. m_vdecl->SetStream(vbo, m_attribs[0], m_attribs[1], m_attribs[2]);
  303. const ImDrawIdx* idx_buffer_offset = 0;
  304. for (int cmd_i = 0; cmd_i < command_list.CmdBuffer.Size; cmd_i++)
  305. {
  306. auto const &command = command_list.CmdBuffer[cmd_i];
  307. Texture* texture = (Texture*)command.TextureId;
  308. if (texture)
  309. {
  310. texture->Bind();
  311. m_shader->SetUniform(m_texture, texture->GetTextureUniform(), 0);
  312. }
  313. rc.scissor_rect(command.ClipRect);
  314. #ifdef SHOW_IMGUI_DEBUG
  315. //-----------------------------------------------------------------
  316. //<Debug render> --------------------------------------------------
  317. //-----------------------------------------------------------------
  318. //Doesn't work anymore ......
  319. static uint32_t idx_buffer_offset_i = 0;
  320. if (cmd_i == 0)
  321. idx_buffer_offset_i = 0;
  322. float mod = -200.f;
  323. vec3 off = vec3(vec2(-size.x, -size.y), 0.f);
  324. vec3 pos[4] = {
  325. (1.f / mod) * (off + vec3(0.f)),
  326. (1.f / mod) * (off + size.x * vec3::axis_x),
  327. (1.f / mod) * (off + size.x * vec3::axis_x + size.y * vec3::axis_y),
  328. (1.f / mod) * (off + size.y * vec3::axis_y)
  329. };
  330. for (int i = 0; i < 4; ++i)
  331. Debug::DrawLine(pos[i], pos[(i + 1) % 4], Color::white);
  332. ImDrawVert* buf = vert;
  333. for (uint16_t i = 0; i < command.ElemCount; i += 3)
  334. {
  335. uint16_t ib = indices[idx_buffer_offset_i + i];
  336. vec2 pos[3];
  337. pos[0] = vec2(buf[ib + 0].pos.x, buf[ib + 0].pos.y);
  338. pos[1] = vec2(buf[ib + 1].pos.x, buf[ib + 1].pos.y);
  339. pos[2] = vec2(buf[ib + 2].pos.x, buf[ib + 2].pos.y);
  340. vec4 col[3];
  341. col[0] = vec4(Color::FromRGBA32(buf[ib + 0].col).arg, 1.f);
  342. col[1] = vec4(Color::FromRGBA32(buf[ib + 1].col).arg, 1.f);
  343. col[2] = vec4(Color::FromRGBA32(buf[ib + 2].col).arg, 1.f);
  344. Debug::DrawLine((off + vec3(pos[0], 0.f)) / mod, (off + vec3(pos[1], 0.f)) / mod, col[0]);
  345. Debug::DrawLine((off + vec3(pos[1], 0.f)) / mod, (off + vec3(pos[2], 0.f)) / mod, col[1]);
  346. Debug::DrawLine((off + vec3(pos[2], 0.f)) / mod, (off + vec3(pos[0], 0.f)) / mod, col[2]);
  347. }
  348. idx_buffer_offset_i += command.ElemCount;
  349. //-----------------------------------------------------------------
  350. //<\Debug render> -------------------------------------------------
  351. //-----------------------------------------------------------------
  352. #endif //SHOW_IMGUI_DEBUG
  353. //Debug::DrawLine(vec2::zero, vec2::axis_x /*, Color::green*/);
  354. m_vdecl->DrawIndexedElements(MeshPrimitive::Triangles, command.ElemCount, (const short*)idx_buffer_offset);
  355. idx_buffer_offset += command.ElemCount;
  356. }
  357. m_vdecl->Unbind();
  358. ibo->Unbind();
  359. }
  360. m_shader->Unbind();
  361. }