SystemUI.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. }
  84. SystemUI::~SystemUI()
  85. {
  86. ImGui::Shutdown();
  87. }
  88. void SystemUI::UpdateProjectionMatrix()
  89. {
  90. // Update screen size
  91. auto graphics = GetSubsystem<Graphics>();
  92. ImGui::GetIO().DisplaySize = ImVec2((float)graphics->GetWidth(), (float)graphics->GetHeight());
  93. // Update projection matrix
  94. IntVector2 viewSize = graphics->GetViewport().Size();
  95. Vector2 invScreenSize(1.0f / viewSize.x_, 1.0f / viewSize.y_);
  96. Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
  97. Vector2 offset(-1.0f, 1.0f);
  98. projection_ = Matrix4(Matrix4::IDENTITY);
  99. projection_.m00_ = scale.x_ * uiScale_;
  100. projection_.m03_ = offset.x_;
  101. projection_.m11_ = scale.y_ * uiScale_;
  102. projection_.m13_ = offset.y_;
  103. projection_.m22_ = 1.0f;
  104. projection_.m23_ = 0.0f;
  105. projection_.m33_ = 1.0f;
  106. }
  107. void SystemUI::OnRawEvent(VariantMap& args)
  108. {
  109. auto evt = static_cast<SDL_Event*>(args[SDLRawInput::P_SDLEVENT].Get<void*>());
  110. auto& io = ImGui::GetIO();
  111. switch (evt->type)
  112. {
  113. case SDL_KEYUP:
  114. case SDL_KEYDOWN:
  115. {
  116. auto code = evt->key.keysym.scancode;
  117. auto down = evt->type == SDL_KEYDOWN;
  118. if (code < 512)
  119. io.KeysDown[code] = down;
  120. if (evt->key.keysym.sym == SDLK_LCTRL || evt->key.keysym.sym == SDLK_RCTRL)
  121. io.KeyCtrl = down;
  122. else if (evt->key.keysym.sym == SDLK_LSHIFT || evt->key.keysym.sym == SDLK_RSHIFT)
  123. io.KeyShift = down;
  124. else if (evt->key.keysym.sym == SDLK_LALT || evt->key.keysym.sym == SDLK_RALT)
  125. io.KeyAlt = down;
  126. else if (evt->key.keysym.sym == SDLK_LGUI || evt->key.keysym.sym == SDLK_RGUI)
  127. io.KeySuper = down;
  128. break;
  129. }
  130. case SDL_MOUSEWHEEL:
  131. io.MouseWheel = evt->wheel.y;
  132. break;
  133. case SDL_MOUSEBUTTONUP:
  134. case SDL_MOUSEBUTTONDOWN:
  135. io.MouseDown[evt->button.button - 1] = evt->type == SDL_MOUSEBUTTONDOWN;
  136. case SDL_MOUSEMOTION:
  137. io.MousePos.x = evt->motion.x / uiScale_;
  138. io.MousePos.y = evt->motion.y / uiScale_;
  139. break;
  140. case SDL_FINGERUP:
  141. io.MouseDown[0] = false;
  142. io.MousePos.x = -1;
  143. io.MousePos.y = -1;
  144. case SDL_FINGERDOWN:
  145. io.MouseDown[0] = true;
  146. case SDL_FINGERMOTION:
  147. io.MousePos.x = evt->tfinger.x / uiScale_;
  148. io.MousePos.y = evt->tfinger.y / uiScale_;
  149. break;
  150. case SDL_TEXTINPUT:
  151. ImGui::GetIO().AddInputCharactersUTF8(evt->text.text);
  152. break;
  153. default:
  154. break;
  155. }
  156. switch (evt->type)
  157. {
  158. case SDL_KEYUP:
  159. case SDL_KEYDOWN:
  160. case SDL_TEXTINPUT:
  161. args[SDLRawInput::P_CONSUMED] = ImGui::IsAnyItemActive();
  162. break;
  163. case SDL_MOUSEWHEEL:
  164. case SDL_MOUSEBUTTONUP:
  165. case SDL_MOUSEBUTTONDOWN:
  166. case SDL_MOUSEMOTION:
  167. case SDL_FINGERUP:
  168. case SDL_FINGERDOWN:
  169. case SDL_FINGERMOTION:
  170. args[SDLRawInput::P_CONSUMED] = ImGui::IsMouseHoveringAnyWindow();
  171. break;
  172. default:
  173. break;
  174. }
  175. }
  176. void SystemUI::OnPostUpdate(VariantMap& args)
  177. {
  178. auto& io = ImGui::GetIO();
  179. float timeStep = args[PostUpdate::P_TIMESTEP].GetFloat();
  180. io.DeltaTime = timeStep > 0.0f ? timeStep : 1.0f / 60.0f;
  181. ImGui::NewFrame();
  182. ATOMIC_PROFILE(SystemUiFrame);
  183. SendEvent(E_SYSTEMUIFRAME);
  184. }
  185. void SystemUI::OnRenderDrawLists(ImDrawData* data)
  186. {
  187. auto _graphics = GetSubsystem<Graphics>();
  188. // Engine does not render when window is closed or device is lost
  189. assert(_graphics && _graphics->IsInitialized() && !_graphics->IsDeviceLost());
  190. ImGuiIO& io = ImGui::GetIO();
  191. int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
  192. int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
  193. if (fb_width == 0 || fb_height == 0)
  194. return;
  195. data->ScaleClipRects(io.DisplayFramebufferScale);
  196. for (int n = 0; n < data->CmdListsCount; n++)
  197. {
  198. const ImDrawList* cmd_list = data->CmdLists[n];
  199. unsigned int idx_buffer_offset = 0;
  200. // Resize vertex and index buffers on the fly. Once buffer becomes too small for data that is to be rendered
  201. // we reallocate buffer to be twice as big as we need now. This is done in order to minimize memory reallocation
  202. // in rendering loop.
  203. if (cmd_list->VtxBuffer.Size > vertexBuffer_.GetVertexCount())
  204. {
  205. PODVector<VertexElement> elems = {VertexElement(TYPE_VECTOR2, SEM_POSITION),
  206. VertexElement(TYPE_VECTOR2, SEM_TEXCOORD),
  207. VertexElement(TYPE_UBYTE4_NORM, SEM_COLOR)
  208. };
  209. vertexBuffer_.SetSize((unsigned int)(cmd_list->VtxBuffer.Size * 2), elems, true);
  210. }
  211. if (cmd_list->IdxBuffer.Size > indexBuffer_.GetIndexCount())
  212. indexBuffer_.SetSize((unsigned int)(cmd_list->IdxBuffer.Size * 2), false, true);
  213. #if (defined(_WIN32) && !defined(ATOMIC_D3D11) && !defined(ATOMIC_OPENGL)) || defined(ATOMIC_D3D9)
  214. for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
  215. {
  216. ImDrawVert& v = cmd_list->VtxBuffer.Data[i];
  217. v.pos.x += 0.5f;
  218. v.pos.y += 0.5f;
  219. }
  220. #endif
  221. vertexBuffer_.SetDataRange(cmd_list->VtxBuffer.Data, 0, (unsigned int)cmd_list->VtxBuffer.Size, true);
  222. indexBuffer_.SetDataRange(cmd_list->IdxBuffer.Data, 0, (unsigned int)cmd_list->IdxBuffer.Size, true);
  223. _graphics->ClearParameterSources();
  224. _graphics->SetColorWrite(true);
  225. _graphics->SetCullMode(CULL_NONE);
  226. _graphics->SetDepthTest(CMP_ALWAYS);
  227. _graphics->SetDepthWrite(false);
  228. _graphics->SetFillMode(FILL_SOLID);
  229. _graphics->SetStencilTest(false);
  230. _graphics->SetVertexBuffer(&vertexBuffer_);
  231. _graphics->SetIndexBuffer(&indexBuffer_);
  232. for (const ImDrawCmd* cmd = cmd_list->CmdBuffer.begin(); cmd != cmd_list->CmdBuffer.end(); cmd++)
  233. {
  234. if (cmd->UserCallback)
  235. cmd->UserCallback(cmd_list, cmd);
  236. else
  237. {
  238. ShaderVariation* ps;
  239. ShaderVariation* vs;
  240. Texture2D* texture = static_cast<Texture2D*>(cmd->TextureId);
  241. if (!texture)
  242. {
  243. ps = _graphics->GetShader(PS, "Basic", "VERTEXCOLOR");
  244. vs = _graphics->GetShader(VS, "Basic", "VERTEXCOLOR");
  245. }
  246. else
  247. {
  248. // If texture contains only an alpha channel, use alpha shader (for fonts)
  249. vs = _graphics->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
  250. if (texture->GetFormat() == Graphics::GetAlphaFormat())
  251. ps = _graphics->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
  252. else
  253. ps = _graphics->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
  254. }
  255. _graphics->SetShaders(vs, ps);
  256. if (_graphics->NeedParameterUpdate(SP_OBJECT, this))
  257. _graphics->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
  258. if (_graphics->NeedParameterUpdate(SP_CAMERA, this))
  259. _graphics->SetShaderParameter(VSP_VIEWPROJ, projection_);
  260. if (_graphics->NeedParameterUpdate(SP_MATERIAL, this))
  261. _graphics->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
  262. float elapsedTime = GetSubsystem<Time>()->GetElapsedTime();
  263. _graphics->SetShaderParameter(VSP_ELAPSEDTIME, elapsedTime);
  264. _graphics->SetShaderParameter(PSP_ELAPSEDTIME, elapsedTime);
  265. IntRect scissor = IntRect(int(cmd->ClipRect.x * uiScale_), int(cmd->ClipRect.y * uiScale_),
  266. int(cmd->ClipRect.z * uiScale_), int(cmd->ClipRect.w * uiScale_));
  267. _graphics->SetBlendMode(BLEND_ALPHA);
  268. _graphics->SetScissorTest(true, scissor);
  269. _graphics->SetTexture(0, texture);
  270. _graphics->Draw(TRIANGLE_LIST, idx_buffer_offset, cmd->ElemCount, 0, 0,
  271. vertexBuffer_.GetVertexCount());
  272. idx_buffer_offset += cmd->ElemCount;
  273. }
  274. }
  275. }
  276. _graphics->SetScissorTest(false);
  277. }
  278. ImFont* SystemUI::AddFont(const String& font_path, float size, const unsigned short* ranges, bool merge)
  279. {
  280. auto io = ImGui::GetIO();
  281. if (size == 0)
  282. {
  283. if (io.Fonts->Fonts.size() == 0)
  284. return 0;
  285. size = io.Fonts->Fonts.back()->FontSize;
  286. }
  287. if (auto font_file = GetSubsystem<ResourceCache>()->GetFile(font_path))
  288. {
  289. PODVector<uint8_t> data;
  290. data.Resize(font_file->GetSize());
  291. auto bytes_len = font_file->Read(&data.Front(), data.Size());
  292. ImFontConfig cfg;
  293. cfg.MergeMode = merge;
  294. cfg.FontDataOwnedByAtlas = false;
  295. if (auto new_font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(&data.Front(), bytes_len, size, &cfg, ranges))
  296. {
  297. ReallocateFontTexture();
  298. return new_font;
  299. }
  300. }
  301. return 0;
  302. }
  303. ImFont* SystemUI::AddFont(const Atomic::String& font_path, float size,
  304. const std::initializer_list<unsigned short>& ranges, bool merge)
  305. {
  306. return AddFont(font_path, size, ranges.size() ? &*ranges.begin() : 0, merge);
  307. }
  308. void SystemUI::ReallocateFontTexture()
  309. {
  310. auto io = ImGui::GetIO();
  311. // Create font texture.
  312. unsigned char* pixels;
  313. int width, height;
  314. io.Fonts->GetTexDataAsRGBA32(&pixels, &width,
  315. &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
  316. if (!fontTexture_ || width != fontTexture_->GetWidth() || height != fontTexture_->GetHeight())
  317. {
  318. fontTexture_ = new Texture2D(context_);
  319. fontTexture_->SetNumLevels(1);
  320. fontTexture_->SetSize(width, height, Graphics::GetRGBAFormat());
  321. fontTexture_->SetData(0, 0, 0, width, height, pixels);
  322. fontTexture_->SetFilterMode(FILTER_BILINEAR);
  323. // Store our identifier
  324. io.Fonts->TexID = (void*)fontTexture_.Get();
  325. io.Fonts->ClearTexData();
  326. }
  327. }
  328. void SystemUI::SetScale(float scale)
  329. {
  330. if (uiScale_ == scale)
  331. return;
  332. uiScale_ = scale;
  333. UpdateProjectionMatrix();
  334. }
  335. void SystemUI::CreateConsoleAndDebugHud()
  336. {
  337. Console* console = new Console(context_);
  338. DebugHud* debugHud = new DebugHud(context_);
  339. // Create console & debug hud
  340. context_->RegisterSubsystem(console);
  341. context_->RegisterSubsystem(debugHud);
  342. }
  343. }