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