SystemUI.cpp 14 KB

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