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