Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GUIRenderer.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "GUIRenderer.h"
21 
22 #include "graphics/ShaderManager.h"
24 #include "gui/GUIutil.h"
25 #include "lib/ogl.h"
26 #include "lib/utf8.h"
27 #include "lib/res/h_mgr.h"
28 #include "lib/tex/tex.h"
29 #include "ps/CLogger.h"
30 #include "ps/Filesystem.h"
31 #include "renderer/Renderer.h"
32 
33 
34 using namespace GUIRenderer;
35 
36 
38 {
39 }
40 
41 // DrawCalls needs to be copyable, so it can be used in other copyable types.
42 // But actually copying data is hard, since we'd need to avoid losing track of
43 // who owns various pointers, so instead we just return an empty list.
44 // The list should get filled in again (by GUIRenderer::UpdateDrawCallCache)
45 // before it's used for rendering. (TODO: Is this class actually used safely
46 // in practice?)
47 
49  : std::vector<SDrawCall>()
50 {
51 }
52 
54 {
55  return *this;
56 }
57 
58 
59 void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, const CStr& SpriteName, const CRect &Size, int CellID, std::map<CStr, CGUISprite> &Sprites)
60 {
61  // This is called only when something has changed (like the size of the
62  // sprite), so it doesn't need to be particularly efficient.
63 
64  // Clean up the old data
65  Calls.clear();
66 
67  // If this object has zero size, there's nothing to render. (This happens
68  // with e.g. tooltips that have zero size before they're first drawn, so
69  // it isn't necessarily an error.)
70  if (Size.left == Size.right && Size.top == Size.bottom)
71  return;
72 
73 
74  std::map<CStr, CGUISprite>::iterator it (Sprites.find(SpriteName));
75  if (it == Sprites.end())
76  {
77  // Sprite not found. Check whether this a special sprite:
78  // "stretched:filename.ext" - stretched image
79  // "stretched:grayscale:filename.ext" - stretched grayscale image
80  // "cropped:(0.5, 0.25)" - stretch this ratio (x,y) of the top left of the image
81  // "colour:r g b a" - solid colour
82  //
83  // and if so, try to create it as a new sprite.
84  if (SpriteName.substr(0, 10) == "stretched:")
85  {
86  // TODO: Should check (nicely) that this is a valid file?
87  SGUIImage Image;
88 
89  // Allow grayscale images for disabled portraits
90  if (SpriteName.substr(10, 10) == "grayscale:")
91  {
92  Image.m_TextureName = VfsPath("art/textures/ui") / wstring_from_utf8(SpriteName.substr(20));
93  Image.m_Effects = new SGUIImageEffects;
94  Image.m_Effects->m_Greyscale = true;
95  }
96  else
97  {
98  Image.m_TextureName = VfsPath("art/textures/ui") / wstring_from_utf8(SpriteName.substr(10));
99  }
100 
101  CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
102  Image.m_Size = ca;
103  Image.m_TextureSize = ca;
104 
105  CGUISprite Sprite;
106  Sprite.AddImage(Image);
107 
108  Sprites[SpriteName] = Sprite;
109 
110  it = Sprites.find(SpriteName);
111  ENSURE(it != Sprites.end()); // The insertion above shouldn't fail
112  }
113  else if (SpriteName.substr(0, 8) == "cropped:")
114  {
115  // TODO: Should check (nicely) that this is a valid file?
116  SGUIImage Image;
117 
118  double xRatio = SpriteName.BeforeFirst(",").AfterLast("(").ToDouble();
119  double yRatio = SpriteName.BeforeFirst(")").AfterLast(",").ToDouble();
120 
121  int PathStart = SpriteName.Find(")") + 1;
122 
123  Image.m_TextureName = VfsPath("art/textures/ui") / wstring_from_utf8(SpriteName.substr(PathStart));
124 
125  CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
126  CClientArea cb(CRect(0, 0, 0, 0), CRect(0, 0, 100/xRatio, 100/yRatio));
127  Image.m_Size = ca;
128  Image.m_TextureSize = cb;
129 
130  CGUISprite Sprite;
131  Sprite.AddImage(Image);
132 
133  Sprites[SpriteName] = Sprite;
134 
135  it = Sprites.find(SpriteName);
136  ENSURE(it != Sprites.end()); // The insertion above shouldn't fail
137  }
138  else if (SpriteName.substr(0, 7) == "colour:")
139  {
140  CStrW value = wstring_from_utf8(SpriteName.substr(7));
141  CColor color;
142 
143  // Check colour is valid
144  if (!GUI<CColor>::ParseString(value, color))
145  {
146  LOGERROR(L"GUI: Error parsing sprite 'colour' (\"%ls\")", value.c_str());
147  return;
148  }
149 
150  SGUIImage image;
151 
152  image.m_BackColor = color;
153 
154  CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
155  image.m_Size = ca;
156  image.m_TextureSize = ca;
157 
158  CGUISprite Sprite;
159  Sprite.AddImage(image);
160 
161  Sprites[SpriteName] = Sprite;
162 
163  it = Sprites.find(SpriteName);
164  ENSURE(it != Sprites.end()); // The insertion above shouldn't fail
165  }
166  else
167  {
168  // Otherwise, just complain and give up:
169  LOGERROR(L"Trying to use a sprite that doesn't exist (\"%hs\").", SpriteName.c_str());
170  return;
171  }
172  }
173 
174  Calls.reserve(it->second.m_Images.size());
175 
176  // Iterate through all the sprite's images, loading the texture and
177  // calculating the texture coordinates
178  std::vector<SGUIImage>::const_iterator cit;
179  for (cit = it->second.m_Images.begin(); cit != it->second.m_Images.end(); ++cit)
180  {
181  SDrawCall Call(&*cit); // pointers are safe since we never modify sprites/images after startup
182 
183  CRect ObjectSize = cit->m_Size.GetClientArea(Size);
184 
185  if (ObjectSize.GetWidth() == 0.0 || ObjectSize.GetHeight() == 0.0)
186  {
187  // Zero sized object. Don't report as an error, since it's common for e.g. hitpoint bars.
188  continue; // i.e. don't continue with this image
189  }
190 
191  Call.m_Vertices = ObjectSize;
192  if (cit->m_RoundCoordinates)
193  {
194  // Round the vertex coordinates to integers, to avoid ugly filtering artifacts
195  Call.m_Vertices.left = (int)(Call.m_Vertices.left + 0.5f);
196  Call.m_Vertices.right = (int)(Call.m_Vertices.right + 0.5f);
197  Call.m_Vertices.top = (int)(Call.m_Vertices.top + 0.5f);
198  Call.m_Vertices.bottom = (int)(Call.m_Vertices.bottom + 0.5f);
199  }
200 
201  if (! cit->m_TextureName.empty())
202  {
203  CTextureProperties textureProps(cit->m_TextureName);
204  textureProps.SetWrap(cit->m_WrapMode);
205  CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
206  texture->Prefetch();
207  Call.m_HasTexture = true;
208  Call.m_Texture = texture;
209 
210  Call.m_EnableBlending = false; // will be overridden if the texture has an alpha channel
211 
212  Call.m_ObjectSize = ObjectSize;
213  Call.m_CellID = CellID;
214  }
215  else
216  {
217  Call.m_HasTexture = false;
218  // Enable blending if it's transparent (allowing a little error in the calculations)
219  Call.m_EnableBlending = !(fabs(cit->m_BackColor.a - 1.0f) < 0.0000001f);
220  }
221 
222  Call.m_BackColor = cit->m_BackColor;
223  Call.m_BorderColor = cit->m_Border ? cit->m_BorderColor : CColor();
224  Call.m_DeltaZ = cit->m_DeltaZ;
225 
226  if (!Call.m_HasTexture)
227  {
228  Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
229  }
230  else if (cit->m_Effects)
231  {
232  if (cit->m_Effects->m_AddColor != CColor())
233  {
234  Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_add);
235  Call.m_ShaderColorParameter = cit->m_Effects->m_AddColor;
236  // Always enable blending if something's being subtracted from
237  // the alpha channel
238  if (cit->m_Effects->m_AddColor.a < 0.f)
239  Call.m_EnableBlending = true;
240  }
241  else if (cit->m_Effects->m_Greyscale)
242  {
243  Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_grayscale);
244  }
245  else /* Slight confusion - why no effects? */
246  {
247  Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
248  }
249  }
250  else
251  {
252  Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
253  }
254 
255  Calls.push_back(Call);
256  }
257 }
258 
260 {
261  float TexWidth = m_Texture->GetWidth();
262  float TexHeight = m_Texture->GetHeight();
263 
264  if (!TexWidth || !TexHeight)
265  {
266  return CRect(0, 0, 1, 1);
267  }
268 
269  // Textures are positioned by defining a rectangular block of the
270  // texture (usually the whole texture), and a rectangular block on
271  // the screen. The texture is positioned to make those blocks line up.
272 
273  // Get the screen's position/size for the block
275 
277  BlockScreen.right = BlockScreen.left + BlockScreen.GetHeight() * m_Image->m_FixedHAspectRatio;
278 
279  // Get the texture's position/size for the block:
280  CRect BlockTex;
281 
282  // "real_texture_placement" overrides everything
284  {
285  BlockTex = m_Image->m_TexturePlacementInFile;
286  }
287  // Check whether this sprite has "cell_size" set (and non-zero)
288  else if ((int)m_Image->m_CellSize.cx)
289  {
290  int cols = (int)TexWidth / (int)m_Image->m_CellSize.cx;
291  if (cols == 0)
292  cols = 1; // avoid divide-by-zero
293  int col = m_CellID % cols;
294  int row = m_CellID / cols;
295  BlockTex = CRect(m_Image->m_CellSize.cx*col, m_Image->m_CellSize.cy*row,
296  m_Image->m_CellSize.cx*(col+1), m_Image->m_CellSize.cy*(row+1));
297  }
298  // Use the whole texture
299  else
300  BlockTex = CRect(0, 0, TexWidth, TexHeight);
301 
302  // When rendering, BlockTex will be transformed onto BlockScreen.
303  // Also, TexCoords will be transformed onto ObjectSize (giving the
304  // UV coords at each vertex of the object). We know everything
305  // except for TexCoords, so calculate it:
306 
307  CPos translation (BlockTex.TopLeft()-BlockScreen.TopLeft());
308  float ScaleW = BlockTex.GetWidth()/BlockScreen.GetWidth();
309  float ScaleH = BlockTex.GetHeight()/BlockScreen.GetHeight();
310 
311  CRect TexCoords (
312  // Resize (translating to/from the origin, so the
313  // topleft corner stays in the same place)
315  .Scale(ScaleW, ScaleH)
317  // Translate from BlockTex to BlockScreen
318  + translation
319  );
320 
321  // The tex coords need to be scaled so that (texwidth,texheight) is
322  // mapped onto (1,1)
323  TexCoords.left /= TexWidth;
324  TexCoords.right /= TexWidth;
325  TexCoords.top /= TexHeight;
326  TexCoords.bottom /= TexHeight;
327 
328  return TexCoords;
329 }
330 
331 void GUIRenderer::Draw(DrawCalls &Calls, float Z)
332 {
333  // Called every frame, to draw the object (based on cached calculations)
334 
335  // TODO: batching by shader/texture/etc would be nice
336 
337  CMatrix3D matrix = GetDefaultGuiMatrix();
338 
339  glDisable(GL_BLEND);
340 
341  // Set LOD bias so mipmapped textures are prettier
342 #if CONFIG2_GLES
343 #warning TODO: implement GUI LOD bias for GLES
344 #else
345  glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.f);
346 #endif
347 
348  // Iterate through each DrawCall, and execute whatever drawing code is being called
349  for (DrawCalls::const_iterator cit = Calls.begin(); cit != Calls.end(); ++cit)
350  {
351  cit->m_Shader->BeginPass();
352  CShaderProgramPtr shader = cit->m_Shader->GetShader();
353  shader->Uniform(str_transform, matrix);
354 
355  if (cit->m_HasTexture)
356  {
357  shader->Uniform(str_color, cit->m_ShaderColorParameter);
358  shader->BindTexture(str_tex, cit->m_Texture);
359 
360  if (cit->m_EnableBlending || cit->m_Texture->HasAlpha()) // (shouldn't call HasAlpha before BindTexture)
361  {
362  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
363  glEnable(GL_BLEND);
364  }
365 
366  CRect TexCoords = cit->ComputeTexCoords();
367 
368  // Ensure the quad has the correct winding order, and update texcoords to match
369  CRect Verts = cit->m_Vertices;
370  if (Verts.right < Verts.left)
371  {
372  std::swap(Verts.right, Verts.left);
373  std::swap(TexCoords.right, TexCoords.left);
374  }
375  if (Verts.bottom < Verts.top)
376  {
377  std::swap(Verts.bottom, Verts.top);
378  std::swap(TexCoords.bottom, TexCoords.top);
379  }
380 
381  std::vector<float> data;
382 #define ADD(u, v, x, y, z) STMT(data.push_back(u); data.push_back(v); data.push_back(x); data.push_back(y); data.push_back(z))
383  ADD(TexCoords.left, TexCoords.bottom, Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
384  ADD(TexCoords.right, TexCoords.bottom, Verts.right, Verts.bottom, Z + cit->m_DeltaZ);
385  ADD(TexCoords.right, TexCoords.top, Verts.right, Verts.top, Z + cit->m_DeltaZ);
386 
387  ADD(TexCoords.right, TexCoords.top, Verts.right, Verts.top, Z + cit->m_DeltaZ);
388  ADD(TexCoords.left, TexCoords.top, Verts.left, Verts.top, Z + cit->m_DeltaZ);
389  ADD(TexCoords.left, TexCoords.bottom, Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
390 #undef ADD
391 
392  shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]);
393  shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]);
394  glDrawArrays(GL_TRIANGLES, 0, 6);
395  }
396  else
397  {
398  shader->Uniform(str_color, cit->m_BackColor);
399 
400  if (cit->m_EnableBlending)
401  {
402  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
403  glEnable(GL_BLEND);
404  }
405 
406  // Ensure the quad has the correct winding order
407  CRect Verts = cit->m_Vertices;
408  if (Verts.right < Verts.left)
409  std::swap(Verts.right, Verts.left);
410  if (Verts.bottom < Verts.top)
411  std::swap(Verts.bottom, Verts.top);
412 
413  std::vector<float> data;
414 #define ADD(x, y, z) STMT(data.push_back(x); data.push_back(y); data.push_back(z))
415  ADD(Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
416  ADD(Verts.right, Verts.bottom, Z + cit->m_DeltaZ);
417  ADD(Verts.right, Verts.top, Z + cit->m_DeltaZ);
418 
419  ADD(Verts.right, Verts.top, Z + cit->m_DeltaZ);
420  ADD(Verts.left, Verts.top, Z + cit->m_DeltaZ);
421  ADD(Verts.left, Verts.bottom, Z + cit->m_DeltaZ);
422 
423  shader->VertexPointer(3, GL_FLOAT, 3*sizeof(float), &data[0]);
424  glDrawArrays(GL_TRIANGLES, 0, 6);
425 
426  if (cit->m_BorderColor != CColor())
427  {
428  shader->Uniform(str_color, cit->m_BorderColor);
429 
430  data.clear();
431  ADD(Verts.left + 0.5f, Verts.top + 0.5f, Z + cit->m_DeltaZ);
432  ADD(Verts.right - 0.5f, Verts.top + 0.5f, Z + cit->m_DeltaZ);
433  ADD(Verts.right - 0.5f, Verts.bottom - 0.5f, Z + cit->m_DeltaZ);
434  ADD(Verts.left + 0.5f, Verts.bottom - 0.5f, Z + cit->m_DeltaZ);
435 
436  shader->VertexPointer(3, GL_FLOAT, 3*sizeof(float), &data[0]);
437  glDrawArrays(GL_LINE_LOOP, 0, 4);
438  }
439 #undef ADD
440  }
441 
442  cit->m_Shader->EndPass();
443 
444  glDisable(GL_BLEND);
445  }
446 
447 #if CONFIG2_GLES
448 #warning TODO: implement GUI LOD bias for GLES
449 #else
450  glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.f);
451 #endif
452 }
CRect ComputeTexCoords() const
CMatrix3D GetDefaultGuiMatrix()
Definition: GUIutil.cpp:253
Path VfsPath
VFS path of the form &quot;(dir/)*file?&quot;.
Definition: vfs_path.h:40
#define LOGERROR
Definition: CLogger.h:35
CTexturePtr m_Texture
Definition: GUIRenderer.h:49
float m_FixedHAspectRatio
If non-zero, then the image&#39;s width will be adjusted when rendering so that the width:height ratio eq...
Definition: CGUISprite.h:109
float top
Definition: Overlay.h:159
Definition: Overlay.h:34
float left
Returning CPos representing each corner.
Definition: Overlay.h:159
Represents the filename and GL parameters of a texture, for passing to CTextureManager::CreateTexture...
static void swap(UniqueRange &p1, UniqueRange &p2)
Definition: unique_range.h:176
#define g_Renderer
Definition: Renderer.h:61
float GetWidth() const
Definition: Overlay.cpp:232
Includes static functions that needs one template argument.
Definition: GUIutil.h:103
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
DrawCalls & operator=(const DrawCalls &)
Definition: GUIRenderer.cpp:53
CColor m_BackColor
Definition: CGUISprite.h:126
CClientArea m_Size
Definition: CGUISprite.h:89
void AddImage(const SGUIImage &image)
Adds an image to the sprite collage.
Definition: CGUISprite.h:161
SGUIImageEffects * m_Effects
Definition: CGUISprite.h:123
void UpdateDrawCallCache(DrawCalls &Calls, const CStr &SpriteName, const CRect &Size, int CellID, std::map< CStr, CGUISprite > &Sprites)
Definition: GUIRenderer.cpp:59
CRect m_TexturePlacementInFile
Definition: CGUISprite.h:97
CRect GetClientArea(const CRect &parent) const
Get client area rectangle when the parent is given.
Definition: GUIbase.cpp:46
Made to represent screen positions and delta values.
Definition: Overlay.h:167
VfsPath m_TextureName
Definition: CGUISprite.h:86
std::wstring wstring_from_utf8(const std::string &src, Status *err)
convert UTF-8 to a wide string (UTF-16 or UCS-4, depending on the platform&#39;s wchar_t).
Definition: utf8.cpp:225
A CGUISprite is actually a collage of several real sprites, this struct represents is such real sprit...
Definition: CGUISprite.h:77
The GUI sprite, is actually several real sprites (images) like a collage.
Definition: CGUISprite.h:150
float right
Definition: Overlay.h:159
Definition: Decompose.h:22
#define ADD(u, v, x, y, z)
void Draw(DrawCalls &Calls, float Z)
const SGUIImage * m_Image
Definition: GUIRenderer.h:46
CShaderTechniquePtr m_Shader
Definition: GUIRenderer.h:56
float GetHeight() const
Definition: Overlay.cpp:237
CSize m_CellSize
Definition: CGUISprite.h:103
float cy
Definition: Overlay.h:234
float bottom
Definition: Overlay.h:159
void SetWrap(GLint wrap)
Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
shared_ptr< CShaderProgram > CShaderProgramPtr
CClientArea m_TextureSize
Definition: CGUISprite.h:92
float cx
Size.
Definition: Overlay.h:234
CPos TopLeft() const
Get Position equivalent to top/left corner.
Definition: Overlay.cpp:247
Rectangle class used for screen rectangles.
Definition: Overlay.h:71
Client Area is a rectangle relative to a parent rectangle.
Definition: GUIbase.h:179
shared_ptr< CTexture > CTexturePtr
Definition: Texture.h:22