1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details 2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt 3 4 #include "Gui.h" 5 6 #include "graphics/Renderer.h" 7 #include "text/TextSupport.h" 8 #include "vector3.h" // for projection 9 10 namespace Gui { 11 12 bool Screen::initted = false; 13 int Screen::width; 14 int Screen::height; 15 int Screen::realWidth; 16 int Screen::realHeight; 17 float Screen::invRealWidth; 18 float Screen::invRealHeight; 19 float Screen::fontScale[2]; 20 std::list<Widget *> Screen::kbshortcut_widgets; 21 Gui::Fixed *Screen::baseContainer; 22 Gui::Widget *Screen::focusedWidget; 23 matrix4x4f Screen::modelMatrix; 24 matrix4x4f Screen::projMatrix; 25 Graphics::Viewport Screen::viewport; 26 27 FontCache Screen::s_fontCache; 28 std::stack<RefCountedPtr<Text::TextureFont>> Screen::s_fontStack; 29 RefCountedPtr<Text::TextureFont> Screen::s_defaultFont; 30 31 Graphics::Renderer *Screen::s_renderer; 32 Graphics::RenderState *Screen::alphaBlendState = nullptr; 33 Graphics::Material *Screen::flatColorMaterial = nullptr; 34 Init(Graphics::Renderer * renderer,int real_width,int real_height,int ui_width,int ui_height)35 void Screen::Init(Graphics::Renderer *renderer, int real_width, int real_height, int ui_width, int ui_height) 36 { 37 s_renderer = renderer; 38 39 Screen::width = ui_width; 40 Screen::height = ui_height; 41 Screen::realWidth = real_width; 42 Screen::realHeight = real_height; 43 Screen::invRealWidth = 1.0f / real_width; 44 Screen::invRealHeight = 1.0f / real_height; 45 Screen::initted = true; 46 // Why? because although our font textures get bigger with screen 47 // resolution, our Gui Ortho projection is still 800x600 so vertex 48 // coords must be scaled. 49 Screen::fontScale[0] = ui_width / float(real_width); 50 Screen::fontScale[1] = ui_height / float(real_height); 51 s_defaultFont = s_fontCache.GetTextureFont("GuiFont"); 52 PushFont(s_defaultFont); 53 Screen::baseContainer = new Gui::Fixed(); 54 Screen::baseContainer->SetSize(float(Screen::width), float(Screen::height)); 55 Screen::baseContainer->Show(); 56 57 Graphics::RenderStateDesc rsd; 58 rsd.blendMode = Graphics::BLEND_ALPHA; 59 rsd.depthWrite = false; 60 alphaBlendState = renderer->CreateRenderState(rsd); 61 62 Graphics::MaterialDescriptor mdesc; 63 flatColorMaterial = renderer->CreateMaterial(mdesc); 64 } 65 Uninit()66 void Screen::Uninit() 67 { 68 Screen::baseContainer->RemoveAllChildren(); // children deleted elsewhere? 69 delete Screen::baseContainer; 70 delete flatColorMaterial; 71 } 72 73 static sigc::connection _focusedWidgetOnDelete; 74 OnDeleteFocusedWidget()75 void Screen::OnDeleteFocusedWidget() 76 { 77 _focusedWidgetOnDelete.disconnect(); 78 focusedWidget = 0; 79 } 80 SetFocused(Widget * w,bool enableKeyRepeat)81 void Screen::SetFocused(Widget *w, bool enableKeyRepeat) 82 { 83 ClearFocus(); 84 _focusedWidgetOnDelete = w->onDelete.connect(sigc::ptr_fun(&Screen::OnDeleteFocusedWidget)); 85 focusedWidget = w; 86 } 87 ClearFocus()88 void Screen::ClearFocus() 89 { 90 if (!focusedWidget) return; 91 _focusedWidgetOnDelete.disconnect(); 92 focusedWidget = 0; 93 } 94 Project(const vector3d & in,vector3d & out)95 bool Screen::Project(const vector3d &in, vector3d &out) 96 { 97 PROFILE_SCOPED() 98 // implements gluProject (see the OpenGL documentation or the Mesa implementation of gluProject) 99 const vector3d vcam = matrix4x4d(modelMatrix) * in; 100 const double w = vcam.z * projMatrix[11] + projMatrix[15]; 101 102 // convert view coordinates -> homogeneous coordinates -> NDC 103 // perspective divide is applied last (left-to-right associativity) 104 const vector3d vNDC = matrix4x4d(projMatrix) * vcam / w; 105 106 // convert -1..1 NDC to 0..1 viewport coordinates 107 const vector3d vVP = { 108 vNDC.x * 0.5 + 0.5, 109 vNDC.y * 0.5 + 0.5, 110 -vNDC.z // undo reverse-Z coordinate flip 111 }; 112 113 // viewport coord * size + position 114 out.x = vVP.x * viewport.w + viewport.x; 115 out.y = vVP.y * viewport.h + viewport.y; 116 out.z = vVP.z; 117 118 // map to pixels 119 out.x = out.x * width * invRealWidth; 120 out.y = GetHeight() - out.y * height * invRealHeight; 121 return true; 122 } 123 EnterOrtho()124 void Screen::EnterOrtho() 125 { 126 PROFILE_SCOPED() 127 128 Graphics::Renderer *r = GetRenderer(); 129 130 modelMatrix = r->GetTransform(); 131 projMatrix = r->GetProjection(); 132 133 viewport = r->GetViewport(); 134 r->SetOrthographicProjection(0, width, height, 0, 0, 1); 135 r->SetTransform(matrix4x4f::Identity()); 136 } 137 LeaveOrtho()138 void Screen::LeaveOrtho() 139 { 140 PROFILE_SCOPED() 141 142 Graphics::Renderer *r = GetRenderer(); 143 144 r->SetProjection(projMatrix); 145 r->SetTransform(modelMatrix); 146 } 147 Draw()148 void Screen::Draw() 149 { 150 PROFILE_SCOPED() 151 assert(Screen::initted); 152 EnterOrtho(); 153 baseContainer->Draw(); 154 LeaveOrtho(); 155 } 156 IsBaseWidget(const Widget * w)157 bool Screen::IsBaseWidget(const Widget *w) 158 { 159 PROFILE_SCOPED() 160 return w == static_cast<const Widget *>(baseContainer); 161 } 162 AddBaseWidget(Widget * w,int x,int y)163 void Screen::AddBaseWidget(Widget *w, int x, int y) 164 { 165 baseContainer->Add(w, float(x), float(y)); 166 } 167 RemoveBaseWidget(Widget * w)168 void Screen::RemoveBaseWidget(Widget *w) 169 { 170 baseContainer->Remove(w); 171 } 172 SDLEventCoordToScreenCoord(int sdlev_x,int sdlev_y,float * x,float * y)173 void Screen::SDLEventCoordToScreenCoord(int sdlev_x, int sdlev_y, float *x, float *y) 174 { 175 *y = sdlev_y * height * invRealHeight; 176 *x = sdlev_x * width * invRealWidth; 177 } 178 OnMouseMotion(SDL_MouseMotionEvent * e)179 void Screen::OnMouseMotion(SDL_MouseMotionEvent *e) 180 { 181 MouseMotionEvent ev; 182 float x, y; 183 Screen::SDLEventCoordToScreenCoord(e->x, e->y, &x, &y); 184 ev.screenX = ev.x = x; 185 ev.screenY = ev.y = y; 186 baseContainer->OnMouseMotion(&ev); 187 ev.screenX = ev.x = x; 188 ev.screenY = ev.y = y; 189 RawEvents::onMouseMotion.emit(&ev); 190 } 191 OnClick(SDL_MouseButtonEvent * e)192 void Screen::OnClick(SDL_MouseButtonEvent *e) 193 { 194 MouseButtonEvent ev; 195 float x, y; 196 Screen::SDLEventCoordToScreenCoord(e->x, e->y, &x, &y); 197 ev.button = e->button; 198 ev.isdown = (e->type == SDL_MOUSEBUTTONDOWN); 199 ev.screenX = ev.x = x; 200 ev.screenY = ev.y = y; 201 if (ev.isdown) { 202 baseContainer->OnMouseDown(&ev); 203 RawEvents::onMouseDown.emit(&ev); 204 } else { 205 baseContainer->OnMouseUp(&ev); 206 RawEvents::onMouseUp.emit(&ev); 207 } 208 } 209 OnKeyDown(const SDL_Keysym * sym)210 void Screen::OnKeyDown(const SDL_Keysym *sym) 211 { 212 if (focusedWidget) { 213 bool accepted = focusedWidget->OnKeyDown(sym); 214 // don't check shortcuts if the focused widget accepted the key-press 215 if (accepted) 216 return; 217 } 218 for (std::list<Widget *>::iterator i = kbshortcut_widgets.begin(); i != kbshortcut_widgets.end(); ++i) { 219 if (!(*i)->IsVisible()) continue; 220 if (!(*i)->GetEnabled()) continue; 221 (*i)->OnPreShortcut(sym); 222 } 223 } 224 OnKeyUp(const SDL_Keysym * sym)225 void Screen::OnKeyUp(const SDL_Keysym *sym) 226 { 227 } 228 OnTextInput(const SDL_TextInputEvent * e)229 void Screen::OnTextInput(const SDL_TextInputEvent *e) 230 { 231 if (!focusedWidget) return; 232 Uint32 unicode; 233 Text::utf8_decode_char(&unicode, e->text); 234 focusedWidget->OnTextInput(unicode); 235 } 236 GetFontHeight(Text::TextureFont * font)237 float Screen::GetFontHeight(Text::TextureFont *font) 238 { 239 if (!font) font = GetFont().Get(); 240 241 return font->GetHeight() * fontScale[1]; 242 } 243 GetFontDescender(Text::TextureFont * font)244 float Screen::GetFontDescender(Text::TextureFont *font) 245 { 246 if (!font) font = GetFont().Get(); 247 248 return font->GetDescender() * fontScale[1]; 249 } 250 MeasureString(const std::string & s,float & w,float & h,Text::TextureFont * font)251 void Screen::MeasureString(const std::string &s, float &w, float &h, Text::TextureFont *font) 252 { 253 if (!font) font = GetFont().Get(); 254 assert(font); 255 256 font->MeasureString(s.c_str(), w, h); 257 w *= fontScale[0]; 258 h *= fontScale[1]; 259 } 260 MeasureCharacterPos(const std::string & s,int charIndex,float & x,float & y,Text::TextureFont * font)261 void Screen::MeasureCharacterPos(const std::string &s, int charIndex, float &x, float &y, Text::TextureFont *font) 262 { 263 assert((charIndex >= 0) && (charIndex <= int(s.size()))); 264 265 if (!font) font = GetFont().Get(); 266 assert(font); 267 268 font->MeasureCharacterPos(s.c_str(), charIndex, x, y); 269 x *= fontScale[0]; 270 y *= fontScale[1]; 271 } 272 PickCharacterInString(const std::string & s,float x,float y,Text::TextureFont * font)273 int Screen::PickCharacterInString(const std::string &s, float x, float y, Text::TextureFont *font) 274 { 275 if (!font) font = GetFont().Get(); 276 assert(font); 277 278 x /= fontScale[0]; 279 y /= fontScale[1]; 280 281 return font->PickCharacter(s.c_str(), x, y); 282 } 283 RenderStringBuffer(RefCountedPtr<Graphics::VertexBuffer> vb,const std::string & s,float xoff,float yoff,const Color & color,Text::TextureFont * font)284 void Screen::RenderStringBuffer(RefCountedPtr<Graphics::VertexBuffer> vb, const std::string &s, float xoff, float yoff, const Color &color, Text::TextureFont *font) 285 { 286 PROFILE_SCOPED() 287 if (!font) font = GetFont().Get(); 288 289 Graphics::Renderer *r = Gui::Screen::GetRenderer(); 290 291 const matrix4x4f &modelMatrix_ = r->GetTransform(); 292 Graphics::Renderer::MatrixTicket ticket(r); 293 294 const float x = modelMatrix_[12] + xoff; 295 const float y = modelMatrix_[13] + yoff; 296 297 matrix4x4f modelView = matrix4x4f::Identity(); 298 modelView.Translate(floor(x / Screen::fontScale[0]) * Screen::fontScale[0], floor(y / Screen::fontScale[1]) * Screen::fontScale[1], 0); 299 modelView.Scale(Screen::fontScale[0], Screen::fontScale[1], 1); 300 r->SetTransform(modelView); 301 302 // temporary, owned by the font 303 Graphics::VertexBuffer *pVB = font->GetCachedVertexBuffer(s); 304 if (pVB) { 305 // found the buffer 306 font->RenderBuffer(pVB, color); 307 } else { 308 Graphics::VertexArray va(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0); 309 font->PopulateString(va, s.c_str(), 0, 0, color); 310 311 if (va.GetNumVerts() > 0) { 312 if (!vb.Valid() || vb->GetCapacity() < va.GetNumVerts()) { 313 vb.Reset(font->CreateVertexBuffer(va, s, true)); 314 } 315 316 vb->Populate(va); 317 318 font->RenderBuffer(vb.Get(), color); 319 } 320 } 321 } 322 RenderMarkupBuffer(RefCountedPtr<Graphics::VertexBuffer> vb,const std::string & s,const Color & color,Text::TextureFont * font)323 void Screen::RenderMarkupBuffer(RefCountedPtr<Graphics::VertexBuffer> vb, const std::string &s, const Color &color, Text::TextureFont *font) 324 { 325 PROFILE_SCOPED() 326 if (!font) font = GetFont().Get(); 327 328 Graphics::Renderer *r = Gui::Screen::GetRenderer(); 329 330 const matrix4x4f &modelMatrix_ = r->GetTransform(); 331 Graphics::Renderer::MatrixTicket ticket(r); 332 333 const float x = modelMatrix_[12]; 334 const float y = modelMatrix_[13]; 335 336 matrix4x4f modelView = matrix4x4f::Identity(); 337 modelView.Translate(floor(x / Screen::fontScale[0]) * Screen::fontScale[0], floor(y / Screen::fontScale[1]) * Screen::fontScale[1], 0); 338 modelView.Scale(Screen::fontScale[0], Screen::fontScale[1], 1); 339 r->SetTransform(modelView); 340 341 // temporary, owned by the font 342 Graphics::VertexBuffer *pVB = font->GetCachedVertexBuffer(s); 343 if (pVB) { 344 // found the buffer 345 font->RenderBuffer(pVB, color); 346 } else { 347 Graphics::VertexArray va(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0); 348 font->PopulateMarkup(va, s.c_str(), 0, 0, color); 349 350 if (va.GetNumVerts() > 0) { 351 if (!vb.Valid() || vb->GetCapacity() < va.GetNumVerts()) { 352 vb.Reset(font->CreateVertexBuffer(va, s, true)); 353 } 354 355 vb->Populate(va); 356 357 font->RenderBuffer(vb.Get()); 358 } 359 } 360 } 361 AddShortcutWidget(Widget * w)362 void Screen::AddShortcutWidget(Widget *w) 363 { 364 kbshortcut_widgets.push_back(w); 365 } 366 RemoveShortcutWidget(Widget * w)367 void Screen::RemoveShortcutWidget(Widget *w) 368 { 369 kbshortcut_widgets.remove(w); 370 } 371 372 } // namespace Gui 373