1 /* GG is a GUI for OpenGL. 2 Copyright (C) 2003-2008 T. Zachary Laine 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Lesser General Public License 6 as published by the Free Software Foundation; either version 2.1 7 of the License, or (at your option) any later version. 8 9 This library 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 GNU 12 Lesser General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with this library; if not, write to the Free 16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 17 02111-1307 USA 18 19 If you do not wish to comply with the terms of the LGPL please 20 contact the author as other terms are available for a fee. 21 22 Zach Laine 23 whatwasthataddress@gmail.com */ 24 25 #include <GG/DrawUtil.h> 26 27 #include <GG/ClrConstants.h> 28 #include <GG/GUI.h> 29 #include <GG/GLClientAndServerBuffer.h> 30 31 32 #include <valarray> 33 34 namespace { // file-scope constants and functions 35 using namespace GG; 36 37 const double PI = 3.14159426; 38 const double SQRT2OVER2 = std::sqrt(2.0) / 2.0; 39 40 /// a stack of the currently-active clipping rects, in GG coordinates, not OpenGL scissor coordinates 41 std::vector<Rect> g_scissor_clipping_rects; 42 43 GLboolean g_prev_color_writemask[4]; 44 GLboolean g_prev_depth_writemask; 45 46 /// the index of the next stencil bit to use for stencil clipping 47 unsigned int g_stencil_bit = 0; 48 49 /// whenever points on the unit circle are calculated with expensive sin() and cos() calls, the results are cached here 50 std::map<int, std::valarray<double>> unit_circle_coords; 51 /// this doesn't serve as a cache, but does allow us to prevent numerous constructions and destructions of Clr valarrays. 52 std::map<int, std::valarray<Clr>> color_arrays; 53 Rectangle(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int bevel_thick,bool bevel_left,bool bevel_top,bool bevel_right,bool bevel_bottom)54 void Rectangle(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2, unsigned int bevel_thick, 55 bool bevel_left, bool bevel_top, bool bevel_right, bool bevel_bottom) 56 { 57 X inner_x1 = ul.x + (bevel_left ? static_cast<int>(bevel_thick) : 0); 58 Y inner_y1 = ul.y + (bevel_top ? static_cast<int>(bevel_thick) : 0); 59 X inner_x2 = lr.x - (bevel_right ? static_cast<int>(bevel_thick) : 0); 60 Y inner_y2 = lr.y - (bevel_bottom ? static_cast<int>(bevel_thick) : 0); 61 62 GL2DVertexBuffer verts; 63 verts.reserve(14); 64 verts.store(inner_x2, inner_y1); 65 verts.store(lr.x, ul.y); 66 verts.store(inner_x1, inner_y1); 67 verts.store(ul.x, ul.y); 68 verts.store(inner_x1, inner_y2); 69 verts.store(ul.x, lr.y); 70 verts.store(inner_x2, inner_y2); 71 verts.store(lr.x, lr.y); 72 verts.store(inner_x2, inner_y1); 73 verts.store(lr.x, ul.y); 74 75 verts.store(inner_x2, inner_y1); 76 verts.store(inner_x1, inner_y1); 77 verts.store(inner_x1, inner_y2); 78 verts.store(inner_x2, inner_y2); 79 80 verts.activate(); 81 82 glDisable(GL_TEXTURE_2D); 83 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 84 glEnableClientState(GL_VERTEX_ARRAY); 85 glDisableClientState(GL_COLOR_ARRAY); 86 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 87 88 // draw beveled edges 89 if (bevel_thick && (border_color1 != CLR_ZERO || border_color2 != CLR_ZERO)) { 90 glColor(border_color1); 91 if (border_color1 == border_color2) { 92 glDrawArrays(GL_QUAD_STRIP, 0, 10); 93 } else { 94 glDrawArrays(GL_QUAD_STRIP, 0, 6); 95 glColor(border_color2); 96 glDrawArrays(GL_QUAD_STRIP, 4, 6); 97 } 98 } 99 100 // draw interior of rectangle 101 if (color != CLR_ZERO) { 102 glColor(color); 103 glDrawArrays(GL_QUADS, 10, 4); 104 } 105 106 glPopClientAttrib(); 107 glEnable(GL_TEXTURE_2D); 108 } 109 Check(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3)110 void Check(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3) 111 { 112 X wd = lr.x - ul.x; 113 Y ht = lr.y - ul.y; 114 115 // all vertices 116 GLfloat verts[][2] = {{-0.2f, 0.2f}, {-0.6f, -0.2f}, {-0.6f, 0.0f}, {-0.2f, 0.4f}, {-0.8f, 0.0f}, 117 { -0.2f, 0.6f}, { 0.8f, -0.4f}, { 0.6f, -0.4f}, { 0.8f, -0.8f}}; 118 119 glPushMatrix(); 120 const float sf = 1.25f; // scale factor to make the check look right 121 glTranslatef(Value(ul.x + wd / 2.0f), Value(ul.y + ht / 2.0f * sf), 0.0f); // move origin to the center of the rectangle 122 glScalef(Value(wd / 2.0f * sf), Value(ht / 2.0f * sf), 1.0f); // map the range [-1,1] to the rectangle in both directions 123 124 static std::size_t indices[22] = { 1, 4, 2, 125 8, 0, 3, 7, 126 2, 4, 5, 3, 7, 3, 5, 6, 127 8, 7, 6, 128 0, 1, 2, 3}; 129 130 GL2DVertexBuffer vert_buf; 131 vert_buf.reserve(22); 132 for (std::size_t i = 0; i < 22; ++i) 133 vert_buf.store(verts[indices[i]][0], verts[indices[i]][1]); 134 135 glDisable(GL_TEXTURE_2D); 136 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 137 glEnableClientState(GL_VERTEX_ARRAY); 138 glDisableClientState(GL_COLOR_ARRAY); 139 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 140 141 vert_buf.activate(); 142 143 glColor(color3); 144 glDrawArrays(GL_TRIANGLES, 0, 3); 145 glDrawArrays(GL_QUADS, 3, 4); 146 147 glColor(color2); 148 glDrawArrays(GL_QUADS, 7, 8); 149 150 glColor(color1); 151 glDrawArrays(GL_TRIANGLES, 15, 3); 152 glDrawArrays(GL_QUADS, 18, 4); 153 154 glPopClientAttrib(); 155 156 glPopMatrix(); 157 glEnable(GL_TEXTURE_2D); 158 } 159 XMark(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3)160 void XMark(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3) 161 { 162 X wd = lr.x - ul.x; 163 Y ht = lr.y - ul.y; 164 glDisable(GL_TEXTURE_2D); 165 166 // all vertices 167 GLfloat verts[][2] = {{-0.4f, -0.6f}, {-0.6f, -0.4f}, {-0.4f, -0.4f}, {-0.2f, 0.0f}, {-0.6f, 0.4f}, 168 {-0.4f, 0.6f}, {-0.4f, 0.4f}, { 0.0f, 0.2f}, { 0.4f, 0.6f}, { 0.6f, 0.4f}, 169 { 0.4f, 0.4f}, { 0.2f, 0.0f}, { 0.6f, -0.4f}, { 0.4f, -0.6f}, { 0.4f, -0.4f}, 170 { 0.0f, -0.2f}, { 0.0f, 0.0f}}; 171 172 glPushMatrix(); 173 const float sf = 1.75f; // scale factor; the check wasn't the right size as drawn originally 174 glTranslatef(Value(ul.x + wd / 2.0f), Value(ul.y + ht / 2.0f), 0.0f); // move origin to the center of the rectangle 175 glScalef(Value(wd / 2.0f * sf), Value(ht / 2.0f * sf), 1.0f); // map the range [-1,1] to the rectangle in both directions 176 177 static std::size_t indices[44] = {12, 13, 14, 178 15, 0, 2, 16, 9, 11, 16, 10, 179 0, 1, 2, 180 13, 15, 16, 14, 3, 4, 6, 16, 181 4, 5, 6, 8, 9, 10, 182 14, 16, 11, 12, 2, 1, 3, 16, 16, 6, 5, 7, 16, 7, 8, 10}; 183 184 GL2DVertexBuffer vert_buf; 185 vert_buf.reserve(44); 186 for (std::size_t i = 0; i < 44; ++i) 187 vert_buf.store(verts[indices[i]][0], verts[indices[i]][1]); 188 189 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 190 glEnableClientState(GL_VERTEX_ARRAY); 191 glDisableClientState(GL_COLOR_ARRAY); 192 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 193 194 vert_buf.activate(); 195 196 glColor(color1); 197 glDrawArrays(GL_TRIANGLES, 0, 3); 198 glDrawArrays(GL_QUADS, 3, 8); 199 200 glColor(color2); 201 glDrawArrays(GL_TRIANGLES, 11, 3); 202 glDrawArrays(GL_QUADS, 14, 8); 203 204 glColor(color3); 205 glDrawArrays(GL_TRIANGLES, 22, 6); 206 glDrawArrays(GL_QUADS, 28, 16); 207 208 glPopClientAttrib(); 209 210 glPopMatrix(); 211 glEnable(GL_TEXTURE_2D); 212 } 213 BubbleArc(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3,double theta1,double theta2)214 void BubbleArc(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3, double theta1, double theta2) 215 { 216 X wd = lr.x - ul.x; 217 Y ht = lr.y - ul.y; 218 glDisable(GL_TEXTURE_2D); 219 220 // correct theta* values to range [0, 2pi) 221 if (theta1 < 0) 222 theta1 += (int(-theta1 / (2 * PI)) + 1) * 2 * PI; 223 else if (theta1 >= 2 * PI) 224 theta1 -= int(theta1 / (2 * PI)) * 2 * PI; 225 if (theta2 < 0) 226 theta2 += (int(-theta2 / (2 * PI)) + 1) * 2 * PI; 227 else if (theta2 >= 2 * PI) 228 theta2 -= int(theta2 / (2 * PI)) * 2 * PI; 229 230 const int SLICES = std::min(3 + std::max(Value(wd), Value(ht)), 50); // this is a good guess at how much to tesselate the circle coordinates (50 segments max) 231 const double HORZ_THETA = (2 * PI) / SLICES; 232 233 std::valarray<double>& unit_vertices = unit_circle_coords[SLICES]; 234 std::valarray<Clr>& colors = color_arrays[SLICES]; 235 bool calc_vertices = unit_vertices.size() == 0; 236 if (calc_vertices) { 237 unit_vertices.resize(2 * (SLICES + 1), 0.0); 238 double theta = 0.0f; 239 for (int j = 0; j <= SLICES; theta += HORZ_THETA, ++j) { // calculate x,y values for each point on a unit circle divided into SLICES arcs 240 unit_vertices[j*2] = cos(-theta); 241 unit_vertices[j*2+1] = sin(-theta); 242 } 243 colors.resize(SLICES + 1, Clr()); // create but don't initialize (this is essentially just scratch space, since the colors are different call-to-call) 244 } 245 int first_slice_idx = int(theta1 / HORZ_THETA + 1); 246 int last_slice_idx = int(theta2 / HORZ_THETA - 1); 247 if (theta1 >= theta2) 248 last_slice_idx += SLICES; 249 for (int j = first_slice_idx; j <= last_slice_idx; ++j) { // calculate the color value for each needed point 250 int X = (j > SLICES ? (j - SLICES) : j) * 2, Y = X + 1; 251 double color_scale_factor = (SQRT2OVER2 * (unit_vertices[X] + unit_vertices[Y]) + 1) / 2; // this is essentially the dot product of (x,y) with (sqrt2over2,sqrt2over2), the direction of the light source, scaled to the range [0,1] 252 colors[j] = BlendClr(color2, color3, color_scale_factor); 253 } 254 255 glPushMatrix(); 256 glTranslatef(Value(ul.x + wd / 2.0), Value(ul.y + ht / 2.0), 0.0); // move origin to the center of the rectangle 257 glScalef(Value(wd / 2.0), Value(ht / 2.0), 1.0); // map the range [-1,1] to the rectangle in both (x- and y-) directions 258 259 glColor(color1); 260 glBegin(GL_TRIANGLE_FAN); 261 glVertex2f(0, 0); 262 // point on circle at angle theta1 263 double x = cos(-theta1), 264 y = sin(-theta1); 265 double color_scale_factor = (SQRT2OVER2 * (x + y) + 1) / 2; 266 Clr clr = BlendClr(color2, color3, color_scale_factor); 267 glColor4ub(clr.r, clr.g, clr.b, clr.a); 268 glVertex2f(x, y); 269 // angles in between theta1 and theta2, if any 270 for (int i = first_slice_idx; i <= last_slice_idx; ++i) { 271 int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1; 272 glColor(colors[i]); 273 glVertex2f(unit_vertices[X], unit_vertices[Y]); 274 } 275 // theta2 276 x = cos(-theta2); 277 y = sin(-theta2); 278 color_scale_factor = (SQRT2OVER2 * (x + y) + 1) / 2; 279 clr = BlendClr(color2, color3, color_scale_factor); 280 glColor4ub(clr.r, clr.g, clr.b, clr.a); 281 glVertex2f(x, y); 282 glEnd(); 283 glPopMatrix(); 284 glEnable(GL_TEXTURE_2D); 285 } 286 CircleArc(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int bevel_thick,double theta1,double theta2)287 void CircleArc(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2, 288 unsigned int bevel_thick, double theta1, double theta2) 289 { 290 //std::cout << "GG::CircleArc ul: " << ul << " lr: " << lr << " bevel thick: " << bevel_thick << " theta1: " << theta1 << " theta2: " << theta2 << std::endl; 291 X wd = lr.x - ul.x; 292 Y ht = lr.y - ul.y; 293 glDisable(GL_TEXTURE_2D); 294 295 // correct theta* values to range [0, 2pi) 296 if (theta1 < 0) 297 theta1 += (int(-theta1 / (2 * PI)) + 1) * 2 * PI; 298 else if (theta1 >= 2 * PI) 299 theta1 -= int(theta1 / (2 * PI)) * 2 * PI; 300 if (theta2 < 0) 301 theta2 += (int(-theta2 / (2 * PI)) + 1) * 2 * PI; 302 else if (theta2 >= 2 * PI) 303 theta2 -= int(theta2 / (2 * PI)) * 2 * PI; 304 305 const int SLICES = std::min(3 + std::max(Value(wd), Value(ht)), 50); // this is a good guess at how much to tesselate the circle coordinates (50 segments max) 306 const double HORZ_THETA = (2 * PI) / SLICES; 307 308 std::valarray<double>& unit_vertices = unit_circle_coords[SLICES]; 309 std::valarray<Clr>& colors = color_arrays[SLICES]; 310 bool calc_vertices = unit_vertices.size() == 0; 311 if (calc_vertices) { 312 unit_vertices.resize(2 * (SLICES + 1), 0.0); 313 double theta = 0.0f; 314 for (int j = 0; j <= SLICES; theta += HORZ_THETA, ++j) { // calculate x,y values for each point on a unit circle divided into SLICES arcs 315 unit_vertices[j*2] = cos(-theta); 316 unit_vertices[j*2+1] = sin(-theta); 317 } 318 colors.resize(SLICES + 1, Clr()); // create but don't initialize (this is essentially just scratch space, since the colors are different call-to-call) 319 } 320 int first_slice_idx = int(theta1 / HORZ_THETA + 1); 321 int last_slice_idx = int(theta2 / HORZ_THETA - 1); 322 if (theta1 >= theta2) 323 last_slice_idx += SLICES; 324 for (int j = first_slice_idx; j <= last_slice_idx; ++j) { // calculate the color value for each needed point 325 int X = (j > SLICES ? (j - SLICES) : j) * 2, Y = X + 1; 326 double color_scale_factor = (SQRT2OVER2 * (unit_vertices[X] + unit_vertices[Y]) + 1) / 2; // this is essentially the dot product of (x,y) with (sqrt2over2,sqrt2over2), the direction of the light source, scaled to the range [0,1] 327 colors[j] = BlendClr(border_color1, border_color2, color_scale_factor); 328 } 329 330 glPushMatrix(); 331 glTranslatef(Value(ul.x + wd / 2.0), Value(ul.y + ht / 2.0), 0.0); // move origin to the center of the rectangle 332 glScalef(Value(wd / 2.0), Value(ht / 2.0), 1.0); // map the range [-1,1] to the rectangle in both (x- and y-) directions 333 334 double inner_radius = (std::min(Value(wd), Value(ht)) - 2.0 * bevel_thick) / std::min(Value(wd), Value(ht)); 335 glColor(color); 336 glBegin(GL_TRIANGLE_FAN); 337 glVertex2f(0, 0); 338 // point on circle at angle theta1 339 double theta1_x = cos(-theta1), theta1_y = sin(-theta1); 340 glVertex2f(theta1_x * inner_radius, theta1_y * inner_radius); 341 // angles in between theta1 and theta2, if any 342 for (int i = first_slice_idx; i <= last_slice_idx; ++i) { 343 int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1; 344 glVertex2f(unit_vertices[X] * inner_radius, unit_vertices[Y] * inner_radius); 345 } // theta2 346 double theta2_x = cos(-theta2), theta2_y = sin(-theta2); 347 glVertex2f(theta2_x * inner_radius, theta2_y * inner_radius); 348 glEnd(); 349 glBegin(GL_QUAD_STRIP); 350 // point on circle at angle theta1 351 double color_scale_factor = (SQRT2OVER2 * (theta1_x + theta1_y) + 1) / 2; 352 Clr clr = BlendClr(border_color1, border_color2, color_scale_factor); 353 glColor4ub(clr.r, clr.g, clr.b, clr.a); 354 glVertex2f(theta1_x, theta1_y); 355 glVertex2f(theta1_x * inner_radius, theta1_y * inner_radius); 356 // angles in between theta1 and theta2, if any 357 for (int i = first_slice_idx; i <= last_slice_idx; ++i) { 358 int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1; 359 glColor(colors[i]); 360 glVertex2f(unit_vertices[X], unit_vertices[Y]); 361 glVertex2f(unit_vertices[X] * inner_radius, unit_vertices[Y] * inner_radius); 362 } 363 // theta2 364 color_scale_factor = (SQRT2OVER2 * (theta2_x + theta2_y) + 1) / 2; 365 clr = BlendClr(border_color1, border_color2, color_scale_factor); 366 glColor4ub(clr.r, clr.g, clr.b, clr.a); 367 glVertex2f(theta2_x, theta2_y); 368 glVertex2f(theta2_x * inner_radius, theta2_y * inner_radius); 369 glEnd(); 370 glPopMatrix(); 371 glEnable(GL_TEXTURE_2D); 372 } 373 RoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int corner_radius,int thick)374 void RoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2, 375 unsigned int corner_radius, int thick) 376 { 377 int circle_diameter = corner_radius * 2; 378 CircleArc(Pt(lr.x - circle_diameter, ul.y), Pt(lr.x, ul.y + circle_diameter), 379 color, border_color2, border_color1, thick, 0, 0.5 * PI); // ur corner 380 CircleArc(Pt(ul.x, ul.y), Pt(ul.x + circle_diameter, ul.y + circle_diameter), 381 color, border_color2, border_color1, thick, 0.5 * PI, PI); // ul corner 382 CircleArc(Pt(ul.x, lr.y - circle_diameter), Pt(ul.x + circle_diameter, lr.y), 383 color, border_color2, border_color1, thick, PI, 1.5 * PI); // ll corner 384 CircleArc(Pt(lr.x - circle_diameter, lr.y - circle_diameter), Pt(lr.x, lr.y), 385 color, border_color2, border_color1, thick, 1.5 * PI, 0); // lr corner 386 387 388 // lines connecting circle arcs and giving bevel appearance 389 GL2DVertexBuffer vert_buf; 390 vert_buf.reserve(28); 391 GLRGBAColorBuffer colour_buf; // need to give each vertex in lightness bar its own colour so can't just use a glColor call 392 colour_buf.reserve(28); 393 394 int rad = static_cast<int>(corner_radius); 395 396 double color_scale_factor = (SQRT2OVER2 * (0 + 1) + 1) / 2; 397 Clr clr = BlendClr(border_color1, border_color2, color_scale_factor); 398 // top 399 vert_buf.store(lr.x - rad, ul.y); 400 vert_buf.store(ul.x + rad, ul.y); 401 vert_buf.store(ul.x + rad, ul.y + thick); 402 vert_buf.store(lr.x - rad, ul.y + thick); 403 // left 404 vert_buf.store(ul.x + thick, ul.y + rad); 405 vert_buf.store(ul.x, ul.y + rad); 406 vert_buf.store(ul.x, lr.y - rad); 407 vert_buf.store(ul.x + thick, lr.y - rad); 408 for (unsigned int i = 0; i < 8; ++i) 409 colour_buf.store(clr); 410 411 412 color_scale_factor = (SQRT2OVER2 * (-1 + 0) + 1) / 2; 413 clr = BlendClr(border_color1, border_color2, color_scale_factor); 414 // right 415 vert_buf.store(lr.x, ul.y + rad); 416 vert_buf.store(lr.x - thick, ul.y + rad); 417 vert_buf.store(lr.x - thick, lr.y - rad); 418 vert_buf.store(lr.x, lr.y - rad); 419 // bottom (uses color scale factor (SQRT2OVER2 * (0 + -1) + 1) / 2, which equals that of right 420 vert_buf.store(lr.x - rad, lr.y - thick); 421 vert_buf.store(ul.x + rad, lr.y - thick); 422 vert_buf.store(ul.x + rad, lr.y); 423 vert_buf.store(lr.x - rad, lr.y); 424 for (unsigned int i = 0; i < 8; ++i) 425 colour_buf.store(clr); 426 427 428 // middle 429 vert_buf.store(lr.x - rad, ul.y + thick); 430 vert_buf.store(ul.x + rad, ul.y + thick); 431 vert_buf.store(ul.x + rad, lr.y - thick); 432 vert_buf.store(lr.x - rad, lr.y - thick); 433 434 vert_buf.store(lr.x - thick, ul.y + rad); 435 vert_buf.store(lr.x - rad, ul.y + rad); 436 vert_buf.store(lr.x - rad, lr.y - rad); 437 vert_buf.store(lr.x - thick, lr.y - rad); 438 439 vert_buf.store(ul.x + thick, ul.y + rad); 440 vert_buf.store(ul.x + rad, ul.y + rad); 441 vert_buf.store(ul.x + rad, lr.y - rad); 442 vert_buf.store(ul.x + thick, lr.y - rad); 443 for (unsigned int i = 0; i < 12; ++i) 444 colour_buf.store(color); 445 446 447 glDisable(GL_TEXTURE_2D); 448 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 449 glEnableClientState(GL_VERTEX_ARRAY); 450 glEnableClientState(GL_COLOR_ARRAY); 451 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 452 453 vert_buf.activate(); 454 colour_buf.activate(); 455 456 glDrawArrays(GL_QUADS, 0, vert_buf.size()); 457 458 glPopClientAttrib(); 459 glEnable(GL_TEXTURE_2D); 460 } 461 BubbleRectangle(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3,unsigned int corner_radius)462 void BubbleRectangle(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3, unsigned int corner_radius) 463 { 464 int circle_diameter = corner_radius * 2; 465 BubbleArc(Pt(lr.x - circle_diameter, ul.y), Pt(lr.x, ul.y + circle_diameter), color1, color3, color2, 0, 0.5 * PI); // ur corner 466 BubbleArc(Pt(ul.x, ul.y), Pt(ul.x + circle_diameter, ul.y + circle_diameter), color1, color3, color2, 0.5 * PI, PI); // ul corner 467 BubbleArc(Pt(ul.x, lr.y - circle_diameter), Pt(ul.x + circle_diameter, lr.y), color1, color3, color2, PI, 1.5 * PI); // ll corner 468 BubbleArc(Pt(lr.x - circle_diameter, lr.y - circle_diameter), Pt(lr.x, lr.y), color1, color3, color2, 1.5 * PI, 0); // lr corner 469 470 int rad = static_cast<int>(corner_radius); 471 472 // top 473 double color_scale_factor = (SQRT2OVER2 * (0 + 1) + 1) / 2; 474 Clr scaled_color = BlendClr(color2, color3, color_scale_factor); 475 476 GL2DVertexBuffer verts; 477 verts.reserve(20); 478 GLRGBAColorBuffer colours; 479 colours.reserve(20); 480 481 colours.store(scaled_color); 482 colours.store(scaled_color); 483 verts.store(lr.x - rad, ul.y); 484 verts.store(ul.x + rad, ul.y); 485 colours.store(color1); 486 colours.store(color1); 487 verts.store(ul.x + rad, ul.y + rad); 488 verts.store(lr.x - rad, ul.y + rad); 489 490 // left (uses color scale factor (SQRT2OVER2 * (1 + 0) + 1) / 2, which equals that of top 491 colours.store(scaled_color); 492 colours.store(scaled_color); 493 verts.store(ul.x, ul.y + rad); 494 verts.store(ul.x, lr.y - rad); 495 colours.store(color1); 496 colours.store(color1); 497 verts.store(ul.x + rad, lr.y - rad); 498 verts.store(ul.x + rad, ul.y + rad); 499 500 // right 501 color_scale_factor = (SQRT2OVER2 * (-1 + 0) + 1) / 2; 502 scaled_color = BlendClr(color2, color3, color_scale_factor); 503 colours.store(color1); 504 colours.store(color1); 505 verts.store(lr.x - rad, ul.y + rad); 506 verts.store(lr.x - rad, lr.y - rad); 507 colours.store(scaled_color); 508 colours.store(scaled_color); 509 verts.store(lr.x, lr.y - rad); 510 verts.store(lr.x, ul.y + rad); 511 512 // bottom (uses color scale factor (SQRT2OVER2 * (0 + -1) + 1) / 2, which equals that of left 513 colours.store(color1); 514 colours.store(color1); 515 verts.store(lr.x - rad, lr.y - rad); 516 verts.store(ul.x + rad, lr.y - rad); 517 colours.store(scaled_color); 518 colours.store(scaled_color); 519 verts.store(ul.x + rad, lr.y); 520 verts.store(lr.x - rad, lr.y); 521 522 // middle 523 colours.store(color1); 524 colours.store(color1); 525 verts.store(lr.x - rad, ul.y + rad); 526 verts.store(ul.x + rad, ul.y + rad); 527 colours.store(color1); 528 colours.store(color1); 529 verts.store(ul.x + rad, lr.y - rad); 530 verts.store(lr.x - rad, lr.y - rad); 531 532 533 glDisable(GL_TEXTURE_2D); 534 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 535 glEnableClientState(GL_VERTEX_ARRAY); 536 glEnableClientState(GL_COLOR_ARRAY); 537 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 538 539 verts.activate(); 540 colours.activate(); 541 glDrawArrays(GL_QUADS, 0, verts.size()); 542 543 glPopClientAttrib(); 544 glEnable(GL_TEXTURE_2D); 545 } 546 } // namespace 547 548 549 namespace GG { operator <<(std::ostream & os,const Clr & clr)550 std::ostream& operator<<(std::ostream& os, const Clr& clr) 551 { 552 os << "(" << +clr.r << ", " << +clr.g << ", " << +clr.b << ", " << +clr.a << ")"; 553 return os; 554 } 555 glColor(Clr clr)556 void glColor(Clr clr) 557 { glColor4ub(clr.r, clr.g, clr.b, clr.a); } 558 DisabledColor(Clr clr)559 Clr DisabledColor(Clr clr) 560 { 561 Clr retval = clr; 562 const double gray_factor = 0.75; // amount to move clr in the direction of gray 563 retval.r = static_cast<int>(retval.r + (CLR_GRAY.r - retval.r) * gray_factor); 564 retval.g = static_cast<int>(retval.g + (CLR_GRAY.g - retval.g) * gray_factor); 565 retval.b = static_cast<int>(retval.b + (CLR_GRAY.b - retval.b) * gray_factor); 566 return retval; 567 } 568 BeginScissorClipping(Pt ul,Pt lr)569 void BeginScissorClipping(Pt ul, Pt lr) 570 { 571 if (g_scissor_clipping_rects.empty()) { 572 glPushAttrib(GL_SCISSOR_BIT | GL_ENABLE_BIT); 573 glEnable(GL_SCISSOR_TEST); 574 if (g_stencil_bit) 575 glDisable(GL_STENCIL_TEST); 576 } else { 577 const Rect& r = g_scissor_clipping_rects.back(); 578 ul.x = std::max(r.Left(), std::min(ul.x, r.Right())); 579 ul.y = std::max(r.Top(), std::min(ul.y, r.Bottom())); 580 lr.x = std::max(r.Left(), std::min(lr.x, r.Right())); 581 lr.y = std::max(r.Top(), std::min(lr.y, r.Bottom())); 582 } 583 glScissor(Value(ul.x), Value(GUI::GetGUI()->AppHeight() - lr.y), 584 Value(lr.x - ul.x), Value(lr.y - ul.y)); 585 g_scissor_clipping_rects.push_back(Rect(ul, lr)); 586 } 587 EndScissorClipping()588 void EndScissorClipping() 589 { 590 assert(!g_scissor_clipping_rects.empty()); 591 g_scissor_clipping_rects.pop_back(); 592 if (g_scissor_clipping_rects.empty()) { 593 if (g_stencil_bit) 594 glEnable(GL_STENCIL_TEST); 595 glPopAttrib(); 596 } else { 597 const Rect& r = g_scissor_clipping_rects.back(); 598 glScissor(Value(r.Left()), Value(GUI::GetGUI()->AppHeight() - r.Bottom()), 599 Value(r.Width()), Value(r.Height())); 600 } 601 } 602 ActiveScissorClippingRegion()603 Rect ActiveScissorClippingRegion() 604 { 605 if (g_scissor_clipping_rects.empty()) 606 return Rect(); 607 return g_scissor_clipping_rects.back(); 608 } 609 BeginStencilClipping(Pt inner_ul,Pt inner_lr,Pt outer_ul,Pt outer_lr)610 void BeginStencilClipping(Pt inner_ul, Pt inner_lr, Pt outer_ul, Pt outer_lr) 611 { 612 if (!g_stencil_bit) { 613 glPushAttrib(GL_STENCIL_BUFFER_BIT | GL_ENABLE_BIT); 614 glClearStencil(0); 615 glClear(GL_STENCIL_BUFFER_BIT); 616 glEnable(GL_STENCIL_TEST); 617 if (!g_scissor_clipping_rects.empty()) 618 glDisable(GL_SCISSOR_TEST); 619 } 620 621 glGetBooleanv(GL_COLOR_WRITEMASK, g_prev_color_writemask); 622 glGetBooleanv(GL_DEPTH_WRITEMASK, &g_prev_depth_writemask); 623 624 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 625 glDepthMask(GL_FALSE); 626 627 GLuint mask = 1u << g_stencil_bit; 628 629 glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); 630 glEnableClientState(GL_VERTEX_ARRAY); 631 glDisableClientState(GL_COLOR_ARRAY); 632 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 633 634 glStencilFunc(GL_ALWAYS, mask, mask); 635 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); 636 GLint outer_vertices[] = { 637 Value(outer_ul.x), Value(outer_ul.y), 638 Value(outer_ul.x), Value(outer_lr.y), 639 Value(outer_lr.x), Value(outer_lr.y), 640 Value(outer_lr.x), Value(outer_ul.y) 641 }; 642 glVertexPointer(2, GL_INT, 0, outer_vertices); 643 glDrawArrays(GL_QUADS, 0, 4); 644 645 glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT); 646 GLint inner_vertices[] = { 647 Value(inner_ul.x), Value(inner_ul.y), 648 Value(inner_ul.x), Value(inner_lr.y), 649 Value(inner_lr.x), Value(inner_lr.y), 650 Value(inner_lr.x), Value(inner_ul.y) 651 }; 652 glVertexPointer(2, GL_INT, 0, inner_vertices); 653 glDrawArrays(GL_QUADS, 0, 4); 654 655 glColorMask(g_prev_color_writemask[0], 656 g_prev_color_writemask[1], 657 g_prev_color_writemask[2], 658 g_prev_color_writemask[3]); 659 glDepthMask(g_prev_depth_writemask); 660 661 glStencilFunc(GL_EQUAL, mask, mask); 662 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 663 ++g_stencil_bit; 664 665 glPopClientAttrib(); 666 } 667 EndStencilClipping()668 void EndStencilClipping() 669 { 670 assert(g_stencil_bit); 671 --g_stencil_bit; 672 if (!g_stencil_bit) { 673 if (!g_scissor_clipping_rects.empty()) 674 glEnable(GL_SCISSOR_TEST); 675 glPopAttrib(); 676 } 677 } 678 Line(Pt pt1,Pt pt2,Clr color,float thick)679 void Line(Pt pt1, Pt pt2, Clr color, float thick) 680 { 681 glLineWidth(thick); 682 glColor(color); 683 Line(pt1.x, pt1.y, pt2.x, pt2.y); 684 } 685 Line(X x1,Y y1,X x2,Y y2)686 void Line(X x1, Y y1, X x2, Y y2) 687 { 688 GLfloat vertices[4] = {GLfloat(Value(x1)), GLfloat(Value(y1)), 689 GLfloat(Value(x2)), GLfloat(Value(y2))}; 690 691 glDisable(GL_TEXTURE_2D); 692 693 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 694 glEnableClientState(GL_VERTEX_ARRAY); 695 glDisableClientState(GL_COLOR_ARRAY); 696 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 697 698 glVertexPointer(2, GL_FLOAT, 0, vertices); 699 glDrawArrays(GL_LINES, 0, 2); 700 701 glPopClientAttrib(); 702 glLineWidth(1.0f); 703 glEnable(GL_TEXTURE_2D); 704 } 705 Triangle(Pt pt1,Pt pt2,Pt pt3,Clr color,Clr border_color,float border_thick)706 void Triangle(Pt pt1, Pt pt2, Pt pt3, Clr color, Clr border_color, float border_thick) 707 { 708 GLfloat vertices[6] = {GLfloat(Value(pt1.x)), GLfloat(Value(pt1.y)), GLfloat(Value(pt2.x)), 709 GLfloat(Value(pt2.y)), GLfloat(Value(pt3.x)), GLfloat(Value(pt3.y))}; 710 711 glDisable(GL_TEXTURE_2D); 712 713 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 714 glEnableClientState(GL_VERTEX_ARRAY); 715 glDisableClientState(GL_COLOR_ARRAY); 716 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 717 718 glVertexPointer(2, GL_FLOAT, 0, vertices); 719 glColor(color); 720 glDrawArrays(GL_TRIANGLES, 0, 3); 721 722 if (border_color != GG::CLR_ZERO) { 723 glLineWidth(border_thick); 724 glColor(border_color); 725 726 glDrawArrays(GL_LINE_LOOP, 0, 3); 727 glLineWidth(1.0f); 728 } 729 730 glPopClientAttrib(); 731 glEnable(GL_TEXTURE_2D); 732 } 733 Triangle(X x1,Y y1,X x2,Y y2,X x3,Y y3,bool filled)734 void Triangle(X x1, Y y1, X x2, Y y2, X x3, Y y3, bool filled) 735 { 736 GLfloat vertices[6] = {GLfloat(Value(x1)), GLfloat(Value(y1)), GLfloat(Value(x2)), 737 GLfloat(Value(y2)), GLfloat(Value(x3)), GLfloat(Value(y3))}; 738 739 glDisable(GL_TEXTURE_2D); 740 741 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 742 glEnableClientState(GL_VERTEX_ARRAY); 743 glDisableClientState(GL_COLOR_ARRAY); 744 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 745 746 glVertexPointer(2, GL_FLOAT, 0, vertices); 747 748 if (filled) 749 glDrawArrays(GL_TRIANGLE_FAN, 0, 3); 750 else 751 glDrawArrays(GL_TRIANGLES, 0, 3); 752 753 glPopClientAttrib(); 754 glEnable(GL_TEXTURE_2D); 755 } 756 FlatRectangle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int border_thick)757 void FlatRectangle(Pt ul, Pt lr, Clr color, Clr border_color, 758 unsigned int border_thick/* = 2*/) 759 { 760 Rectangle(ul, lr, color, border_color, border_color, border_thick, 761 true, true, true, true); 762 } 763 BeveledRectangle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int bevel_thick,bool bevel_left,bool bevel_top,bool bevel_right,bool bevel_bottom)764 void BeveledRectangle(Pt ul, Pt lr, Clr color, Clr border_color, bool up, 765 unsigned int bevel_thick/* = 2*/, bool bevel_left/* = true*/, 766 bool bevel_top/* = true*/, bool bevel_right/* = true*/, 767 bool bevel_bottom/* = true*/) 768 { 769 Rectangle(ul, lr, color, 770 (up ? LightenClr(border_color) : DarkenClr(border_color)), 771 (up ? DarkenClr(border_color) : LightenClr(border_color)), 772 bevel_thick, bevel_left, bevel_top, bevel_right, bevel_bottom); 773 } 774 FlatRoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int corner_radius,unsigned int border_thick)775 void FlatRoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color, 776 unsigned int corner_radius/* = 5*/, 777 unsigned int border_thick/* = 2*/) 778 { 779 RoundedRectangle(ul, lr, color, border_color, border_color, 780 corner_radius, border_thick); 781 } 782 BeveledRoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int corner_radius,unsigned int bevel_thick)783 void BeveledRoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color, bool up, 784 unsigned int corner_radius/* = 5*/, 785 unsigned int bevel_thick/* = 2*/) 786 { 787 RoundedRectangle(ul, lr, color, 788 (up ? LightenClr(border_color) : DarkenClr(border_color)), 789 (up ? DarkenClr(border_color) : LightenClr(border_color)), 790 corner_radius, bevel_thick); 791 } 792 FlatCheck(Pt ul,Pt lr,Clr color)793 void FlatCheck(Pt ul, Pt lr, Clr color) 794 { Check(ul, lr, color, color, color); } 795 BeveledCheck(Pt ul,Pt lr,Clr color)796 void BeveledCheck(Pt ul, Pt lr, Clr color) 797 { Check(ul, lr, color, LightenClr(color), DarkenClr(color)); } 798 FlatX(Pt ul,Pt lr,Clr color)799 void FlatX(Pt ul, Pt lr, Clr color) 800 { XMark(ul, lr, color, color, color); } 801 Bubble(Pt ul,Pt lr,Clr color,bool up)802 void Bubble(Pt ul, Pt lr, Clr color, bool up/* = true*/) 803 { 804 BubbleArc(ul, lr, color, 805 (up ? DarkenClr(color) : LightenClr(color)), 806 (up ? LightenClr(color) : DarkenClr(color)), 807 0, 0); 808 } 809 FlatCircle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int thick)810 void FlatCircle(Pt ul, Pt lr, Clr color, Clr border_color, unsigned int thick/* = 2*/) 811 { CircleArc(ul, lr, color, border_color, border_color, thick, 0, 0); } 812 BeveledCircle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int bevel_thick)813 void BeveledCircle(Pt ul, Pt lr, Clr color, Clr border_color, bool up/* = true*/, unsigned int bevel_thick/* = 2*/) 814 { 815 CircleArc(ul, lr, color, 816 (up ? DarkenClr(border_color) : LightenClr(border_color)), 817 (up ? LightenClr(border_color) : DarkenClr(border_color)), 818 bevel_thick, 0, 0); 819 } 820 BubbleRectangle(Pt ul,Pt lr,Clr color,bool up,unsigned int corner_radius)821 void BubbleRectangle(Pt ul, Pt lr, Clr color, bool up, unsigned int corner_radius/* = 5*/) 822 { 823 ::BubbleRectangle(ul, lr, color, 824 (up ? LightenClr(color) : DarkenClr(color)), 825 (up ? DarkenClr(color) : LightenClr(color)), 826 corner_radius); 827 } 828 } // namespace GG 829