SystemUI.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. //
  2. // Copyright (c) 2017 the Atomic project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #define STB_TEXTEDIT_IMPLEMENTATION 1
  23. #include "../../Input/InputEvents.h"
  24. #include "../../Input/Input.h"
  25. #include "../../Core/CoreEvents.h"
  26. #include "../../Core/Context.h"
  27. #include "../../Core/Profiler.h"
  28. #include "../../Engine/EngineEvents.h"
  29. #include "../../Graphics/GraphicsEvents.h"
  30. #include "../../Graphics/Graphics.h"
  31. #include "../../Resource/ResourceCache.h"
  32. #include "../../UI/SystemUI/SystemUI.h"
  33. #include "../../UI/SystemUI/Console.h"
  34. #include "../../UI/SystemUI/DebugHud.h"
  35. #include <SDL.h>
  36. using namespace std::placeholders;
  37. namespace Atomic
  38. {
  39. SystemUI::SystemUI(Atomic::Context* context)
  40. : Object(context)
  41. , vertexBuffer_(context)
  42. , indexBuffer_(context)
  43. {
  44. ImGuiIO& io = ImGui::GetIO();
  45. io.KeyMap[ImGuiKey_Tab] = SCANCODE_TAB;
  46. io.KeyMap[ImGuiKey_LeftArrow] = SCANCODE_LEFT;
  47. io.KeyMap[ImGuiKey_RightArrow] = SCANCODE_RIGHT;
  48. io.KeyMap[ImGuiKey_UpArrow] = SCANCODE_UP;
  49. io.KeyMap[ImGuiKey_DownArrow] = SCANCODE_DOWN;
  50. io.KeyMap[ImGuiKey_Home] = SCANCODE_HOME;
  51. io.KeyMap[ImGuiKey_End] = SCANCODE_END;
  52. io.KeyMap[ImGuiKey_Delete] = SCANCODE_DELETE;
  53. io.KeyMap[ImGuiKey_Backspace] = SCANCODE_BACKSPACE;
  54. io.KeyMap[ImGuiKey_Enter] = SCANCODE_RETURN;
  55. io.KeyMap[ImGuiKey_Escape] = SCANCODE_ESCAPE;
  56. io.KeyMap[ImGuiKey_A] = SCANCODE_A;
  57. io.KeyMap[ImGuiKey_C] = SCANCODE_C;
  58. io.KeyMap[ImGuiKey_V] = SCANCODE_V;
  59. io.KeyMap[ImGuiKey_X] = SCANCODE_X;
  60. io.KeyMap[ImGuiKey_Y] = SCANCODE_Y;
  61. io.KeyMap[ImGuiKey_Z] = SCANCODE_Z;
  62. io.RenderDrawListsFn = [](ImDrawData* data)
  63. {
  64. static_cast<SystemUI*>(ImGui::GetIO().UserData)->OnRenderDrawLists(data);
  65. };
  66. io.SetClipboardTextFn = [](void* user_data, const char* text)
  67. { SDL_SetClipboardText(text); };
  68. io.GetClipboardTextFn = [](void* user_data) -> const char*
  69. { return SDL_GetClipboardText(); };
  70. io.UserData = this;
  71. io.Fonts->AddFontDefault();
  72. ReallocateFontTexture();
  73. UpdateProjectionMatrix();
  74. // Subscribe to events
  75. SubscribeToEvent(E_POSTUPDATE, std::bind(&SystemUI::OnPostUpdate, this, _2));
  76. SubscribeToEvent(E_ENDRENDERING, [&](StringHash, VariantMap&)
  77. {
  78. ATOMIC_PROFILE(SystemUiRender);
  79. ImGui::Render();
  80. });
  81. SubscribeToEvent(E_SDLRAWINPUT, std::bind(&SystemUI::OnRawEvent, this, _2));
  82. SubscribeToEvent(E_SCREENMODE, std::bind(&SystemUI::UpdateProjectionMatrix, this));
  83. context_->RegisterSubsystem(new Console(context_));
  84. context_->RegisterSubsystem(new DebugHud(context_));
  85. }
  86. SystemUI::~SystemUI()
  87. {
  88. ImGui::Shutdown();
  89. }
  90. void SystemUI::UpdateProjectionMatrix()
  91. {
  92. // Update screen size
  93. auto graphics = GetSubsystem<Graphics>();
  94. ImGui::GetIO().DisplaySize = ImVec2((float)graphics->GetWidth(), (float)graphics->GetHeight());
  95. // Update projection matrix
  96. IntVector2 viewSize = graphics->GetViewport().Size();
  97. Vector2 invScreenSize(1.0f / viewSize.x_, 1.0f / viewSize.y_);
  98. Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
  99. Vector2 offset(-1.0f, 1.0f);
  100. projection_ = Matrix4(Matrix4::IDENTITY);
  101. projection_.m00_ = scale.x_ * uiScale_;
  102. projection_.m03_ = offset.x_;
  103. projection_.m11_ = scale.y_ * uiScale_;
  104. projection_.m13_ = offset.y_;
  105. projection_.m22_ = 1.0f;
  106. projection_.m23_ = 0.0f;
  107. projection_.m33_ = 1.0f;
  108. }
  109. void SystemUI::OnRawEvent(VariantMap& args)
  110. {
  111. auto evt = static_cast<SDL_Event*>(args[SDLRawInput::P_SDLEVENT].Get<void*>());
  112. auto& io = ImGui::GetIO();
  113. switch (evt->type)
  114. {
  115. case SDL_KEYUP:
  116. case SDL_KEYDOWN:
  117. {
  118. auto code = evt->key.keysym.scancode;
  119. auto down = evt->type == SDL_KEYDOWN;
  120. if (code < 512)
  121. io.KeysDown[code] = down;
  122. if (evt->key.keysym.sym == SDLK_LCTRL || evt->key.keysym.sym == SDLK_RCTRL)
  123. io.KeyCtrl = down;
  124. else if (evt->key.keysym.sym == SDLK_LSHIFT || evt->key.keysym.sym == SDLK_RSHIFT)
  125. io.KeyShift = down;
  126. else if (evt->key.keysym.sym == SDLK_LALT || evt->key.keysym.sym == SDLK_RALT)
  127. io.KeyAlt = down;
  128. else if (evt->key.keysym.sym == SDLK_LGUI || evt->key.keysym.sym == SDLK_RGUI)
  129. io.KeySuper = down;
  130. break;
  131. }
  132. case SDL_MOUSEWHEEL:
  133. io.MouseWheel = evt->wheel.y;
  134. break;
  135. case SDL_MOUSEBUTTONUP:
  136. case SDL_MOUSEBUTTONDOWN:
  137. io.MouseDown[evt->button.button - 1] = evt->type == SDL_MOUSEBUTTONDOWN;
  138. case SDL_MOUSEMOTION:
  139. io.MousePos.x = evt->motion.x / uiScale_;
  140. io.MousePos.y = evt->motion.y / uiScale_;
  141. break;
  142. case SDL_FINGERUP:
  143. io.MouseDown[0] = false;
  144. io.MousePos.x = -1;
  145. io.MousePos.y = -1;
  146. case SDL_FINGERDOWN:
  147. io.MouseDown[0] = true;
  148. case SDL_FINGERMOTION:
  149. io.MousePos.x = evt->tfinger.x / uiScale_;
  150. io.MousePos.y = evt->tfinger.y / uiScale_;
  151. break;
  152. case SDL_TEXTINPUT:
  153. ImGui::GetIO().AddInputCharactersUTF8(evt->text.text);
  154. break;
  155. default:
  156. break;
  157. }
  158. switch (evt->type)
  159. {
  160. case SDL_KEYUP:
  161. case SDL_KEYDOWN:
  162. case SDL_TEXTINPUT:
  163. args[SDLRawInput::P_CONSUMED] = ImGui::IsAnyItemActive();
  164. break;
  165. case SDL_MOUSEWHEEL:
  166. case SDL_MOUSEBUTTONUP:
  167. case SDL_MOUSEBUTTONDOWN:
  168. case SDL_MOUSEMOTION:
  169. case SDL_FINGERUP:
  170. case SDL_FINGERDOWN:
  171. case SDL_FINGERMOTION:
  172. args[SDLRawInput::P_CONSUMED] = ImGui::IsMouseHoveringAnyWindow();
  173. break;
  174. default:
  175. break;
  176. }
  177. }
  178. void SystemUI::OnPostUpdate(VariantMap& args)
  179. {
  180. auto& io = ImGui::GetIO();
  181. float timeStep = args[PostUpdate::P_TIMESTEP].GetFloat();
  182. io.DeltaTime = timeStep > 0.0f ? timeStep : 1.0f / 60.0f;
  183. ImGui::NewFrame();
  184. ATOMIC_PROFILE(SystemUiFrame);
  185. SendEvent(E_SYSTEMUIFRAME);
  186. }
  187. void SystemUI::OnRenderDrawLists(ImDrawData* data)
  188. {
  189. auto _graphics = GetSubsystem<Graphics>();
  190. // Engine does not render when window is closed or device is lost
  191. assert(_graphics && _graphics->IsInitialized() && !_graphics->IsDeviceLost());
  192. ImGuiIO& io = ImGui::GetIO();
  193. int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
  194. int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
  195. if (fb_width == 0 || fb_height == 0)
  196. return;
  197. data->ScaleClipRects(io.DisplayFramebufferScale);
  198. for (int n = 0; n < data->CmdListsCount; n++)
  199. {
  200. const ImDrawList* cmd_list = data->CmdLists[n];
  201. unsigned int idx_buffer_offset = 0;
  202. // Resize vertex and index buffers on the fly. Once buffer becomes too small for data that is to be rendered
  203. // we reallocate buffer to be twice as big as we need now. This is done in order to minimize memory reallocation
  204. // in rendering loop.
  205. if (cmd_list->VtxBuffer.Size > vertexBuffer_.GetVertexCount())
  206. {
  207. PODVector<VertexElement> elems = {VertexElement(TYPE_VECTOR2, SEM_POSITION),
  208. VertexElement(TYPE_VECTOR2, SEM_TEXCOORD),
  209. VertexElement(TYPE_UBYTE4_NORM, SEM_COLOR)
  210. };
  211. vertexBuffer_.SetSize((unsigned int)(cmd_list->VtxBuffer.Size * 2), elems, true);
  212. }
  213. if (cmd_list->IdxBuffer.Size > indexBuffer_.GetIndexCount())
  214. indexBuffer_.SetSize((unsigned int)(cmd_list->IdxBuffer.Size * 2), false, true);
  215. #if (defined(_WIN32) && !defined(ATOMIC_D3D11) && !defined(ATOMIC_OPENGL)) || defined(ATOMIC_D3D9)
  216. for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
  217. {
  218. ImDrawVert& v = cmd_list->VtxBuffer.Data[i];
  219. v.pos.x += 0.5f;
  220. v.pos.y += 0.5f;
  221. }
  222. #endif
  223. vertexBuffer_.SetDataRange(cmd_list->VtxBuffer.Data, 0, (unsigned int)cmd_list->VtxBuffer.Size, true);
  224. indexBuffer_.SetDataRange(cmd_list->IdxBuffer.Data, 0, (unsigned int)cmd_list->IdxBuffer.Size, true);
  225. _graphics->ClearParameterSources();
  226. _graphics->SetColorWrite(true);
  227. _graphics->SetCullMode(CULL_NONE);
  228. _graphics->SetDepthTest(CMP_ALWAYS);
  229. _graphics->SetDepthWrite(false);
  230. _graphics->SetFillMode(FILL_SOLID);
  231. _graphics->SetStencilTest(false);
  232. _graphics->SetVertexBuffer(&vertexBuffer_);
  233. _graphics->SetIndexBuffer(&indexBuffer_);
  234. for (const ImDrawCmd* cmd = cmd_list->CmdBuffer.begin(); cmd != cmd_list->CmdBuffer.end(); cmd++)
  235. {
  236. if (cmd->UserCallback)
  237. cmd->UserCallback(cmd_list, cmd);
  238. else
  239. {
  240. ShaderVariation* ps;
  241. ShaderVariation* vs;
  242. Texture2D* texture = static_cast<Texture2D*>(cmd->TextureId);
  243. if (!texture)
  244. {
  245. ps = _graphics->GetShader(PS, "Basic", "VERTEXCOLOR");
  246. vs = _graphics->GetShader(VS, "Basic", "VERTEXCOLOR");
  247. }
  248. else
  249. {
  250. // If texture contains only an alpha channel, use alpha shader (for fonts)
  251. vs = _graphics->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
  252. if (texture->GetFormat() == Graphics::GetAlphaFormat())
  253. ps = _graphics->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
  254. else
  255. ps = _graphics->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
  256. }
  257. _graphics->SetShaders(vs, ps);
  258. if (_graphics->NeedParameterUpdate(SP_OBJECT, this))
  259. _graphics->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
  260. if (_graphics->NeedParameterUpdate(SP_CAMERA, this))
  261. _graphics->SetShaderParameter(VSP_VIEWPROJ, projection_);
  262. if (_graphics->NeedParameterUpdate(SP_MATERIAL, this))
  263. _graphics->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
  264. float elapsedTime = GetSubsystem<Time>()->GetElapsedTime();
  265. _graphics->SetShaderParameter(VSP_ELAPSEDTIME, elapsedTime);
  266. _graphics->SetShaderParameter(PSP_ELAPSEDTIME, elapsedTime);
  267. IntRect scissor = IntRect(int(cmd->ClipRect.x * uiScale_), int(cmd->ClipRect.y * uiScale_),
  268. int(cmd->ClipRect.z * uiScale_), int(cmd->ClipRect.w * uiScale_));
  269. _graphics->SetBlendMode(BLEND_ALPHA);
  270. _graphics->SetScissorTest(true, scissor);
  271. _graphics->SetTexture(0, texture);
  272. _graphics->Draw(TRIANGLE_LIST, idx_buffer_offset, cmd->ElemCount, 0, 0,
  273. vertexBuffer_.GetVertexCount());
  274. idx_buffer_offset += cmd->ElemCount;
  275. }
  276. }
  277. }
  278. _graphics->SetScissorTest(false);
  279. }
  280. ImFont* SystemUI::AddFont(const String& font_path, float size, const unsigned short* ranges, bool merge)
  281. {
  282. auto io = ImGui::GetIO();
  283. if (size == 0)
  284. {
  285. if (io.Fonts->Fonts.size() == 0)
  286. return 0;
  287. size = io.Fonts->Fonts.back()->FontSize;
  288. }
  289. if (auto font_file = GetSubsystem<ResourceCache>()->GetFile(font_path))
  290. {
  291. PODVector<uint8_t> data;
  292. data.Resize(font_file->GetSize());
  293. auto bytes_len = font_file->Read(&data.Front(), data.Size());
  294. ImFontConfig cfg;
  295. cfg.MergeMode = merge;
  296. cfg.FontDataOwnedByAtlas = false;
  297. if (auto new_font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(&data.Front(), bytes_len, size, &cfg, ranges))
  298. {
  299. ReallocateFontTexture();
  300. return new_font;
  301. }
  302. }
  303. return 0;
  304. }
  305. ImFont* SystemUI::AddFont(const Atomic::String& font_path, float size,
  306. const std::initializer_list<unsigned short>& ranges, bool merge)
  307. {
  308. return AddFont(font_path, size, ranges.size() ? &*ranges.begin() : 0, merge);
  309. }
  310. void SystemUI::ReallocateFontTexture()
  311. {
  312. auto io = ImGui::GetIO();
  313. // Create font texture.
  314. unsigned char* pixels;
  315. int width, height;
  316. io.Fonts->GetTexDataAsRGBA32(&pixels, &width,
  317. &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
  318. if (!fontTexture_ || width != fontTexture_->GetWidth() || height != fontTexture_->GetHeight())
  319. {
  320. fontTexture_ = new Texture2D(context_);
  321. fontTexture_->SetNumLevels(1);
  322. fontTexture_->SetSize(width, height, Graphics::GetRGBAFormat());
  323. fontTexture_->SetData(0, 0, 0, width, height, pixels);
  324. fontTexture_->SetFilterMode(FILTER_BILINEAR);
  325. // Store our identifier
  326. io.Fonts->TexID = (void*)fontTexture_.Get();
  327. io.Fonts->ClearTexData();
  328. }
  329. }
  330. void SystemUI::SetScale(float scale)
  331. {
  332. if (uiScale_ == scale)
  333. return;
  334. uiScale_ = scale;
  335. UpdateProjectionMatrix();
  336. }
  337. }