1 /** @file gl_main.cpp GL-Graphics Subsystem
2  * @ingroup gl
3  *
4  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5  * @authors Copyright © 2006-2014 Daniel Swanson <danij@dengine.net>
6  * @authors Copyright © 2006 Jamie Jones <jamie_jones_au@yahoo.com.au>
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA</small>
21  */
22 
23 #ifdef UNIX
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 #endif
28 
29 #include "de_base.h"
30 #include "gl/gl_main.h"
31 #include "api_gl.h"
32 
33 #include <de/concurrency.h>
34 #include <de/App>
35 #include <de/Config>
36 #include <de/DisplayMode>
37 #include <de/GLInfo>
38 #include <de/GLState>
39 #include <de/LogBuffer>
40 #include <doomsday/console/cmd.h>
41 #include <doomsday/console/var.h>
42 #include <doomsday/defs/mapinfo.h>
43 #include <doomsday/filesys/fs_main.h>
44 #include <doomsday/resource/colorpalettes.h>
45 #include "clientapp.h"
46 #include "sys_system.h"  // novideo
47 
48 #include "world/map.h"
49 #include "world/p_object.h"
50 #include "world/p_players.h"
51 
52 #include "gl/gl_tex.h"
53 #include "gl/gl_texmanager.h"
54 #include "gl/texturecontent.h"
55 
56 #include "resource/hq2x.h"
57 #include "MaterialAnimator"
58 #include "MaterialVariantSpec"
59 #include "ClientTexture"
60 
61 #include "api_render.h"
62 #include "render/rend_main.h"
63 #include "render/r_main.h"
64 #include "render/cameralensfx.h"
65 #include "render/rendersystem.h"
66 #include "render/rend_font.h"
67 #include "render/rend_model.h"
68 #include "render/rend_particle.h"
69 #include "render/vr.h"
70 #include "misc/r_util.h"
71 
72 #include "ui/ui_main.h"
73 #include "ui/clientwindowsystem.h"
74 
75 using namespace de;
76 
77 extern dint maxnumnodes;
78 extern dd_bool fillOutlines;
79 
80 dint test3dfx;
81 dint r_detail = true;  ///< Draw detail textures (if available).
82 
83 dfloat vid_gamma = 1.0f, vid_bright, vid_contrast = 1.0f;
84 dfloat glNearClip, glFarClip;
85 
86 static dd_bool initGLOk;
87 static dd_bool initFullGLOk;
88 
89 static dd_bool gamma_support;
90 static dfloat oldgamma, oldcontrast, oldbright;
91 
92 static dint fogModeDefault;
93 
94 //static viewport_t currentView;
95 
resSys()96 static inline ClientResources &resSys()
97 {
98     return App_Resources();
99 }
100 
GL_IsInited()101 dd_bool GL_IsInited()
102 {
103     return initGLOk;
104 }
105 
GL_IsFullyInited()106 dd_bool GL_IsFullyInited()
107 {
108     return initFullGLOk;
109 }
110 
111 #if 0
112 void GL_GetGammaRamp(DisplayColorTransfer *ramp)
113 {
114     if(!gamma_support) return;
115 
116     DisplayMode_GetColorTransfer(ramp);
117 }
118 
119 void GL_SetGammaRamp(DisplayColorTransfer const *ramp)
120 {
121     if(!gamma_support) return;
122 
123     DisplayMode_SetColorTransfer(ramp);
124 }
125 
126 /**
127  * Calculate a gamma ramp and write the result to the location pointed to.
128  *
129  * @todo  Allow for finer control of the curves (separate red, green, blue).
130  *
131  * @param ramp      Ptr to the ramp table to write to. Must point to a ushort[768] area of memory.
132  * @param gamma     Non-linear factor (curvature; >1.0 multiplies).
133  * @param contrast  Steepness.
134  * @param bright    Brightness, uniform offset.
135  */
136 void GL_MakeGammaRamp(ushort *ramp, dfloat gamma, dfloat contrast, dfloat bright)
137 {
138     DENG2_ASSERT(ramp);
139 
140     ddouble ideal[256];  // After processing clamped to unsigned short.
141 
142     // Don't allow stupid values.
143     if(contrast < 0.1f)
144         contrast = 0.1f;
145 
146     if(bright > 0.8f)  bright = 0.8f;
147     if(bright < -0.8f) bright = -0.8f;
148 
149     // Init the ramp as a line with the steepness defined by contrast.
150     for(dint i = 0; i < 256; ++i)
151     {
152         ideal[i] = i * contrast - (contrast - 1) * 127;
153     }
154 
155     // Apply the gamma curve.
156     if(gamma != 1)
157     {
158         if(gamma <= 0.1f) gamma = 0.1f;
159 
160         ddouble norm = pow(255, 1 / ddouble( gamma ) - 1);  // Normalizing factor.
161         for(dint i = 0; i < 256; ++i)
162         {
163             ideal[i] = pow(ideal[i], 1 / ddouble( gamma )) / norm;
164         }
165     }
166 
167     // The last step is to add the brightness offset.
168     for(dint i = 0; i < 256; ++i)
169     {
170         ideal[i] += bright * 128;
171     }
172 
173     // Clamp it and write the ramp table.
174     for(dint i = 0; i < 256; ++i)
175     {
176         ideal[i] *= 0x100;  // Byte => word
177         if(ideal[i] < 0)        ideal[i] = 0;
178         if(ideal[i] > 0xffff)   ideal[i] = 0xffff;
179 
180         ramp[i] = ramp[i + 256] = ramp[i + 512] = (dushort) ideal[i];
181     }
182 }
183 
184 /**
185  * Updates the gamma ramp based on vid_gamma, vid_contrast and vid_bright.
186  */
187 void GL_SetGamma()
188 {
189     DisplayColorTransfer myramp;
190 
191     oldgamma    = vid_gamma;
192     oldcontrast = vid_contrast;
193     oldbright   = vid_bright;
194 
195     GL_MakeGammaRamp(myramp.table, vid_gamma, vid_contrast, vid_bright);
196     GL_SetGammaRamp(&myramp);
197 }
198 #endif
199 
200 
GL_FinishFrame()201 void GL_FinishFrame()
202 {
203     if (ClientApp::vr().mode() == VRConfig::OculusRift) return;
204 
205     DENG2_ASSERT_IN_RENDER_THREAD();
206     LIBGUI_ASSERT_GL_CONTEXT_ACTIVE();
207 
208 #if 0
209     // Check for color adjustment changes.
210     if (oldgamma != vid_gamma || oldcontrast != vid_contrast || oldbright != vid_bright)
211     {
212         GL_SetGamma();
213     }
214 #endif
215 
216 #if !defined (DENG_MOBILE)
217     // Wait until the right time to show the frame so that the realized
218     // frame rate is exactly right.
219     LIBGUI_GL.glFlush();
220     DD_WaitForOptimalUpdateTime();
221 #endif
222 }
223 
printConfiguration()224 static void printConfiguration()
225 {
226     LOG_GL_VERBOSE(_E(b) "Render configuration:");
227 
228     //LOG_GL_VERBOSE("  Multisampling: %b") << GL_state.features.multisample;
229     /*if(GL_state.features.multisample)
230     {
231         LOG_GL_VERBOSE("  Multisampling format: %i") << GL_state.multisampleFormat;
232     }*/
233     //LOG_GL_VERBOSE("  Multitexturing: %s") << (numTexUnits > 1? (envModAdd? "full" : "partial") : "not available");
234     LOG_GL_VERBOSE("  Texture Anisotropy: %s") << (GL_state.features.texFilterAniso? "variable" : "fixed");
235     LOG_GL_VERBOSE("  Texture Compression: %b") << GL_state.features.texCompression;
236 }
237 
GL_EarlyInit()238 void GL_EarlyInit()
239 {
240     if(novideo) return;
241     if(initGLOk) return;  // Already initialized.
242 
243     LOG_GL_VERBOSE("Initializing Render subsystem...");
244 
245     ClientApp::renderSystem().glInit();
246 
247     gamma_support = !CommandLine_Check("-noramp");
248 
249     GL_InitDeferredTask();
250 
251     // Model renderer must be initialized early as it may need to configure
252     // gl-element arrays.
253     Rend_ModelInit();
254 
255     // Check the maximum texture size.
256     if(GLInfo::limits().maxTexSize == 256)
257     {
258         LOG_GL_WARNING("Using restricted texture w/h ratio (1:8)");
259         ratioLimit = 8;
260     }
261     if(CommandLine_Check("-outlines"))
262     {
263         fillOutlines = false;
264         LOG_GL_NOTE("Textures have outlines");
265     }
266 
267     renderTextures = !CommandLine_Exists("-notex");
268 
269     printConfiguration();
270 
271     // Initialize the renderer into a 2D state.
272     GL_Init2DState();
273 
274     GL_SetVSync(App::config().getb("window.main.vsync"));
275 
276     initGLOk = true;
277 }
278 
GL_Init()279 void GL_Init()
280 {
281     if(novideo) return;
282 
283     if(!initGLOk)
284     {
285         App_Error("GL_Init: GL_EarlyInit has not been done yet.\n");
286     }
287 
288 #if 0
289     // Set the gamma in accordance with vid-gamma, vid-bright and vid-contrast.
290     GL_SetGamma();
291 #endif
292 
293     // Initialize one viewport.
294     R_SetupDefaultViewWindow(0);
295     R_SetViewGrid(1, 1);
296 }
297 
GL_InitRefresh()298 void GL_InitRefresh()
299 {
300     if(novideo) return;
301 
302     GL_InitTextureManager();
303 
304     initFullGLOk = true;
305 }
306 
GL_ShutdownRefresh()307 void GL_ShutdownRefresh()
308 {
309     initFullGLOk = false;
310 }
311 
GL_Shutdown()312 void GL_Shutdown()
313 {
314     if(!initGLOk || !ClientWindow::mainExists())
315         return; // Not yet initialized fully.
316 
317     DENG_ASSERT_IN_MAIN_THREAD();
318     DENG_ASSERT_GL_CONTEXT_ACTIVE();
319 
320     // We won't be drawing anything further but we don't want to shutdown
321     // with the previous frame still visible as this can lead to unwanted
322     // artefacts during video context switches on some displays.
323     //
324     // Render a few black frames before we continue.
325     /*if(!novideo)
326     {
327         dint i = 0;
328         do
329         {
330             LIBGUI_GL.glClear(GL_COLOR_BUFFER_BIT);
331             GL_FinishFrame();
332         } while(++i < 3);
333     }*/
334     ClientApp::renderSystem().glDeinit();
335     GL_ShutdownDeferredTask();
336     FR_Shutdown();
337     Rend_ModelShutdown();
338     LensFx_Shutdown();
339     Rend_Reset();
340     GL_ShutdownRefresh();
341     DGL_Shutdown();
342 
343     // Shutdown OpenGL.
344     Sys_GLShutdown();
345 
346     initGLOk = false;
347 }
348 
GL_Init2DState()349 void GL_Init2DState()
350 {
351     // The variables.
352     glNearClip = 5;
353     glFarClip  = 16500;
354 
355     DENG_ASSERT_IN_MAIN_THREAD();
356 
357     //DGL_SetInteger(DGL_FLUSH_BACKTRACE, true);
358 
359     // Here we configure the OpenGL state and set the projection matrix.
360     DGL_CullFace(DGL_NONE);
361     DGL_Disable(DGL_DEPTH_TEST);
362     DGL_Disable(DGL_TEXTURE_2D);
363 
364     // The projection matrix.
365     DGL_MatrixMode(DGL_PROJECTION);
366     DGL_LoadIdentity();
367     DGL_Ortho(0, 0, 320, 200, -1, 1);
368 
369     // Default state for the white fog is off.
370     {
371         fogParams.usingFog    = false;
372         fogParams.fogColor[0] = DEFAULT_FOG_COLOR_RED;
373         fogParams.fogColor[1] = DEFAULT_FOG_COLOR_GREEN;
374         fogParams.fogColor[2] = DEFAULT_FOG_COLOR_BLUE;
375         fogParams.fogColor[3] = 1.f;
376     }
377     DGL_Disable(DGL_FOG);
378     DGL_Fogi(DGL_FOG_MODE, (fogModeDefault == 0 ? DGL_LINEAR :
379                             fogModeDefault == 1 ? DGL_EXP    : DGL_EXP2));
380     DGL_Fogf(DGL_FOG_START,   DEFAULT_FOG_START);
381     DGL_Fogf(DGL_FOG_END,     DEFAULT_FOG_END);
382     DGL_Fogf(DGL_FOG_DENSITY, DEFAULT_FOG_DENSITY);
383     DGL_Fogfv(DGL_FOG_COLOR, fogParams.fogColor);
384 
385     LIBGUI_ASSERT_GL_OK();
386 }
387 
GL_DepthClipRange()388 Rangef GL_DepthClipRange()
389 {
390     return Rangef(glNearClip, glFarClip);
391 }
392 
GL_ProjectionMatrix(bool useFixedFov)393 void GL_ProjectionMatrix(bool useFixedFov)
394 {
395     DENG2_ASSERT_IN_RENDER_THREAD();
396     DENG_ASSERT_GL_CONTEXT_ACTIVE();
397 
398     DGL_MatrixMode(DGL_PROJECTION);
399     DGL_LoadMatrix(Rend_GetProjectionMatrix(useFixedFov ? weaponFixedFOV : 0.f).values());
400 }
401 
GL_SetupFogFromMapInfo(Record const * mapInfo)402 void GL_SetupFogFromMapInfo(Record const *mapInfo)
403 {
404     if(mapInfo)
405     {
406         R_SetupFog(mapInfo->getf("fogStart"), mapInfo->getf("fogEnd"),
407                    mapInfo->getf("fogDensity"),
408                    Vector3f(mapInfo->get("fogColor")).data().baseAs<float>());
409     }
410     else
411     {
412         R_SetupFogDefaults();
413     }
414 
415     if(mapInfo && !(mapInfo->geti("flags") & MIF_FOG))
416     {
417         GL_UseFog(false);
418     }
419 
420     String fadeTable = (mapInfo? mapInfo->gets("fadeTable") : "");
421     if(!fadeTable.isEmpty())
422     {
423         LumpIndex const &lumps = App_FileSystem().nameIndex();
424         dint lumpNum = lumps.findLast(fadeTable + ".lmp");
425         if(lumpNum == lumps.findLast("COLORMAP.lmp"))
426         {
427             // We don't want fog in this case.
428             GL_UseFog(false);
429         }
430         // Probably fog ... don't use fullbright sprites.
431         else if(lumpNum == lumps.findLast("FOGMAP.lmp"))
432         {
433             GL_UseFog(true);
434         }
435     }
436 }
437 
438 #undef GL_UseFog
GL_UseFog(dint yes)439 DENG_EXTERN_C void GL_UseFog(dint yes)
440 {
441     fogParams.usingFog = yes;
442 }
443 
GL_SelectTexUnits(dint count)444 void GL_SelectTexUnits(dint count)
445 {
446     DENG2_ASSERT_IN_RENDER_THREAD();
447     DENG_ASSERT_GL_CONTEXT_ACTIVE();
448 
449     for (dint i = MAX_TEX_UNITS - 1; i >= count; i--)
450     {
451         DGL_Disable(DGL_TEXTURE0 + i);
452     }
453 
454     // Enable the selected units.
455     for (dint i = count - 1; i >= 0; i--)
456     {
457         if (i >= MAX_TEX_UNITS) continue;
458 
459         DGL_Enable(DGL_TEXTURE0 + i);
460     }
461 }
462 
GL_TotalReset()463 void GL_TotalReset()
464 {
465     if(isDedicated) return;
466 
467     // Release all texture memory.
468     resSys().releaseAllGLTextures();
469     GL_LoadLightingSystemTextures();
470     GL_LoadFlareTextures();
471     Rend_ParticleLoadSystemTextures();
472 
473     GL_ReleaseReservedNames();
474 
475 #if _DEBUG
476     Z_CheckHeap();
477 #endif
478 }
479 
GL_TotalRestore()480 void GL_TotalRestore()
481 {
482     if(isDedicated) return;
483 
484     // Getting back up and running.
485     GL_ReserveNames();
486     GL_Init2DState();
487 
488     // Choose fonts again.
489     UI_LoadFonts();
490     //Con_Resize();
491 
492     // Restore the fog settings.
493     GL_SetupFogFromMapInfo(App_World().hasMap() ? &App_World().map().mapInfo() : nullptr);
494 
495 #ifdef DENG2_DEBUG
496     Z_CheckHeap();
497 #endif
498 }
499 
GL_BlendMode(blendmode_t mode)500 void GL_BlendMode(blendmode_t mode)
501 {
502     DENG2_ASSERT_IN_RENDER_THREAD();
503     DENG_ASSERT_GL_CONTEXT_ACTIVE();
504 
505     switch(mode)
506     {
507     case BM_ZEROALPHA:
508         DGL_BlendOp(DGL_ADD);
509         DGL_BlendFunc(DGL_ONE, DGL_ZERO);
510         break;
511 
512     case BM_ADD:
513         DGL_BlendOp(DGL_ADD);
514         DGL_BlendFunc(DGL_SRC_ALPHA, DGL_ONE);
515         break;
516 
517     case BM_DARK:
518         DGL_BlendOp(DGL_ADD);
519         DGL_BlendFunc(DGL_DST_COLOR, DGL_ONE_MINUS_SRC_ALPHA);
520         break;
521 
522     case BM_SUBTRACT:
523         DGL_BlendOp(DGL_SUBTRACT);
524         DGL_BlendFunc(DGL_ONE, DGL_SRC_ALPHA);
525         break;
526 
527     case BM_ALPHA_SUBTRACT:
528         DGL_BlendOp(DGL_SUBTRACT);
529         DGL_BlendFunc(DGL_SRC_ALPHA, DGL_ONE);
530         break;
531 
532     case BM_REVERSE_SUBTRACT:
533         DGL_BlendOp(DGL_REVERSE_SUBTRACT);
534         DGL_BlendFunc(DGL_SRC_ALPHA, DGL_ONE);
535         break;
536 
537     case BM_MUL:
538         DGL_BlendOp(DGL_ADD);
539         DGL_BlendFunc(DGL_ZERO, DGL_SRC_COLOR);
540         break;
541 
542     case BM_INVERSE:
543         DGL_BlendOp(DGL_ADD);
544         DGL_BlendFunc(DGL_ONE_MINUS_DST_COLOR, DGL_ONE_MINUS_SRC_COLOR);
545         break;
546 
547     case BM_INVERSE_MUL:
548         DGL_BlendOp(DGL_ADD);
549         DGL_BlendFunc(DGL_ZERO, DGL_ONE_MINUS_SRC_COLOR);
550         break;
551 
552     default:
553         DGL_BlendOp(DGL_ADD);
554         DGL_BlendFunc(DGL_SRC_ALPHA, DGL_ONE_MINUS_SRC_ALPHA);
555         break;
556     }
557 }
558 
GL_Filter(gl::Filter f)559 GLenum GL_Filter(gl::Filter f)
560 {
561     switch(f)
562     {
563     case gl::Nearest: return GL_NEAREST;
564     case gl::Linear:  return GL_LINEAR;
565     }
566     return GL_REPEAT;
567 }
568 
GL_Wrap(gl::Wrapping w)569 GLenum GL_Wrap(gl::Wrapping w)
570 {
571     switch(w)
572     {
573     case gl::Repeat:         return GL_REPEAT;
574     case gl::RepeatMirrored: return GL_MIRRORED_REPEAT;
575     case gl::ClampToEdge:    return GL_CLAMP_TO_EDGE;
576     }
577     return GL_REPEAT;
578 }
579 
GL_NumMipmapLevels(dint width,dint height)580 dint GL_NumMipmapLevels(dint width, dint height)
581 {
582     dint numLevels = 0;
583     while(width > 1 || height > 1)
584     {
585         width  /= 2;
586         height /= 2;
587         ++numLevels;
588     }
589     return numLevels;
590 }
591 
GL_OptimalTextureSize(dint width,dint height,dd_bool noStretch,dd_bool isMipMapped,dint * optWidth,dint * optHeight)592 dd_bool GL_OptimalTextureSize(dint width, dint height, dd_bool noStretch, dd_bool isMipMapped,
593     dint *optWidth, dint *optHeight)
594 {
595     DENG2_ASSERT(optWidth && optHeight);
596     if (!isMipMapped)
597     {
598         *optWidth  = width;
599         *optHeight = height;
600     }
601     else if (noStretch)
602     {
603         *optWidth  = M_CeilPow2(width);
604         *optHeight = M_CeilPow2(height);
605     }
606     else
607     {
608         // Determine the most favorable size for the texture.
609         if(texQuality == TEXQ_BEST)
610         {
611             // At the best texture quality *opt, all textures are
612             // sized *upwards*, so no details are lost. This takes
613             // more memory, but naturally looks better.
614             *optWidth  = M_CeilPow2(width);
615             *optHeight = M_CeilPow2(height);
616         }
617         else if(texQuality == 0)
618         {
619             // At the lowest quality, all textures are sized down to the
620             // nearest power of 2.
621             *optWidth  = M_FloorPow2(width);
622             *optHeight = M_FloorPow2(height);
623         }
624         else
625         {
626             // At the other quality *opts, a weighted rounding is used.
627             *optWidth  = M_WeightPow2(width,  1 - texQuality / dfloat( TEXQ_BEST ));
628             *optHeight = M_WeightPow2(height, 1 - texQuality / dfloat( TEXQ_BEST ));
629         }
630     }
631 
632     // Hardware limitations may force us to modify the preferred size.
633     if(*optWidth > GLInfo::limits().maxTexSize)
634     {
635         *optWidth = GLInfo::limits().maxTexSize;
636         noStretch = false;
637     }
638     if(*optHeight > GLInfo::limits().maxTexSize)
639     {
640         *optHeight = GLInfo::limits().maxTexSize;
641         noStretch = false;
642     }
643 
644     // Some GL drivers seem to have problems with VERY small textures.
645     if(*optWidth < MINTEXWIDTH)
646         *optWidth = MINTEXWIDTH;
647     if(*optHeight < MINTEXHEIGHT)
648         *optHeight = MINTEXHEIGHT;
649 
650     if(ratioLimit)
651     {
652         if(*optWidth > *optHeight)  // Wide texture.
653         {
654             if(*optHeight < *optWidth / ratioLimit)
655                 *optHeight = *optWidth / ratioLimit;
656         }
657         else  // Tall texture.
658         {
659             if(*optWidth < *optHeight / ratioLimit)
660                 *optWidth = *optHeight / ratioLimit;
661         }
662     }
663 
664     return noStretch;
665 }
666 
GL_GetTexAnisoMul(dint level)667 dint GL_GetTexAnisoMul(dint level)
668 {
669     // Should anisotropic filtering be used?
670     if(!GL_state.features.texFilterAniso)
671         return 1;
672 
673     if(level < 0)
674     {
675         // Go with the maximum!
676         return GLInfo::limits().maxTexFilterAniso;
677     }
678 
679     // Convert from a DGL aniso-level to a multiplier.
680     dint mul;
681     switch(level)
682     {
683     default: mul =  1; break;
684     case 1:  mul =  2; break;
685     case 2:  mul =  4; break;
686     case 3:  mul =  8; break;
687     case 4:  mul = 16; break;
688     }
689 
690     // Clamp.
691     return de::min(mul, GLInfo::limits().maxTexFilterAniso);
692 }
693 
uploadContentUnmanaged(texturecontent_t const & content)694 static void uploadContentUnmanaged(texturecontent_t const &content)
695 {
696     LOG_AS("uploadContentUnmanaged");
697     if(novideo) return;
698 
699     gl::UploadMethod uploadMethod = GL_ChooseUploadMethod(&content);
700     if(uploadMethod == gl::Immediate)
701     {
702         LOGDEV_GL_XVERBOSE("Uploading texture (%i:%ix%i) while not busy! "
703                            "Should have been precached in busy mode?",
704                            content.name << content.width << content.height);
705     }
706 
707     GL_UploadTextureContent(content, uploadMethod);
708 }
709 
GL_NewTextureWithParams(dgltexformat_t format,dint width,dint height,duint8 const * pixels,dint flags)710 GLuint GL_NewTextureWithParams(dgltexformat_t format, dint width, dint height,
711     duint8 const *pixels, dint flags)
712 {
713     texturecontent_t c;
714     GL_InitTextureContent(&c);
715     c.name   = GL_GetReservedTextureName();
716     c.format = format;
717     c.width  = width;
718     c.height = height;
719     c.pixels = pixels;
720     c.flags  = flags;
721 
722     uploadContentUnmanaged(c);
723     return c.name;
724 }
725 
GL_NewTextureWithParams(dgltexformat_t format,dint width,dint height,uint8_t const * pixels,dint flags,dint grayMipmap,dint minFilter,dint magFilter,dint anisoFilter,dint wrapS,dint wrapT)726 GLuint GL_NewTextureWithParams(dgltexformat_t format, dint width, dint height,
727     uint8_t const *pixels, dint flags, dint grayMipmap, dint minFilter, dint magFilter,
728     dint anisoFilter, dint wrapS, dint wrapT)
729 {
730     texturecontent_t c;
731     GL_InitTextureContent(&c);
732     c.name        = GL_GetReservedTextureName();
733     c.format      = format;
734     c.width       = width;
735     c.height      = height;
736     c.pixels      = pixels;
737     c.flags       = flags;
738     c.grayMipmap  = grayMipmap;
739     c.minFilter   = minFilter;
740     c.magFilter   = magFilter;
741     c.anisoFilter = anisoFilter;
742     c.wrap[0]     = wrapS;
743     c.wrap[1]     = wrapT;
744 
745     uploadContentUnmanaged(c);
746     return c.name;
747 }
748 
uiMaterialSpec(gl::Wrapping wrapS,gl::Wrapping wrapT)749 static inline MaterialVariantSpec const &uiMaterialSpec(gl::Wrapping wrapS, gl::Wrapping wrapT)
750 {
751     return resSys().materialSpec(UiContext, 0, 1, 0, 0, GL_Wrap(wrapS), GL_Wrap(wrapT),
752                                  0, 1, 0, false, false, false, false);
753 }
754 
pspriteMaterialSpec(dint tClass,dint tMap)755 static inline MaterialVariantSpec const &pspriteMaterialSpec(dint tClass, dint tMap)
756 {
757     return resSys().materialSpec(PSpriteContext, 0, 1, tClass, tMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE,
758                                  0, -2, 0, false, true, true, false);
759 }
760 
GL_SetMaterialUI2(world::Material * material,gl::Wrapping wrapS,gl::Wrapping wrapT)761 void GL_SetMaterialUI2(world::Material *material, gl::Wrapping wrapS, gl::Wrapping wrapT)
762 {
763     if(!material) return; // @todo we need a "NULL material".
764 
765     MaterialAnimator &matAnimator = static_cast<ClientMaterial *>(material)->getAnimator(uiMaterialSpec(wrapS, wrapT));
766 
767     // Ensure we have up to date info about the material.
768     matAnimator.prepare();
769 
770     GL_BindTexture(matAnimator.texUnit(MaterialAnimator::TU_LAYER0).texture);
771 }
772 
GL_SetMaterialUI(world::Material * mat)773 void GL_SetMaterialUI(world::Material *mat)
774 {
775     GL_SetMaterialUI2(mat, gl::ClampToEdge, gl::ClampToEdge);
776 }
777 
GL_SetPSprite(world::Material * material,dint tClass,dint tMap)778 void GL_SetPSprite(world::Material *material, dint tClass, dint tMap)
779 {
780     if(!material) return;
781 
782     MaterialAnimator &matAnimator = static_cast<ClientMaterial *>(material)->getAnimator(pspriteMaterialSpec(tClass, tMap));
783 
784     // Ensure we have up to date info about the material.
785     matAnimator.prepare();
786 
787     GL_BindTexture(matAnimator.texUnit(MaterialAnimator::TU_LAYER0).texture);
788 }
789 
GL_SetRawImage(lumpnum_t lumpNum,gl::Wrapping wrapS,gl::Wrapping wrapT)790 void GL_SetRawImage(lumpnum_t lumpNum, gl::Wrapping wrapS, gl::Wrapping wrapT)
791 {
792     if(rawtex_t *rawTex = ClientResources::get().declareRawTexture(lumpNum))
793     {
794         GL_BindTextureUnmanaged(GL_PrepareRawTexture(*rawTex), wrapS, wrapT,
795                                 (filterUI ? gl::Linear : gl::Nearest));
796     }
797 }
798 
GL_BindTexture(TextureVariant * vtexture)799 void GL_BindTexture(TextureVariant *vtexture)
800 {
801 #if defined (DENG_HAVE_BUSYRUNNER)
802     if (ClientApp::busyRunner().inWorkerThread()) return;
803 #endif
804 
805     // Ensure we have a prepared texture.
806     duint glTexName = vtexture? vtexture->prepare() : 0;
807     if(glTexName == 0)
808     {
809         GL_SetNoTexture();
810         return;
811     }
812 
813     DENG2_ASSERT_IN_RENDER_THREAD();
814     DENG_ASSERT_GL_CONTEXT_ACTIVE();
815 
816     LIBGUI_GL.glBindTexture(GL_TEXTURE_2D, glTexName);
817     LIBGUI_ASSERT_GL_OK();
818 
819     // Apply dynamic adjustments to the GL texture state according to our spec.
820     TextureVariantSpec const &spec = vtexture->spec();
821     if(spec.type == TST_GENERAL)
822     {
823         LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, spec.variant.wrapS);
824         LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, spec.variant.wrapT);
825 
826         LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, spec.variant.glMagFilter());
827         if(GL_state.features.texFilterAniso)
828         {
829             LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
830                             GL_GetTexAnisoMul(spec.variant.logicalAnisoLevel()));
831         }
832         LIBGUI_ASSERT_GL_OK();
833     }
834 }
835 
GL_BindTextureUnmanaged(GLuint glName,gl::Wrapping wrapS,gl::Wrapping wrapT,gl::Filter filter)836 void GL_BindTextureUnmanaged(GLuint glName, gl::Wrapping wrapS, gl::Wrapping wrapT,
837     gl::Filter filter)
838 {
839 #if defined (DENG_HAVE_BUSYRUNNER)
840     if (ClientApp::busyRunner().inWorkerThread()) return;
841 #endif
842 
843     LIBGUI_ASSERT_GL_OK();
844 
845     if(glName == 0)
846     {
847         GL_SetNoTexture();
848         return;
849     }
850 
851     DENG2_ASSERT_IN_RENDER_THREAD();
852     DENG_ASSERT_GL_CONTEXT_ACTIVE();
853 
854     LIBGUI_GL.glBindTexture(GL_TEXTURE_2D, glName);
855     LIBGUI_ASSERT_GL_OK();
856 
857     LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_Wrap(wrapS));
858     LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_Wrap(wrapT));
859     LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_Filter(filter));
860     if(GL_state.features.texFilterAniso)
861     {
862         LIBGUI_GL.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, GL_GetTexAnisoMul(texAniso));
863     }
864     LIBGUI_ASSERT_GL_OK();
865 }
866 
GL_Bind(GLTextureUnit const & glTU)867 void GL_Bind(GLTextureUnit const &glTU)
868 {
869     if(!glTU.hasTexture()) return;
870 
871     if(!renderTextures)
872     {
873         GL_SetNoTexture();
874         return;
875     }
876 
877     if(glTU.texture)
878     {
879         GL_BindTexture(glTU.texture);
880     }
881     else
882     {
883         GL_BindTextureUnmanaged(glTU.unmanaged.glName, glTU.unmanaged.wrapS,
884                                 glTU.unmanaged.wrapT, glTU.unmanaged.filter);
885     }
886 }
887 
GL_BindTo(GLTextureUnit const & glTU,dint unit)888 void GL_BindTo(GLTextureUnit const &glTU, dint unit)
889 {
890     if(!glTU.hasTexture()) return;
891 
892     DENG2_ASSERT_IN_RENDER_THREAD();
893     DENG_ASSERT_GL_CONTEXT_ACTIVE();
894     DGL_SetInteger(DGL_ACTIVE_TEXTURE, unit);
895     GL_Bind(glTU);
896 }
897 
GL_SetNoTexture()898 void GL_SetNoTexture()
899 {
900 #if defined (DENG_HAVE_BUSYRUNNER)
901     if(ClientApp::busyRunner().inWorkerThread()) return;
902 #endif
903 
904     DENG2_ASSERT_IN_RENDER_THREAD();
905     DENG_ASSERT_GL_CONTEXT_ACTIVE();
906 
907     /// @todo Don't actually change the current binding. Instead we should disable
908     ///       all currently enabled texture types.
909     LIBGUI_GL.glBindTexture(GL_TEXTURE_2D, 0);
910 }
911 
GL_ChooseSmartFilter(dint width,dint height,dint)912 dint GL_ChooseSmartFilter(dint width, dint height, dint /*flags*/)
913 {
914     if(width >= MINTEXWIDTH && height >= MINTEXHEIGHT)
915         return 2;  // hq2x
916     return 1;  // nearest neighbor.
917 }
918 
GL_SmartFilter(dint method,duint8 const * src,dint width,dint height,dint flags,dint * outWidth,dint * outHeight)919 duint8 *GL_SmartFilter(dint method, duint8 const *src, dint width, dint height,
920     dint flags, dint *outWidth, dint *outHeight)
921 {
922     dint newWidth, newHeight;
923     duint8 *out = nullptr;
924 
925     switch(method)
926     {
927     default:  // linear interpolation.
928         newWidth  = width  * 2;
929         newHeight = height * 2;
930         out = GL_ScaleBuffer(src, width, height, 4, newWidth, newHeight);
931         break;
932 
933     case 1:  // nearest neighbor.
934         newWidth  = width  * 2;
935         newHeight = height * 2;
936         out = GL_ScaleBufferNearest(src, width, height, 4, newWidth, newHeight);
937         break;
938 
939     case 2:  // hq2x
940         newWidth  = width  * 2;
941         newHeight = height * 2;
942         out = GL_SmartFilterHQ2x(src, width, height, flags);
943         break;
944     };
945 
946     if(!out)
947     {
948         // Unchanged, return the source image.
949         if(outWidth)  *outWidth  = width;
950         if(outHeight) *outHeight = height;
951         return const_cast<duint8 *>(src);
952     }
953 
954     if(outWidth)  *outWidth  = newWidth;
955     if(outHeight) *outHeight = newHeight;
956     return out;
957 }
958 
GL_ConvertBuffer(duint8 const * in,dint width,dint height,dint informat,colorpaletteid_t paletteId,dint outformat)959 duint8 *GL_ConvertBuffer(duint8 const *in, dint width, dint height, dint informat,
960                          colorpaletteid_t paletteId, dint outformat)
961 {
962     DENG2_ASSERT(in);
963 
964     if(informat == outformat)
965     {
966         // No conversion necessary.
967         return const_cast<duint8 *>(in);
968     }
969 
970     if(width <= 0 || height <= 0)
971     {
972         App_Error("GL_ConvertBuffer: Attempt to convert zero-sized image.");
973         exit(1); // Unreachable.
974     }
975 
976     res::ColorPalette *palette = (informat <= 2? &resSys().colorPalettes().colorPalette(paletteId) : nullptr);
977 
978     auto *out = (duint8 *) M_Malloc(outformat * width * height);
979 
980     // Conversion from pal8(a) to RGB(A).
981     if(informat <= 2 && outformat >= 3)
982     {
983         GL_PalettizeImage(out, outformat, palette, false, in, informat, width, height);
984         return out;
985     }
986 
987     // Conversion from RGB(A) to pal8(a), using pal18To8.
988     if(informat >= 3 && outformat <= 2)
989     {
990         GL_QuantizeImageToPalette(out, outformat, palette, in, informat, width, height);
991         return out;
992     }
993 
994     if(informat == 3 && outformat == 4)
995     {
996         long const numPels = width * height;
997         duint8 const *src  = in;
998         duint8 *dst        = out;
999         for(long i = 0; i < numPels; ++i)
1000         {
1001             dst[0] = src[0];
1002             dst[1] = src[1];
1003             dst[2] = src[2];
1004             dst[3] = 255;  // Opaque.
1005 
1006             src += informat;
1007             dst += outformat;
1008         }
1009     }
1010     return out;
1011 }
1012 
GL_CalcLuminance(duint8 const * buffer,dint width,dint height,dint pixelSize,colorpaletteid_t paletteId,dfloat * retBrightX,dfloat * retBrightY,ColorRawf * retColor,dfloat * retLumSize)1013 void GL_CalcLuminance(duint8 const *buffer, dint width, dint height, dint pixelSize,
1014     colorpaletteid_t paletteId, dfloat *retBrightX, dfloat *retBrightY,
1015     ColorRawf *retColor, dfloat *retLumSize)
1016 {
1017     DENG2_ASSERT(buffer && retBrightX && retBrightY && retColor && retLumSize);
1018 
1019     static duint8 const sizeLimit = 192, brightLimit = 224, colLimit = 192;
1020 
1021     res::ColorPalette *palette = (pixelSize == 1? &resSys().colorPalettes().colorPalette(paletteId) : nullptr);
1022 
1023     // Apply the defaults.
1024     // Default to the center of the texture.
1025     *retBrightX = *retBrightY = .5f;
1026 
1027     // Default to black (i.e., no light).
1028     for(dint c = 0; c < 3; ++c)
1029     {
1030         retColor->rgb[c] = 0;
1031     }
1032     retColor->alpha = 1;
1033 
1034     // Default to a zero-size light.
1035     *retLumSize = 0;
1036 
1037     dint region[4];
1038     FindClipRegionNonAlpha(buffer, width, height, pixelSize, region);
1039     dd_bool zeroAreaRegion = (region[0] > region[1] || region[2] > region[3]);
1040     if(zeroAreaRegion) return;
1041 
1042     //
1043     // Image contains at least one non-transparent pixel.
1044     //
1045 
1046     long bright[2];
1047     for(dint i = 0; i < 2; ++i)
1048     {
1049         bright[i] = 0;
1050     }
1051 
1052     long average[3], lowAvg[3];
1053     for(dint i = 0; i < 3; ++i)
1054     {
1055         average[i] = 0;
1056         lowAvg[i]  = 0;
1057     }
1058 
1059     duint8 const *src = buffer;
1060     // In paletted mode, the alpha channel follows the actual image.
1061     duint8 const *alphaSrc = &buffer[width * height];
1062 
1063     // Skip to the start of the first column.
1064     if(region[2] > 0)
1065     {
1066         src      += pixelSize * width * region[2];
1067         alphaSrc += width * region[2];
1068     }
1069 
1070     duint8 rgb[3] = { 0, 0, 0 };
1071     dint avgCnt = 0, lowCnt = 0;
1072     dint cnt = 0, posCnt = 0;
1073     for(dint y = region[2]; y <= region[3]; ++y)
1074     {
1075         // Skip to the beginning of the row.
1076         if(region[0] > 0)
1077         {
1078             src      += pixelSize * region[0];
1079             alphaSrc += region[0];
1080         }
1081 
1082         for(dint x = region[0]; x <= region[1]; ++x, src += pixelSize, alphaSrc++)
1083         {
1084             // Alpha pixels don't count. Why? -ds
1085             dd_bool const pixelIsTransparent = (pixelSize == 1? *alphaSrc < 255 :
1086                                                 pixelSize == 4?    src[3] < 255 : false);
1087 
1088             if(pixelIsTransparent) continue;
1089 
1090             if(pixelSize == 1)
1091             {
1092                 Vector3ub palColor = palette->color(*src);
1093                 rgb[0] = palColor.x;
1094                 rgb[1] = palColor.y;
1095                 rgb[2] = palColor.z;
1096             }
1097             else if(pixelSize >= 3)
1098             {
1099                 std::memcpy(rgb, src, 3);
1100             }
1101 
1102             // Bright enough?
1103             if(rgb[0] > brightLimit || rgb[1] > brightLimit || rgb[2] > brightLimit)
1104             {
1105                 // This pixel will participate in calculating the average center point.
1106                 posCnt++;
1107                 bright[0] += x;
1108                 bright[1] += y;
1109             }
1110 
1111             // Bright enough to affect size?
1112             if(rgb[0] > sizeLimit || rgb[1] > sizeLimit || rgb[2] > sizeLimit)
1113                 cnt++;
1114 
1115             // How about the color of the light?
1116             if(rgb[0] > colLimit || rgb[1] > colLimit || rgb[2] > colLimit)
1117             {
1118                 avgCnt++;
1119                 for(dint c = 0; c < 3; ++c)
1120                 {
1121                     average[c] += rgb[c];
1122                 }
1123             }
1124             else
1125             {
1126                 lowCnt++;
1127                 for(dint c = 0; c < 3; ++c)
1128                 {
1129                     lowAvg[c] += rgb[c];
1130                 }
1131             }
1132         }
1133 
1134         // Skip to the end of this row.
1135         if(region[1] < width - 1)
1136         {
1137             src      += pixelSize * (width - 1 - region[1]);
1138             alphaSrc += (width - 1 - region[1]);
1139         }
1140     }
1141 
1142     if(posCnt)
1143     {
1144         // Calculate the average of the bright pixels.
1145         *retBrightX = (long double) bright[0] / posCnt;
1146         *retBrightY = (long double) bright[1] / posCnt;
1147     }
1148     else
1149     {
1150         // No bright pixels - Place the origin at the center of the non-alpha region.
1151         *retBrightX = region[0] + (region[1] - region[0]) / 2.0f;
1152         *retBrightY = region[2] + (region[3] - region[2]) / 2.0f;
1153     }
1154 
1155     // Determine rounding (to the nearest pixel center).
1156     dint roundXDir = dint( *retBrightX + .5f ) == dint( *retBrightX )? 1 : -1;
1157     dint roundYDir = dint( *retBrightY + .5f ) == dint( *retBrightY )? 1 : -1;
1158 
1159     // Apply all rounding and output as decimal.
1160     *retBrightX = (ROUND(*retBrightX) + .5f * roundXDir) / dfloat( width );
1161     *retBrightY = (ROUND(*retBrightY) + .5f * roundYDir) / dfloat( height );
1162 
1163     if(avgCnt || lowCnt)
1164     {
1165         // The color.
1166         if(!avgCnt)
1167         {
1168             // Low-intensity color average.
1169             for(dint c = 0; c < 3; ++c)
1170             {
1171                 retColor->rgb[c] = lowAvg[c] / lowCnt / 255.f;
1172             }
1173         }
1174         else
1175         {
1176             // High-intensity color average.
1177             for(dint c = 0; c < 3; ++c)
1178             {
1179                 retColor->rgb[c] = average[c] / avgCnt / 255.f;
1180             }
1181         }
1182 
1183         Vector3f color(retColor->rgb);
1184         R_AmplifyColor(color);
1185         for(dint i = 0; i < 3; ++i)
1186         {
1187             retColor->rgb[i] = color[i];
1188         }
1189 
1190         // How about the size of the light source?
1191         /// @todo These factors should be cvars.
1192         *retLumSize = MIN_OF(((2 * cnt + avgCnt) / 3.0f / 70.0f), 1);
1193     }
1194 
1195     /*
1196     DEBUG_Message(("GL_CalcLuminance: width %dpx, height %dpx, bits %d\n"
1197                    "  cell region X[%d, %d] Y[%d, %d]\n"
1198                    "  flare X= %g Y=%g %s\n"
1199                    "  flare RGB[%g, %g, %g] %s\n",
1200                    width, height, pixelSize,
1201                    region[0], region[1], region[2], region[3],
1202                    *retBrightX, *retBrightY,
1203                    (posCnt? "(average)" : "(center)"),
1204                    retColor->red, retColor->green, retColor->blue,
1205                    (avgCnt? "(hi-intensity avg)" :
1206                     lowCnt? "(low-intensity avg)" : "(white light)")));
1207     */
1208 }
1209 
1210 #if !defined (DENG_MOBILE)
1211 
D_CMD(SetRes)1212 D_CMD(SetRes)
1213 {
1214     DENG2_UNUSED3(src, argc, argv);
1215 
1216     ClientWindow *win = ClientWindowSystem::mainPtr();
1217     if(!win) return false;
1218 
1219     bool isFull = win->isFullScreen();
1220 
1221     dint attribs[] = {
1222         isFull? ClientWindow::FullscreenWidth  : ClientWindow::Width,  String(argv[1]).toInt(),
1223         isFull? ClientWindow::FullscreenHeight : ClientWindow::Height, String(argv[2]).toInt(),
1224         ClientWindow::End
1225     };
1226     return win->changeAttributes(attribs);
1227 }
1228 
D_CMD(SetFullRes)1229 D_CMD(SetFullRes)
1230 {
1231     DENG2_UNUSED2(src, argc);
1232 
1233     ClientWindow *win = ClientWindowSystem::mainPtr();
1234     if(!win) return false;
1235 
1236     dint attribs[] = {
1237         ClientWindow::FullscreenWidth,  String(argv[1]).toInt(),
1238         ClientWindow::FullscreenHeight, String(argv[2]).toInt(),
1239         ClientWindow::Fullscreen,       true,
1240         ClientWindow::End
1241     };
1242     return win->changeAttributes(attribs);
1243 }
1244 
D_CMD(SetWinRes)1245 D_CMD(SetWinRes)
1246 {
1247     DENG2_UNUSED2(src, argc);
1248 
1249     ClientWindow *win = ClientWindowSystem::mainPtr();
1250     if(!win) return false;
1251 
1252     dint attribs[] = {
1253         ClientWindow::Width,      String(argv[1]).toInt(),
1254         ClientWindow::Height,     String(argv[2]).toInt(),
1255         ClientWindow::Fullscreen, false,
1256         ClientWindow::Maximized,  false,
1257         ClientWindow::End
1258     };
1259     return win->changeAttributes(attribs);
1260 }
1261 
D_CMD(ToggleFullscreen)1262 D_CMD(ToggleFullscreen)
1263 {
1264     DENG2_UNUSED3(src, argc, argv);
1265 
1266     ClientWindow *win = ClientWindowSystem::mainPtr();
1267     if(!win) return false;
1268 
1269     dint attribs[] = {
1270         ClientWindow::Fullscreen, !win->isFullScreen(),
1271         ClientWindow::End
1272     };
1273     return win->changeAttributes(attribs);
1274 }
1275 
D_CMD(ToggleMaximized)1276 D_CMD(ToggleMaximized)
1277 {
1278     DENG2_UNUSED3(src, argc, argv);
1279 
1280     ClientWindow *win = ClientWindowSystem::mainPtr();
1281     if(!win) return false;
1282 
1283     dint attribs[] = {
1284         ClientWindow::Maximized, !win->isMaximized(),
1285         ClientWindow::End
1286     };
1287     return win->changeAttributes(attribs);
1288 }
1289 
D_CMD(ToggleCentered)1290 D_CMD(ToggleCentered)
1291 {
1292     DENG2_UNUSED3(src, argc, argv);
1293 
1294     ClientWindow *win = ClientWindowSystem::mainPtr();
1295     if(!win) return false;
1296 
1297     dint attribs[] = {
1298         ClientWindow::Centered, !win->isCentered(),
1299         ClientWindow::End
1300     };
1301     return win->changeAttributes(attribs);
1302 }
1303 
D_CMD(CenterWindow)1304 D_CMD(CenterWindow)
1305 {
1306     DENG2_UNUSED3(src, argc, argv);
1307 
1308     ClientWindow *win = ClientWindowSystem::mainPtr();
1309     if(!win) return false;
1310 
1311     dint attribs[] = {
1312         ClientWindow::Centered, true,
1313         ClientWindow::End
1314     };
1315     return win->changeAttributes(attribs);
1316 }
1317 
D_CMD(SetBPP)1318 D_CMD(SetBPP)
1319 {
1320     DENG2_UNUSED2(src, argc);
1321 
1322     ClientWindow *win = ClientWindowSystem::mainPtr();
1323     if(!win) return false;
1324 
1325     dint attribs[] = {
1326         ClientWindow::ColorDepthBits, String(argv[1]).toInt(),
1327         ClientWindow::End
1328     };
1329     return win->changeAttributes(attribs);
1330 }
1331 
1332 #endif // !DENG_MOBILE
1333 
D_CMD(DisplayModeInfo)1334 D_CMD(DisplayModeInfo)
1335 {
1336     DENG2_UNUSED3(src, argc, argv);
1337 
1338     ClientWindow *win = ClientWindowSystem::mainPtr();
1339     if(!win) return false;
1340 
1341     DisplayMode const *mode = DisplayMode_Current();
1342 
1343     String str = String("Current display mode:%1 depth:%2 (%3:%4")
1344                      .arg(Vector2i(mode->width, mode->height).asText())
1345                      .arg(mode->depth)
1346                      .arg(mode->ratioX)
1347                      .arg(mode->ratioY);
1348     if(mode->refreshRate > 0)
1349     {
1350         str += String(", refresh: %1 Hz").arg(mode->refreshRate, 0, 'f', 1);
1351     }
1352     str += String(")\nMain window:\n  current origin:%1 size:%2"
1353                   "\n  windowed origin:%3 size:%4"
1354                   "\n  fullscreen size:%5")
1355                .arg(win->pos().asText())
1356                .arg(win->pointSize().asText())
1357                .arg(win->windowRect().topLeft.asText())
1358                .arg(win->windowRect().size().asText())
1359                .arg(win->fullscreenSize().asText());
1360 
1361 #if !defined (DENG_MOBILE)
1362     str += String("\n  fullscreen:%1 centered:%2 maximized:%3")
1363                .arg(DENG2_BOOL_YESNO( win->isFullScreen() ))
1364                .arg(DENG2_BOOL_YESNO( win->isCentered()   ))
1365                .arg(DENG2_BOOL_YESNO( win->isMaximized()  ));
1366 #endif
1367 
1368     LOG_GL_MSG("%s") << str;
1369     return true;
1370 }
1371 
D_CMD(ListDisplayModes)1372 D_CMD(ListDisplayModes)
1373 {
1374     DENG2_UNUSED3(src, argc, argv);
1375 
1376     LOG_GL_MSG("There are %i display modes available:") << DisplayMode_Count();
1377     for(dint i = 0; i < DisplayMode_Count(); ++i)
1378     {
1379         DisplayMode const *mode = DisplayMode_ByIndex(i);
1380         if(mode->refreshRate > 0)
1381         {
1382             LOG_GL_MSG("  %i x %i x %i " _E(>) "(%i:%i, refresh: %.1f Hz)")
1383                     << mode->width << mode->height << mode->depth
1384                     << mode->ratioX << mode->ratioY << mode->refreshRate;
1385         }
1386         else
1387         {
1388             LOG_GL_MSG("  %i x %i x %i (%i:%i)")
1389                     << mode->width << mode->height << mode->depth
1390                     << mode->ratioX << mode->ratioY;
1391         }
1392     }
1393     return true;
1394 }
1395 
1396 #if 0
1397 D_CMD(UpdateGammaRamp)
1398 {
1399     DENG2_UNUSED3(src, argc, argv);
1400 
1401     GL_SetGamma();
1402     LOG_GL_VERBOSE("Gamma ramp set");
1403     return true;
1404 }
1405 #endif
1406 
D_CMD(Fog)1407 D_CMD(Fog)
1408 {
1409     DENG2_UNUSED(src);
1410 
1411     if(argc == 1)
1412     {
1413         LOG_SCR_NOTE("Usage: %s (cmd) (args)") << argv[0];
1414         LOG_SCR_MSG("Commands: on, off, mode, color, start, end, density");
1415         LOG_SCR_MSG("Modes: linear, exp, exp2");
1416         LOG_SCR_MSG("Color is given as RGB (0-255)");
1417         LOG_SCR_MSG("Start and end are for linear fog, density for exponential fog.");
1418         return true;
1419     }
1420 
1421     if(!stricmp(argv[1], "on"))
1422     {
1423         GL_UseFog(true);
1424         LOG_GL_VERBOSE("Fog is now active");
1425         return true;
1426     }
1427     if(!stricmp(argv[1], "off"))
1428     {
1429         GL_UseFog(false);
1430         LOG_GL_VERBOSE("Fog is now disabled");
1431         return true;
1432     }
1433     if(!stricmp(argv[1], "color") && argc == 5)
1434     {
1435         for(dint i = 0; i < 3; ++i)
1436         {
1437             fogParams.fogColor[i] = strtol(argv[2 + i], nullptr, 0) / 255.0f;
1438         }
1439         fogParams.fogColor[3] = 1;
1440 
1441         DGL_Fogfv(DGL_FOG_COLOR, fogParams.fogColor);
1442         LOG_GL_VERBOSE("Fog color set");
1443         return true;
1444     }
1445     if(!stricmp(argv[1], "start") && argc == 3)
1446     {
1447         fogParams.fogStart = (GLfloat) strtod(argv[2], nullptr);
1448 
1449         DGL_Fogf(DGL_FOG_START, fogParams.fogStart);
1450         LOG_GL_VERBOSE("Fog start distance set");
1451         return true;
1452     }
1453     if(!stricmp(argv[1], "end") && argc == 3)
1454     {
1455         fogParams.fogEnd = (GLfloat) strtod(argv[2], nullptr);
1456         DGL_Fogf(DGL_FOG_END, fogParams.fogEnd);
1457         LOG_GL_VERBOSE("Fog end distance set");
1458         return true;
1459     }
1460     if(!stricmp(argv[1], "density") && argc == 3)
1461     {
1462         DGL_Fogf(DGL_FOG_DENSITY, (GLfloat) strtod(argv[2], nullptr));
1463         LOG_GL_VERBOSE("Fog density set");
1464         return true;
1465     }
1466     if(!stricmp(argv[1], "mode") && argc == 3)
1467     {
1468         if(!stricmp(argv[2], "linear"))
1469         {
1470             DGL_Fogi(DGL_FOG_MODE, DGL_LINEAR);
1471             LOG_GL_VERBOSE("Fog mode set to linear");
1472             return true;
1473         }
1474         if(!stricmp(argv[2], "exp"))
1475         {
1476             DGL_Fogi(DGL_FOG_MODE, DGL_EXP);
1477             LOG_GL_VERBOSE("Fog mode set to exp");
1478             return true;
1479         }
1480         if(!stricmp(argv[2], "exp2"))
1481         {
1482             DGL_Fogi(DGL_FOG_MODE, DGL_EXP2);
1483             LOG_GL_VERBOSE("Fog mode set to exp2");
1484             return true;
1485         }
1486     }
1487 
1488     return false;
1489 }
1490 
GL_Register()1491 void GL_Register()
1492 {
1493     // Cvars
1494 #if defined (DENG_OPENGL)
1495     C_VAR_INT  ("rend-dev-wireframe",    &renderWireframe,  CVF_NO_ARCHIVE, 0, 2);
1496 #endif
1497     C_VAR_INT  ("rend-fog-default",      &fogModeDefault,   0, 0, 2);
1498 
1499     // * Render-HUD
1500     C_VAR_FLOAT("rend-hud-offset-scale", &weaponOffsetScale,CVF_NO_MAX, 0, 0);
1501     C_VAR_FLOAT("rend-hud-fov-shift",    &weaponFOVShift,   CVF_NO_MAX, 0, 1);
1502     C_VAR_BYTE ("rend-hud-stretch",      &weaponScaleMode,  0, SCALEMODE_FIRST, SCALEMODE_LAST);
1503 
1504     // * Render-Mobj
1505     C_VAR_INT  ("rend-mobj-smooth-move", &useSRVO,          0, 0, 2);
1506     C_VAR_INT  ("rend-mobj-smooth-turn", &useSRVOAngle,     0, 0, 1);
1507 
1508     // * video
1509     C_VAR_FLOAT("vid-gamma",             &vid_gamma,        0, 0.1f, 3);
1510     C_VAR_FLOAT("vid-contrast",          &vid_contrast,     0, 0, 2.5f);
1511     C_VAR_FLOAT("vid-bright",            &vid_bright,       0, -1, 1);
1512 
1513     Con_AddMappedConfigVariable("vid-vsync", "i", "window.main.vsync");
1514     Con_AddMappedConfigVariable("vid-fsaa",  "i", "window.main.fsaa");
1515     Con_AddMappedConfigVariable("vid-fps",   "i", "window.main.showFps");
1516 
1517     // Ccmds
1518     C_CMD_FLAGS("fog",              nullptr,   Fog,                CMDF_NO_NULLGAME|CMDF_NO_DEDICATED);
1519     C_CMD      ("displaymode",      "",     DisplayModeInfo);
1520     C_CMD      ("listdisplaymodes", "",     ListDisplayModes);
1521 #if !defined (DENG_MOBILE)
1522     C_CMD      ("setcolordepth",    "i",    SetBPP);
1523     C_CMD      ("setbpp",           "i",    SetBPP);
1524     C_CMD      ("setres",           "ii",   SetRes);
1525     C_CMD      ("setfullres",       "ii",   SetFullRes);
1526     C_CMD      ("setwinres",        "ii",   SetWinRes);
1527 //    C_CMD      ("setvidramp",       "",     UpdateGammaRamp);
1528     C_CMD      ("togglefullscreen", "",     ToggleFullscreen);
1529     C_CMD      ("togglemaximized",  "",     ToggleMaximized);
1530     C_CMD      ("togglecentered",   "",     ToggleCentered);
1531     C_CMD      ("centerwindow",     "",     CenterWindow);
1532 #endif
1533 }
1534