1 /** @file gl_draw.cpp Basic (Generic) Drawing Routines.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de_base.h"
21 #include "gl/gl_draw.h"
22
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cmath>
26 #include <de/GLState>
27 #include <de/GLInfo>
28 #include <de/Vector>
29 #include <de/concurrency.h>
30 #include <doomsday/console/exec.h>
31
32 #include "gl/gl_main.h"
33 #include "gl/sys_opengl.h"
34
35 #include "world/p_players.h" ///< @todo remove me
36
37 #include "api_render.h"
38 #include "render/viewports.h"
39
40 using namespace de;
41
GL_DrawRectWithCoords(Rectanglei const & rect,Vector2i const coords[4])42 void GL_DrawRectWithCoords(Rectanglei const &rect, Vector2i const coords[4])
43 {
44 DENG_ASSERT_GL_CONTEXT_ACTIVE();
45
46 DGL_Begin(DGL_QUADS);
47 // Top left.
48 if(coords) DGL_TexCoord2f(0, coords[0].x, coords[0].y);
49 DGL_Vertex2f(rect.topLeft.x, rect.topLeft.y);
50
51 // Top Right.
52 if(coords) DGL_TexCoord2f(0, coords[1].x, coords[1].y);
53 DGL_Vertex2f(rect.topRight().x, rect.topRight().y);
54
55 // Bottom right.
56 if(coords) DGL_TexCoord2f(0, coords[2].x, coords[2].y);
57 DGL_Vertex2f(rect.bottomRight.x, rect.bottomRight.y);
58
59 // Bottom left.
60 if(coords) DGL_TexCoord2f(0, coords[3].x, coords[3].y);
61 DGL_Vertex2f(rect.bottomLeft().x, rect.bottomLeft().y);
62 DGL_End();
63 }
64
GL_DrawRect(Rectanglei const & rect)65 void GL_DrawRect(Rectanglei const &rect)
66 {
67 Vector2i coords[4] = { Vector2i(0, 0), Vector2i(1, 0),
68 Vector2i(1, 1), Vector2i(0, 1) };
69 GL_DrawRectWithCoords(rect, coords);
70 }
71
GL_DrawRect2(int x,int y,int w,int h)72 void GL_DrawRect2(int x, int y, int w, int h)
73 {
74 GL_DrawRect(Rectanglei::fromSize(Vector2i(x, y), Vector2ui(w, h)));
75 }
76
GL_DrawRectfWithCoords(const RectRawf * rect,Point2Rawf coords[4])77 void GL_DrawRectfWithCoords(const RectRawf* rect, Point2Rawf coords[4])
78 {
79 if(!rect) return;
80
81 DENG_ASSERT_GL_CONTEXT_ACTIVE();
82
83 DGL_Begin(DGL_QUADS);
84 // Upper left.
85 if(coords) DGL_TexCoord2f(0, coords[0].x, coords[0].y);
86 DGL_Vertex2f(rect->origin.x, rect->origin.y);
87
88 // Upper Right.
89 if(coords) DGL_TexCoord2f(0, coords[1].x, coords[1].y);
90 DGL_Vertex2f(rect->origin.x + rect->size.width, rect->origin.y);
91
92 // Lower right.
93 if(coords) DGL_TexCoord2f(0, coords[2].x, coords[2].y);
94 DGL_Vertex2f(rect->origin.x + rect->size.width, rect->origin.y + rect->size.height);
95
96 // Lower left.
97 if(coords) DGL_TexCoord2f(0, coords[3].x, coords[3].y);
98 DGL_Vertex2f(rect->origin.x, rect->origin.y + rect->size.height);
99 DGL_End();
100 }
101
GL_DrawRectf(const RectRawf * rect)102 void GL_DrawRectf(const RectRawf* rect)
103 {
104 Point2Rawf coords[4];
105 coords[0].x = 0;
106 coords[0].y = 0;
107 coords[1].x = 1;
108 coords[1].y = 0;
109 coords[2].x = 1;
110 coords[2].y = 1;
111 coords[3].x = 0;
112 coords[3].y = 1;
113 GL_DrawRectfWithCoords(rect, coords);
114 }
115
GL_DrawRectf2(double x,double y,double w,double h)116 void GL_DrawRectf2(double x, double y, double w, double h)
117 {
118 RectRawf rect;
119 rect.origin.x = x;
120 rect.origin.y = y;
121 rect.size.width = w;
122 rect.size.height = h;
123 GL_DrawRectf(&rect);
124 }
125
GL_DrawRectf2Color(double x,double y,double w,double h,float r,float g,float b,float a)126 void GL_DrawRectf2Color(double x, double y, double w, double h, float r, float g, float b, float a)
127 {
128 DGL_Color4f(r, g, b, a);
129 GL_DrawRectf2(x, y, w, h);
130 }
131
GL_DrawRectf2TextureColor(double x,double y,double width,double height,int texW,int texH,const float topColor[3],float topAlpha,const float bottomColor[3],float bottomAlpha)132 void GL_DrawRectf2TextureColor(double x, double y, double width, double height,
133 int texW, int texH, const float topColor[3], float topAlpha,
134 const float bottomColor[3], float bottomAlpha)
135 {
136 if(topAlpha <= 0 && bottomAlpha <= 0) return;
137
138 DENG_ASSERT_GL_CONTEXT_ACTIVE();
139
140 DGL_Begin(DGL_QUADS);
141 // Top color.
142 DGL_Color4f(topColor[0], topColor[1], topColor[2], topAlpha);
143 DGL_TexCoord2f(0, 0, 0);
144 DGL_Vertex2f(x, y);
145 DGL_TexCoord2f(0, width / (float) texW, 0);
146 DGL_Vertex2f(x + width, y);
147
148 // Bottom color.
149 DGL_Color4f(bottomColor[0], bottomColor[1], bottomColor[2], bottomAlpha);
150 DGL_TexCoord2f(0, width / (float) texW, height / (float) texH);
151 DGL_Vertex2f(x + width, y + height);
152 DGL_TexCoord2f(0, 0, height / (float) texH);
153 DGL_Vertex2f(x, y + height);
154 DGL_End();
155 }
156
GL_DrawRectf2Tiled(double x,double y,double w,double h,int tw,int th)157 void GL_DrawRectf2Tiled(double x, double y, double w, double h, int tw, int th)
158 {
159 DENG_ASSERT_GL_CONTEXT_ACTIVE();
160
161 DGL_Begin(DGL_QUADS);
162 DGL_TexCoord2f(0, 0, 0);
163 DGL_Vertex2f(x, y);
164 DGL_TexCoord2f(0, w / (float) tw, 0);
165 DGL_Vertex2f(x + w, y);
166 DGL_TexCoord2f(0, w / (float) tw, h / (float) th);
167 DGL_Vertex2f(x + w, y + h);
168 DGL_TexCoord2f(0, 0, h / (float) th);
169 DGL_Vertex2f(x, y + h);
170 DGL_End();
171 }
172
GL_DrawCutRectfTiled(const RectRawf * rect,int tw,int th,int txoff,int tyoff,const RectRawf * cutRect)173 void GL_DrawCutRectfTiled(const RectRawf* rect, int tw, int th, int txoff, int tyoff,
174 const RectRawf* cutRect)
175 {
176 float ftw = tw, fth = th;
177 float txo = (1.0f / (float)tw) * (float)txoff;
178 float tyo = (1.0f / (float)th) * (float)tyoff;
179
180 // We'll draw at max four rectangles.
181 float toph = cutRect->origin.y - rect->origin.y;
182 float bottomh = rect->origin.y + rect->size.height - (cutRect->origin.y + cutRect->size.height);
183 float sideh = rect->size.height - toph - bottomh;
184 float lefth = cutRect->origin.x - rect->origin.x;
185 float righth = rect->origin.x + rect->size.width - (cutRect->origin.x + cutRect->size.width);
186
187 DENG_ASSERT_GL_CONTEXT_ACTIVE();
188
189 DGL_Begin(DGL_QUADS);
190 if(toph > 0)
191 {
192 // The top rectangle.
193 DGL_TexCoord2f(0, txo, tyo);
194 DGL_Vertex2f(rect->origin.x, rect->origin.y);
195 DGL_TexCoord2f(0, txo + (rect->size.width / ftw), tyo);
196 DGL_Vertex2f(rect->origin.x + rect->size.width, rect->origin.y);
197 DGL_TexCoord2f(0, txo + (rect->size.width / ftw), tyo + (toph / fth));
198 DGL_Vertex2f(rect->origin.x + rect->size.width, rect->origin.y + toph);
199 DGL_TexCoord2f(0, txo, tyo + (toph / fth));
200 DGL_Vertex2f(rect->origin.x, rect->origin.y + toph);
201 }
202
203 if(lefth > 0 && sideh > 0)
204 {
205 float yoff = toph / fth;
206
207 // The left rectangle.
208 DGL_TexCoord2f(0, txo, yoff + tyo);
209 DGL_Vertex2f(rect->origin.x, rect->origin.y + toph);
210 DGL_TexCoord2f(0, txo + (lefth / ftw), yoff + tyo);
211 DGL_Vertex2f(rect->origin.x + lefth, rect->origin.y + toph);
212 DGL_TexCoord2f(0, txo + (lefth / ftw), yoff + tyo + sideh / fth);
213 DGL_Vertex2f(rect->origin.x + lefth, rect->origin.y + toph + sideh);
214 DGL_TexCoord2f(0, txo, yoff + tyo + sideh / fth);
215 DGL_Vertex2f(rect->origin.x, rect->origin.y + toph + sideh);
216 }
217
218 if(righth > 0 && sideh > 0)
219 {
220 float ox = rect->origin.x + lefth + cutRect->size.width;
221 float xoff = (lefth + cutRect->size.width) / ftw;
222 float yoff = toph / fth;
223
224 // The left rectangle.
225 DGL_TexCoord2f(0, xoff + txo, yoff + tyo);
226 DGL_Vertex2f(ox, rect->origin.y + toph);
227 DGL_TexCoord2f(0, xoff + txo + righth / ftw, yoff + tyo);
228 DGL_Vertex2f(ox + righth, rect->origin.y + toph);
229 DGL_TexCoord2f(0, xoff + txo + righth / ftw, yoff + tyo + sideh / fth);
230 DGL_Vertex2f(ox + righth, rect->origin.y + toph + sideh);
231 DGL_TexCoord2f(0, xoff + txo, yoff + tyo + sideh / fth);
232 DGL_Vertex2f(ox, rect->origin.y + toph + sideh);
233 }
234
235 if(bottomh > 0)
236 {
237 float oy = rect->origin.y + toph + sideh;
238 float yoff = (toph + sideh) / fth;
239
240 DGL_TexCoord2f(0, txo, yoff + tyo);
241 DGL_Vertex2f(rect->origin.x, oy);
242 DGL_TexCoord2f(0, txo + rect->size.width / ftw, yoff + tyo);
243 DGL_Vertex2f(rect->origin.x + rect->size.width, oy);
244 DGL_TexCoord2f(0, txo + rect->size.width / ftw, yoff + tyo + bottomh / fth);
245 DGL_Vertex2f(rect->origin.x + rect->size.width, oy + bottomh);
246 DGL_TexCoord2f(0, txo, yoff + tyo + bottomh / fth);
247 DGL_Vertex2f(rect->origin.x, oy + bottomh);
248 }
249 DGL_End();
250 }
251
GL_DrawCutRectf2Tiled(double x,double y,double w,double h,int tw,int th,int txoff,int tyoff,double cx,double cy,double cw,double ch)252 void GL_DrawCutRectf2Tiled(double x, double y, double w, double h, int tw, int th,
253 int txoff, int tyoff, double cx, double cy, double cw, double ch)
254 {
255 RectRawf rect, cutRect;
256
257 rect.origin.x = x;
258 rect.origin.y = y;
259 rect.size.width = w;
260 rect.size.height = h;
261
262 cutRect.origin.x = cx;
263 cutRect.origin.y = cy;
264 cutRect.size.width = cw;
265 cutRect.size.height = ch;
266
267 GL_DrawCutRectfTiled(&rect, tw, th, txoff, tyoff, &cutRect);
268 }
269
270 /**
271 * Totally inefficient for a large number of lines.
272 */
GL_DrawLine(float x1,float y1,float x2,float y2,float r,float g,float b,float a)273 void GL_DrawLine(float x1, float y1, float x2, float y2, float r, float g,
274 float b, float a)
275 {
276 DENG_ASSERT_GL_CONTEXT_ACTIVE();
277
278 DGL_Color4f(r, g, b, a);
279 DGL_Begin(DGL_LINES);
280 DGL_Vertex2f(x1, y1);
281 DGL_Vertex2f(x2, y2);
282 DGL_End();
283 }
284
285 #undef GL_ResetViewEffects
GL_ResetViewEffects()286 DENG_EXTERN_C void GL_ResetViewEffects()
287 {
288 GL_SetFilter(false);
289 Con_Executef(CMDS_DDAY, true, "postfx %i none", consolePlayer);
290 DD_SetInteger(DD_RENDER_FULLBRIGHT, false);
291 }
292
293 #undef GL_ConfigureBorderedProjection2
GL_ConfigureBorderedProjection2(dgl_borderedprojectionstate_t * bp,int flags,int width,int height,int availWidth,int availHeight,scalemode_t overrideMode,float stretchEpsilon)294 DENG_EXTERN_C void GL_ConfigureBorderedProjection2(dgl_borderedprojectionstate_t* bp, int flags,
295 int width, int height, int availWidth, int availHeight, scalemode_t overrideMode,
296 float stretchEpsilon)
297 {
298 if(!bp) App_Error("GL_ConfigureBorderedProjection2: Invalid 'bp' argument.");
299
300 bp->flags = flags;
301 bp->width = width; // draw coordinates (e.g., VGA)
302 bp->height = height;
303 bp->availWidth = availWidth; // screen space
304 bp->availHeight = availHeight;
305
306 bp->scaleMode = R_ChooseScaleMode2(bp->width, bp->height,
307 bp->availWidth, bp->availHeight,
308 overrideMode, stretchEpsilon);
309
310 bp->isPillarBoxed = R_ChooseAlignModeAndScaleFactor(&bp->scaleFactor,
311 bp->width, bp->height,
312 bp->availWidth, bp->availHeight,
313 bp->scaleMode);
314 }
315
316 #undef GL_ConfigureBorderedProjection
GL_ConfigureBorderedProjection(dgl_borderedprojectionstate_t * bp,int flags,int width,int height,int availWidth,int availHeight,scalemode_t overrideMode)317 DENG_EXTERN_C void GL_ConfigureBorderedProjection(dgl_borderedprojectionstate_t* bp, int flags,
318 int width, int height, int availWidth, int availHeight, scalemode_t overrideMode)
319 {
320 GL_ConfigureBorderedProjection2(bp, flags, width, height, availWidth,
321 availHeight, overrideMode, DEFAULT_SCALEMODE_STRETCH_EPSILON);
322 }
323
324 #undef GL_BeginBorderedProjection
GL_BeginBorderedProjection(dgl_borderedprojectionstate_t * bp)325 DENG_EXTERN_C void GL_BeginBorderedProjection(dgl_borderedprojectionstate_t* bp)
326 {
327 DENG_ASSERT(bp != 0);
328 if (!bp) return;
329
330 if (bp->scaleMode == SCALEMODE_STRETCH) return;
331
332 DENG2_ASSERT_IN_RENDER_THREAD();
333 DENG_ASSERT_GL_CONTEXT_ACTIVE();
334
335 /**
336 * Use an orthographic projection in screenspace, translating and
337 * scaling the coordinate space using the modelview matrix producing
338 * an aspect-corrected space of availWidth x availHeight and centered
339 * on the larger of the horizontal and vertical axes.
340 */
341 DGL_MatrixMode(DGL_PROJECTION);
342 DGL_PushMatrix();
343 DGL_LoadIdentity();
344 DGL_Ortho(0, 0, bp->availWidth, bp->availHeight, -1, 1);
345
346 DGL_MatrixMode(DGL_MODELVIEW);
347 DGL_PushMatrix();
348
349 DGL_PushState();
350
351 if (bp->isPillarBoxed)
352 {
353 // "Pillarbox":
354 int offset = int((bp->availWidth - bp->scaleFactor * bp->width) / 2 + .5f);
355 if (bp->flags & BPF_OVERDRAW_CLIP)
356 {
357 DGL_SetScissor2(offset, 0,
358 int(bp->scaleFactor * bp->width), bp->availHeight);
359 }
360
361 DGL_Translatef(offset, 0, 0);
362 DGL_Scalef(bp->scaleFactor, bp->scaleFactor * 1.2f, 1);
363 }
364 else
365 {
366 // "Letterbox":
367 int offset = int((bp->availHeight - bp->scaleFactor * 1.2f * bp->height) / 2 + .5f);
368 if (bp->flags & BPF_OVERDRAW_CLIP)
369 {
370 DGL_SetScissor2(0, offset,
371 bp->availWidth, int(bp->scaleFactor * 1.2f * bp->height));
372 }
373
374 DGL_Translatef(0, offset, 0);
375 DGL_Scalef(bp->scaleFactor, bp->scaleFactor * 1.2f, 1);
376 }
377 }
378
379 #undef GL_EndBorderedProjection
GL_EndBorderedProjection(dgl_borderedprojectionstate_t * bp)380 DENG_EXTERN_C void GL_EndBorderedProjection(dgl_borderedprojectionstate_t* bp)
381 {
382 DENG_ASSERT(bp != 0);
383 if(!bp) return;
384
385 if (SCALEMODE_STRETCH == bp->scaleMode) return;
386
387 DENG2_ASSERT_IN_RENDER_THREAD();
388 DENG_ASSERT_GL_CONTEXT_ACTIVE();
389
390 DGL_PopState();
391
392 DGL_MatrixMode(DGL_MODELVIEW);
393 DGL_PopMatrix();
394
395 if (bp->flags & BPF_OVERDRAW_MASK)
396 {
397 // It shouldn't be necessary to bind the "not-texture" but the game
398 // may have left whatever GL texture state it was using on. As this
399 // isn't cleaned up until drawing control returns to the engine we
400 // must explicitly disable it here.
401 GL_SetNoTexture();
402 DGL_Color4f(0, 0, 0, 1);
403
404 if (bp->isPillarBoxed)
405 {
406 // "Pillarbox":
407 int w = int((bp->availWidth - bp->scaleFactor * bp->width) / 2 + .5f);
408 GL_DrawRectf2(0, 0, w, bp->availHeight);
409 GL_DrawRectf2(bp->availWidth - w, 0, w, bp->availHeight);
410 }
411 else
412 {
413 // "Letterbox":
414 int h = int((bp->availHeight - bp->scaleFactor * 1.2f * bp->height) / 2 + .5f);
415 GL_DrawRectf2(0, 0, bp->availWidth, h);
416 GL_DrawRectf2(0, bp->availHeight - h, bp->availWidth, h);
417 }
418 }
419
420 DGL_MatrixMode(DGL_PROJECTION);
421 DGL_PopMatrix();
422 }
423