LCOV - code coverage report
Current view: top level - engine/render - rendergl.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 15 979 1.5 %
Date: 2024-11-22 05:07:59 Functions: 7 82 8.5 %

          Line data    Source code
       1             : /* rendergl.cpp: core opengl rendering stuff
       2             :  *
       3             :  * rendergl.cpp handles the main rendering functions, which render the scene
       4             :  * using OpenGL features aliased in this file. This file also handles the
       5             :  * position of the camera and the projection frustum handling.
       6             :  *
       7             :  * While this file does not handle light and texture rendering, it does handle
       8             :  * the simple world depth fog in libprimis.
       9             :  */
      10             : #include "../libprimis-headers/cube.h"
      11             : #include "../../shared/geomexts.h"
      12             : #include "../../shared/glemu.h"
      13             : #include "../../shared/glexts.h"
      14             : 
      15             : #include "aa.h"
      16             : #include "grass.h"
      17             : #include "hdr.h"
      18             : #include "hud.h"
      19             : #include "octarender.h"
      20             : #include "postfx.h"
      21             : #include "radiancehints.h"
      22             : #include "rendergl.h"
      23             : #include "renderlights.h"
      24             : #include "rendermodel.h"
      25             : #include "renderparticles.h"
      26             : #include "rendersky.h"
      27             : #include "rendertimers.h"
      28             : #include "renderva.h"
      29             : #include "renderwindow.h"
      30             : #include "shader.h"
      31             : #include "shaderparam.h"
      32             : #include "texture.h"
      33             : #include "water.h"
      34             : 
      35             : #include "world/material.h"
      36             : #include "world/octaedit.h"
      37             : #include "world/octaworld.h"
      38             : #include "world/raycube.h"
      39             : #include "world/world.h"
      40             : 
      41             : #include "interface/console.h"
      42             : #include "interface/control.h"
      43             : #include "interface/input.h"
      44             : #include "interface/menus.h"
      45             : #include "interface/ui.h"
      46             : 
      47             : bool hasFBMSBS = false,
      48             :      hasTQ     = false,
      49             :      hasDBT    = false,
      50             :      hasEGPU4  = false,
      51             :      hasES3    = false,
      52             :      hasCI     = false;
      53             : 
      54             : VAR(outline, 0, 0, 1); //vertex/edge highlighting in edit mode
      55             : 
      56             : //read-only info for gl debugging
      57             : VAR(glversion, 1, 0, 0);
      58             : VAR(glslversion, 1, 0, 0);
      59             : 
      60             : // GL_EXT_framebuffer_blit
      61             : PFNGLBLITFRAMEBUFFERPROC         glBlitFramebuffer_         = nullptr;
      62             : 
      63             : // GL_EXT_framebuffer_multisample
      64             : PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample_ = nullptr;
      65             : 
      66             : // GL_ARB_texture_multisample
      67             : PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample_ = nullptr;
      68             : 
      69             : // OpenGL 1.3
      70             : #ifdef WIN32
      71             : PFNGLACTIVETEXTUREPROC    glActiveTexture_    = nullptr;
      72             : 
      73             : PFNGLBLENDEQUATIONEXTPROC glBlendEquation_ = nullptr;
      74             : PFNGLBLENDCOLOREXTPROC    glBlendColor_    = nullptr;
      75             : 
      76             : PFNGLTEXIMAGE3DPROC        glTexImage3D_        = nullptr;
      77             : PFNGLTEXSUBIMAGE3DPROC     glTexSubImage3D_     = nullptr;
      78             : PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D_ = nullptr;
      79             : 
      80             : PFNGLCOMPRESSEDTEXIMAGE3DPROC    glCompressedTexImage3D_    = nullptr;
      81             : PFNGLCOMPRESSEDTEXIMAGE2DPROC    glCompressedTexImage2D_    = nullptr;
      82             : PFNGLCOMPRESSEDTEXIMAGE1DPROC    glCompressedTexImage1D_    = nullptr;
      83             : PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D_ = nullptr;
      84             : PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D_ = nullptr;
      85             : PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D_ = nullptr;
      86             : PFNGLGETCOMPRESSEDTEXIMAGEPROC   glGetCompressedTexImage_   = nullptr;
      87             : 
      88             : PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements_ = nullptr;
      89             : #endif
      90             : 
      91             : // GL_EXT_depth_bounds_test
      92             : PFNGLDEPTHBOUNDSEXTPROC glDepthBounds_ = nullptr;
      93             : 
      94             : // GL_ARB_copy_image
      95             : PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData_ = nullptr;
      96             : 
      97           0 : void masktiles(uint *tiles, float sx1, float sy1, float sx2, float sy2)
      98             : {
      99             :     int tx1, ty1, tx2, ty2;
     100           0 :     calctilebounds(sx1, sy1, sx2, sy2, tx1, ty1, tx2, ty2);
     101           0 :     for(int ty = ty1; ty < ty2; ty++) tiles[ty] |= ((1<<(tx2-tx1))-1)<<tx1;
     102           0 : }
     103             : 
     104           0 : static void *getprocaddress(const char *name)
     105             : {
     106           0 :     return SDL_GL_GetProcAddress(name);
     107             : }
     108             : 
     109             : VAR(glerr, 0, 0, 1);
     110             : 
     111           0 : void glerror(const char *file, int line, GLenum error)
     112             : {
     113           0 :     const char *desc = "unknown";
     114           0 :     switch(error)
     115             :     {
     116           0 :         case GL_NO_ERROR:
     117             :         {
     118           0 :             desc = "no error";
     119           0 :             break;
     120             :         }
     121           0 :         case GL_INVALID_ENUM:
     122             :         {
     123           0 :             desc = "invalid enum";
     124           0 :             break;
     125             :         }
     126           0 :         case GL_INVALID_VALUE:
     127             :         {
     128           0 :             desc = "invalid value";
     129           0 :             break;
     130             :         }
     131           0 :         case GL_INVALID_OPERATION:
     132             :         {
     133           0 :             desc = "invalid operation";
     134           0 :             break;
     135             :         }
     136           0 :         case GL_STACK_OVERFLOW:
     137             :         {
     138           0 :             desc = "stack overflow";
     139           0 :             break;
     140             :         }
     141           0 :         case GL_STACK_UNDERFLOW:
     142             :         {
     143           0 :             desc = "stack underflow";
     144           0 :             break;
     145             :         }
     146           0 :         case GL_OUT_OF_MEMORY:
     147             :         {
     148           0 :             desc = "out of memory";
     149           0 :             break;
     150             :         }
     151             :     }
     152           0 :     std::printf("GL error: %s:%d: %s (%x)\n", file, line, desc, error);
     153           0 : }
     154             : 
     155             : VAR(intel_texalpha_bug, 0, 0, 1);
     156             : VAR(mesa_swap_bug, 0, 0, 1);
     157             : VAR(usetexgather, 1, 0, 0);
     158             : VAR(maxdrawbufs, 1, 0, 0);
     159             : VAR(maxdualdrawbufs, 1, 0, 0);
     160             : 
     161             : VAR(debugexts, 0, 0, 1);
     162             : 
     163             : static std::unordered_set<std::string> glexts;
     164             : 
     165           0 : static void parseglexts()
     166             : {
     167           0 :     GLint numexts = 0;
     168           0 :     glGetIntegerv(GL_NUM_EXTENSIONS, &numexts);
     169           0 :     for(int i = 0; i < numexts; ++i)
     170             :     {
     171             :         //cast from uchar * to char *
     172           0 :         const char *ext = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
     173           0 :         glexts.insert(ext);
     174             :     }
     175           0 : }
     176             : 
     177           1 : static bool hasext(const char *ext)
     178             : {
     179           1 :     return glexts.find(ext)!=glexts.end();
     180             : }
     181             : 
     182           0 : static bool checkdepthtexstencilrb()
     183             : {
     184           0 :     uint w = 256,
     185           0 :          h = 256;
     186           0 :     GLuint fbo = 0;
     187           0 :     glGenFramebuffers(1, &fbo);
     188           0 :     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
     189             : 
     190           0 :     GLuint depthtex = 0;
     191           0 :     glGenTextures(1, &depthtex);
     192           0 :     createtexture(depthtex, w, h, nullptr, 3, 0, GL_DEPTH_COMPONENT24, GL_TEXTURE_RECTANGLE);
     193           0 :     glBindTexture(GL_TEXTURE_RECTANGLE, 0);
     194           0 :     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, depthtex, 0);
     195             : 
     196           0 :     GLuint stencilrb = 0;
     197           0 :     glGenRenderbuffers(1, &stencilrb);
     198           0 :     glBindRenderbuffer(GL_RENDERBUFFER, stencilrb);
     199           0 :     glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);
     200           0 :     glBindRenderbuffer(GL_RENDERBUFFER, 0);
     201           0 :     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilrb);
     202             : 
     203           0 :     bool supported = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
     204             : 
     205           0 :     glBindFramebuffer(GL_FRAMEBUFFER, 0);
     206           0 :     glDeleteFramebuffers(1, &fbo);
     207           0 :     glDeleteTextures(1, &depthtex);
     208           0 :     glDeleteRenderbuffers(1, &stencilrb);
     209             : 
     210           0 :     return supported;
     211             : }
     212             : 
     213           0 : void gl_checkextensions()
     214             : {
     215           0 :     bool mesa   = false,
     216           0 :          intel  = false,
     217           0 :          amd    = false,
     218           0 :          nvidia = false;
     219           0 :     const char *vendor   = reinterpret_cast<const char *>(glGetString(GL_VENDOR)),
     220           0 :                *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER)),
     221           0 :                *version  = reinterpret_cast<const char *>(glGetString(GL_VERSION));
     222           0 :     conoutf(Console_Init, "Renderer: %s (%s)", renderer, vendor);
     223           0 :     conoutf(Console_Init, "Driver: %s", version);
     224             : 
     225           0 :     if(!renderer || !vendor || !version)
     226             :     {
     227           0 :         fatal("Could not get rendering context information!");
     228             :     }
     229           0 :     if(std::strstr(renderer, "Mesa") || std::strstr(version, "Mesa"))
     230             :     {
     231           0 :         mesa = true;
     232           0 :         if(std::strstr(renderer, "Intel"))
     233             :         {
     234           0 :             intel = true;
     235             :         }
     236             :     }
     237           0 :     else if(std::strstr(vendor, "NVIDIA"))
     238             :     {
     239           0 :         nvidia = true;
     240             :     }
     241           0 :     else if(std::strstr(vendor, "ATI") || std::strstr(vendor, "Advanced Micro Devices"))
     242             :     {
     243           0 :         amd = true;
     244             :     }
     245           0 :     else if(std::strstr(vendor, "Intel"))
     246             :     {
     247           0 :         intel = true;
     248             :     }
     249             : 
     250             :     uint glmajorversion, glminorversion;
     251           0 :     if(std::sscanf(version, " %u.%u", &glmajorversion, &glminorversion) != 2)
     252             :     {
     253           0 :         glversion = 100; //__really__ legacy systems (which won't run anyways)
     254             :     }
     255             :     else
     256             :     {
     257           0 :         glversion = glmajorversion*100 + glminorversion*10;
     258             :     }
     259           0 :     if(glversion < 400)
     260             :     {
     261           0 :         fatal("OpenGL 4.0 or greater is required!");
     262             :     }
     263             : 
     264           0 :     const char *glslstr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
     265           0 :     conoutf(Console_Init, "GLSL: %s", glslstr ? glslstr : "unknown");
     266             : 
     267             :     uint glslmajorversion, glslminorversion;
     268           0 :     if(glslstr && std::sscanf(glslstr, " %u.%u", &glslmajorversion, &glslminorversion) == 2)
     269             :     {
     270           0 :         glslversion = glslmajorversion*100 + glslminorversion;
     271             :     }
     272           0 :     if(glslversion < 400)
     273             :     {
     274           0 :         fatal("GLSL 4.00 or greater is required!");
     275             :     }
     276           0 :     parseglexts();
     277           0 :     GLint texsize = 0,
     278           0 :           texunits = 0,
     279           0 :           vtexunits = 0,
     280           0 :           cubetexsize = 0,
     281           0 :           drawbufs = 0;
     282           0 :     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texsize);
     283           0 :     hwtexsize = texsize;
     284           0 :     if(hwtexsize < 4096)
     285             :     {
     286           0 :         fatal("Large texture support is required!");
     287             :     }
     288           0 :     glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &texunits);
     289           0 :     hwtexunits = texunits;
     290           0 :     if(hwtexunits < 16)
     291             :     {
     292           0 :         fatal("Hardware does not support at least 16 texture units.");
     293             :     }
     294           0 :     glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vtexunits);
     295           0 :     hwvtexunits = vtexunits;
     296           0 :     if(hwvtexunits < 16)
     297             :     {
     298           0 :         fatal("Hardware does not support at least 16 vertex texture units.");
     299             :     }
     300           0 :     glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &cubetexsize);
     301           0 :     hwcubetexsize = cubetexsize;
     302           0 :     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawbufs);
     303           0 :     maxdrawbufs = drawbufs;
     304           0 :     if(maxdrawbufs < 4)
     305             :     {
     306           0 :         fatal("Hardware does not support at least 4 draw buffers.");
     307             :     }
     308             :     //OpenGL 3.0
     309             : 
     310           0 :     if(hasext("GL_EXT_gpu_shader4"))
     311             :     {
     312           0 :         hasEGPU4 = true;
     313           0 :         if(debugexts)
     314             :         {
     315           0 :             conoutf(Console_Init, "Using GL_EXT_gpu_shader4 extension.");
     316             :         }
     317             :     }
     318           0 :     glRenderbufferStorageMultisample_ = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)getprocaddress("glRenderbufferStorageMultisample");
     319             : 
     320             :     //OpenGL 3.2
     321           0 :     glTexImage2DMultisample_ = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)getprocaddress("glTexImage2DMultisample");
     322           0 :     if(hasext("GL_EXT_framebuffer_multisample_blit_scaled"))
     323             :     {
     324           0 :         hasFBMSBS = true;
     325           0 :         if(debugexts)
     326             :         {
     327           0 :             conoutf(Console_Init, "Using GL_EXT_framebuffer_multisample_blit_scaled extension.");
     328             :         }
     329             :     }
     330             :     //OpenGL 3.3
     331           0 :     if(hasext("GL_EXT_texture_filter_anisotropic"))
     332             :     {
     333           0 :         GLint val = 0;
     334           0 :         glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val);
     335           0 :         hwmaxaniso = val;
     336           0 :         if(debugexts)
     337             :         {
     338           0 :             conoutf(Console_Init, "Using GL_EXT_texture_filter_anisotropic extension.");
     339             :         }
     340             :     }
     341             :     else
     342             :     {
     343           0 :         fatal("Anisotropic filtering support is required!");
     344             :     }
     345           0 :     if(hasext("GL_EXT_depth_bounds_test"))
     346             :     {
     347           0 :         glDepthBounds_ = (PFNGLDEPTHBOUNDSEXTPROC) getprocaddress("glDepthBoundsEXT");
     348           0 :         hasDBT = true;
     349           0 :         if(debugexts)
     350             :         {
     351           0 :             conoutf(Console_Init, "Using GL_EXT_depth_bounds_test extension.");
     352             :         }
     353             :     }
     354           0 :     GLint dualbufs = 0;
     355           0 :     glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &dualbufs);
     356           0 :     maxdualdrawbufs = dualbufs;
     357           0 :     usetexgather = !intel && !nvidia ? 2 : 1;
     358             :     //OpenGL 4.x
     359           0 :     if(glversion >= 430 || hasext("GL_ARB_ES3_compatibility"))
     360             :     {
     361           0 :         hasES3 = true;
     362           0 :         if(glversion < 430 && debugexts)
     363             :         {
     364           0 :             conoutf(Console_Init, "Using GL_ARB_ES3_compatibility extension.");
     365             :         }
     366             :     }
     367             : 
     368           0 :     if(glversion >= 430 || hasext("GL_ARB_copy_image"))
     369             :     {
     370           0 :         glCopyImageSubData_ = (PFNGLCOPYIMAGESUBDATAPROC)getprocaddress("glCopyImageSubData");
     371             : 
     372           0 :         hasCI = true;
     373           0 :         if(glversion < 430 && debugexts)
     374             :         {
     375           0 :             conoutf(Console_Init, "Using GL_ARB_copy_image extension.");
     376             :         }
     377             :     }
     378           0 :     else if(hasext("GL_NV_copy_image"))
     379             :     {
     380           0 :         glCopyImageSubData_ = (PFNGLCOPYIMAGESUBDATAPROC)getprocaddress("glCopyImageSubDataNV");
     381             : 
     382           0 :         hasCI = true;
     383           0 :         if(debugexts)
     384             :         {
     385           0 :             conoutf(Console_Init, "Using GL_NV_copy_image extension.");
     386             :         }
     387             :     }
     388             : 
     389           0 :     if(amd)
     390             :     {
     391           0 :         msaalineardepth = glineardepth = 1; // reading back from depth-stencil still buggy on newer cards, and requires stencil for MSAA
     392             :     }
     393           0 :     else if(nvidia) //no errata on nvidia cards (yet)
     394             :     {
     395             :     }
     396           0 :     else if(intel)
     397             :     {
     398           0 :         smgather = 1; // native shadow filter is slow
     399           0 :         if(mesa)
     400             :         {
     401           0 :             batchsunlight = 0; // causes massive slowdown in linux driver
     402           0 :             msaalineardepth = 1; // MSAA depth texture access is buggy and resolves are slow
     403             :         }
     404             :         else
     405             :         {
     406             :             // causes massive slowdown in windows driver if reading depth-stencil texture
     407           0 :             if(checkdepthtexstencilrb())
     408             :             {
     409           0 :                 gdepthstencil = 1;
     410           0 :                 gstencil = 1;
     411             :             }
     412             :             // sampling alpha by itself from a texture generates garbage on Intel drivers on Windows
     413           0 :             intel_texalpha_bug = 1;
     414             :         }
     415             :     }
     416           0 :     if(mesa)
     417             :     {
     418           0 :         mesa_swap_bug = 1;
     419             :     }
     420           0 :     tqaaresolvegather = 1;
     421           0 : }
     422             : 
     423             : /* glext(): checks for existence of glext
     424             :  *
     425             :  * returns to console 1 if hashtable glexts contains glext (with the name passed)
     426             :  * and returns 0 otherwise
     427             :  *
     428             :  * glexts is a global variable
     429             :  */
     430           1 : void glext(char *ext)
     431             : {
     432           1 :     intret(hasext(ext) ? 1 : 0);
     433           1 : }
     434             : 
     435             : 
     436           0 : void gl_resize()
     437             : {
     438           0 :     gl_setupframe();
     439           0 :     glViewport(0, 0, hudw(), hudh());
     440           0 : }
     441             : 
     442           0 : void gl_init()
     443             : {
     444           0 :     glerror();
     445             : 
     446           0 :     glClearColor(0, 0, 0, 0);
     447           0 :     glClearDepth(1);
     448           0 :     glClearStencil(0);
     449           0 :     glDepthFunc(GL_LESS);
     450           0 :     glDisable(GL_DEPTH_TEST);
     451           0 :     glDisable(GL_STENCIL_TEST);
     452           0 :     glStencilFunc(GL_ALWAYS, 0, ~0);
     453           0 :     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
     454             : 
     455           0 :     glEnable(GL_LINE_SMOOTH);
     456             :     //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
     457             : 
     458           0 :     glFrontFace(GL_CW);
     459           0 :     glCullFace(GL_BACK);
     460           0 :     glDisable(GL_CULL_FACE);
     461             : 
     462           0 :     gle::setup();
     463           0 :     setupshaders();
     464             : 
     465           0 :     glerror();
     466             : 
     467           0 :     gl_resize();
     468           0 : }
     469             : 
     470             : VAR(wireframe, 0, 0, 1);
     471             : 
     472             : vec worldpos;
     473             : 
     474             : //these three cam() functions replace global variables that previously tracked their respective transforms of cammatrix
     475           0 : vec camdir()
     476             : {
     477           0 :     vec out;
     478           0 :     cammatrix.transposedtransformnormal(vec(viewmatrix.b), out);
     479           0 :     return out;
     480             : }
     481             : 
     482           0 : vec camright()
     483             : {
     484           0 :     vec out;
     485           0 :     cammatrix.transposedtransformnormal(vec(viewmatrix.a).neg(), out);
     486           0 :     return out;
     487             : }
     488             : 
     489           0 : vec camup()
     490             : {
     491           0 :     vec out;
     492           0 :     cammatrix.transposedtransformnormal(vec(viewmatrix.c), out);
     493           0 :     return out;
     494             : }
     495             : 
     496           0 : static void setcammatrix()
     497             : {
     498             :     // move from RH to Z-up LH quake style worldspace
     499           0 :     cammatrix = viewmatrix;
     500           0 :     cammatrix.rotate_around_y(camera1->roll/RAD);
     501           0 :     cammatrix.rotate_around_x(camera1->pitch/-RAD);
     502           0 :     cammatrix.rotate_around_z(camera1->yaw/-RAD);
     503           0 :     cammatrix.translate(vec(camera1->o).neg());
     504             : 
     505           0 :     if(!drawtex)
     506             :     {
     507           0 :         if(raycubepos(camera1->o, camdir(), worldpos, 0, Ray_ClipMat|Ray_SkipFirst) == -1)
     508             :         {
     509           0 :             worldpos = camdir().mul(2*rootworld.mapsize()).add(camera1->o); // if nothing is hit, just far away in the view direction
     510             :         }
     511             :     }
     512           0 : }
     513             : 
     514           0 : void setcamprojmatrix(bool init = true, bool flush = false)
     515             : {
     516           0 :     if(init)
     517             :     {
     518           0 :         setcammatrix();
     519             :     }
     520           0 :     jitteraa();
     521           0 :     camprojmatrix.muld(projmatrix, cammatrix);
     522           0 :     GLOBALPARAM(camprojmatrix, camprojmatrix);
     523           0 :     GLOBALPARAM(lineardepthscale, projmatrix.lineardepthscale()); //(invprojmatrix.c.z, invprojmatrix.d.z));
     524           0 :     if(flush && Shader::lastshader)
     525             :     {
     526           0 :         Shader::lastshader->flushparams();
     527             :     }
     528           0 : }
     529             : 
     530             : matrix4 hudmatrix, hudmatrixstack[64];
     531             : int hudmatrixpos = 0;
     532             : 
     533           0 : void resethudmatrix()
     534             : {
     535           0 :     hudmatrixpos = 0;
     536           0 :     GLOBALPARAM(hudmatrix, hudmatrix);
     537           0 : }
     538             : 
     539           0 : void pushhudmatrix()
     540             : {
     541           0 :     if(hudmatrixpos >= 0 && hudmatrixpos < static_cast<int>(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
     542             :     {
     543           0 :         hudmatrixstack[hudmatrixpos] = hudmatrix;
     544             :     }
     545           0 :     ++hudmatrixpos;
     546           0 : }
     547             : 
     548           0 : void flushhudmatrix(bool flushparams)
     549             : {
     550           0 :     GLOBALPARAM(hudmatrix, hudmatrix);
     551           0 :     if(flushparams && Shader::lastshader)
     552             :     {
     553           0 :         Shader::lastshader->flushparams();
     554             :     }
     555           0 : }
     556             : 
     557           0 : void pophudmatrix(bool flush, bool flushparams)
     558             : {
     559           0 :     --hudmatrixpos;
     560           0 :     if(hudmatrixpos >= 0 && hudmatrixpos < static_cast<int>(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
     561             :     {
     562           0 :         hudmatrix = hudmatrixstack[hudmatrixpos];
     563           0 :         if(flush)
     564             :         {
     565           0 :             flushhudmatrix(flushparams);
     566             :         }
     567             :     }
     568           0 : }
     569             : 
     570           0 : void pushhudscale(float scale)
     571             : {
     572           0 :     pushhudmatrix();
     573           0 :     hudmatrix.scale(scale, scale, 1);
     574           0 :     flushhudmatrix();
     575           0 : }
     576             : 
     577           0 : void pushhudtranslate(float tx, float ty, float sx, float sy)
     578             : {
     579           0 :     if(!sy)
     580             :     {
     581           0 :         sy = sx;
     582             :     }
     583           0 :     pushhudmatrix();
     584           0 :     hudmatrix.translate(tx, ty, 0);
     585           0 :     if(sy)
     586             :     {
     587           0 :         hudmatrix.scale(sx, sy, 1);
     588             :     }
     589           0 :     flushhudmatrix();
     590           0 : }
     591             : 
     592             : float curfov, aspect, fovy;
     593             : static float curavatarfov;
     594             : int farplane;
     595             : VARP(zoominvel, 0, 40, 500);
     596             : VARP(zoomoutvel, 0, 50, 500);
     597             : VARP(zoomfov, 10, 42, 90);
     598             : VARP(fov, 10, 100, 150);
     599             : VAR(avatarzoomfov, 1, 1, 1);
     600             : VAR(avatarfov, 10, 40, 100);
     601             : FVAR(avatardepth, 0, 0.7f, 1);
     602             : FVARNP(aspect, forceaspect, 0, 0, 1e3f);
     603             : 
     604             : static float zoomprogress = 0;
     605             : VAR(zoom, -1, 0, 1);
     606             : 
     607           0 : void disablezoom()
     608             : {
     609           0 :     zoom = 0;
     610           0 :     zoomprogress = 0;
     611           0 : }
     612             : 
     613           0 : void computezoom()
     614             : {
     615           0 :     if(!zoom)
     616             :     {
     617           0 :         zoomprogress = 0;
     618           0 :         curfov = fov;
     619           0 :         curavatarfov = avatarfov;
     620           0 :         return;
     621             :     }
     622           0 :     if(zoom > 0)
     623             :     {
     624           0 :         zoomprogress = zoominvel ? std::min(zoomprogress + static_cast<float>(elapsedtime) / zoominvel, 1.0f) : 1;
     625             :     }
     626             :     else
     627             :     {
     628           0 :         zoomprogress = zoomoutvel ? std::max(zoomprogress - static_cast<float>(elapsedtime) / zoomoutvel, 0.0f) : 0;
     629           0 :         if(zoomprogress <= 0)
     630             :         {
     631           0 :             zoom = 0;
     632             :         }
     633             :     }
     634           0 :     curfov = zoomfov*zoomprogress + fov*(1 - zoomprogress);
     635           0 :     curavatarfov = avatarzoomfov*zoomprogress + avatarfov*(1 - zoomprogress);
     636             : }
     637             : 
     638             : FVARP(zoomsens, 1e-4f, 4.5f, 1e4f);
     639             : FVARP(zoomaccel, 0, 0, 1000);
     640             : VARP(zoomautosens, 0, 1, 1);
     641             : FVARP(sensitivity, 0.01f, 3, 100.f);
     642             : FVARP(sensitivityscale, 1e-4f, 100, 1e4f);
     643             : /* Sensitivity scales:
     644             :  * 100: Quake/Source (TF2, Q3, Apex, L4D)
     645             :  * 333: COD, Destiny, Overwatch, ~BL2/3
     646             :  * 400: Cube/RE
     647             :  */
     648             : VARP(invmouse, 0, 0, 1); //toggles inverting the mouse
     649             : FVARP(mouseaccel, 0, 0, 1000);
     650             : 
     651             : physent *camera1 = nullptr;
     652             : //used in iengine.h
     653             : bool detachedcamera = false;
     654             : 
     655           0 : bool isthirdperson()
     656             : {
     657           0 :     return player!=camera1 || detachedcamera;
     658             : }
     659             : 
     660           0 : void fixcamerarange()
     661             : {
     662           0 :     constexpr float maxpitch = 90.0f;
     663           0 :     if(camera1->pitch>maxpitch)
     664             :     {
     665           0 :         camera1->pitch = maxpitch;
     666             :     }
     667           0 :     if(camera1->pitch<-maxpitch)
     668             :     {
     669           0 :         camera1->pitch = -maxpitch;
     670             :     }
     671           0 :     while(camera1->yaw<0.0f)
     672             :     {
     673           0 :         camera1->yaw += 360.0f;
     674             :     }
     675           0 :     while(camera1->yaw>=360.0f)
     676             :     {
     677           0 :         camera1->yaw -= 360.0f;
     678             :     }
     679           0 : }
     680             : 
     681           0 : void modifyorient(float yaw, float pitch)
     682             : {
     683           0 :     camera1->yaw += yaw;
     684           0 :     camera1->pitch += pitch;
     685           0 :     fixcamerarange();
     686           0 :     if(camera1!=player && !detachedcamera)
     687             :     {
     688           0 :         player->yaw = camera1->yaw;
     689           0 :         player->pitch = camera1->pitch;
     690             :     }
     691           0 : }
     692             : 
     693           0 : void mousemove(int dx, int dy)
     694             : {
     695           0 :     float cursens  = sensitivity,
     696           0 :           curaccel = mouseaccel;
     697           0 :     if(zoom)
     698             :     {
     699           0 :         if(zoomautosens)
     700             :         {
     701           0 :             cursens  = static_cast<float>(sensitivity*zoomfov)/fov;
     702           0 :             curaccel = static_cast<float>(mouseaccel*zoomfov)/fov;
     703             :         }
     704             :         else
     705             :         {
     706           0 :             cursens = zoomsens;
     707           0 :             curaccel = zoomaccel;
     708             :         }
     709             :     }
     710           0 :     if(curaccel && curtime && (dx || dy))
     711             :     {
     712           0 :         cursens += curaccel * sqrtf(dx*dx + dy*dy)/curtime;
     713             :     }
     714           0 :     cursens /= (sensitivityscale/4); //hard factor of 4 for 40 dots/deg like Quake/Source/etc.
     715           0 :     modifyorient(dx*cursens, dy*cursens*(invmouse ? 1 : -1));
     716           0 : }
     717             : 
     718             : matrix4 cammatrix, projmatrix, camprojmatrix;
     719             : 
     720             : FVAR(nearplane, 0.01f, 0.54f, 2.0f);
     721             : 
     722           0 : vec calcavatarpos(const vec &pos, float dist)
     723             : {
     724           0 :     vec eyepos;
     725           0 :     cammatrix.transform(pos, eyepos);
     726           0 :     GLdouble ydist = nearplane * std::tan(curavatarfov/(2*RAD)),
     727           0 :              xdist = ydist * aspect;
     728           0 :     vec4<float> scrpos;
     729           0 :     scrpos.x = eyepos.x*nearplane/xdist;
     730           0 :     scrpos.y = eyepos.y*nearplane/ydist;
     731           0 :     scrpos.z = (eyepos.z*(farplane + nearplane) - 2*nearplane*farplane) / (farplane - nearplane);
     732           0 :     scrpos.w = -eyepos.z;
     733             : 
     734           0 :     vec worldpos = camprojmatrix.inverse().perspectivetransform(scrpos);
     735           0 :     vec dir = vec(worldpos).sub(camera1->o).rescale(dist);
     736           0 :     return dir.add(camera1->o);
     737             : }
     738             : 
     739           0 : void renderavatar(void (*hudfxn)())
     740             : {
     741           0 :     if(isthirdperson())
     742             :     {
     743           0 :         return;
     744             :     }
     745           0 :     matrix4 oldprojmatrix = nojittermatrix;
     746           0 :     projmatrix.perspective(curavatarfov, aspect, nearplane, farplane);
     747           0 :     projmatrix.scalez(avatardepth);
     748           0 :     setcamprojmatrix(false);
     749             : 
     750           0 :     enableavatarmask();
     751           0 :     hudfxn();
     752           0 :     disableavatarmask();
     753             : 
     754           0 :     projmatrix = oldprojmatrix;
     755           0 :     setcamprojmatrix(false);
     756             : }
     757             : 
     758             : FVAR(polygonoffsetfactor, -1e4f, -3.0f, 1e4f);
     759             : FVAR(polygonoffsetunits, -1e4f, -3.0f, 1e4f);
     760             : FVAR(depthoffset, -1e4f, 0.01f, 1e4f);
     761             : 
     762             : static matrix4 nooffsetmatrix;
     763             : 
     764           0 : void enablepolygonoffset(GLenum type)
     765             : {
     766           0 :     if(!depthoffset)
     767             :     {
     768           0 :         glPolygonOffset(polygonoffsetfactor, polygonoffsetunits);
     769           0 :         glEnable(type);
     770           0 :         return;
     771             :     }
     772             : 
     773           0 :     projmatrix = nojittermatrix;
     774           0 :     nooffsetmatrix = projmatrix;
     775           0 :     projmatrix.d.z += depthoffset * projmatrix.c.z;
     776           0 :     setcamprojmatrix(false, true);
     777             : }
     778             : 
     779           0 : void disablepolygonoffset(GLenum type)
     780             : {
     781           0 :     if(!depthoffset)
     782             :     {
     783           0 :         glDisable(type);
     784           0 :         return;
     785             :     }
     786             : 
     787           0 :     projmatrix = nooffsetmatrix;
     788           0 :     setcamprojmatrix(false, true);
     789             : }
     790             : 
     791           0 : bool calcspherescissor(const vec &center, float size, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2)
     792             : {
     793             :     //dim must be 0..2
     794             :     //dir should be +/- 1
     795           0 :     auto checkplane = [] (int dim, const float &dc, int dir, float focaldist, float &low, float &high, const float &cz, const float &drt, const vec &e)
     796             :     {
     797           0 :         float nzc = (cz*cz + 1) / (cz + dir*drt) - cz,
     798           0 :               pz = dc/(nzc*e[dim] - e.z);
     799           0 :         if(pz > 0)
     800             :         {
     801           0 :             float c = (focaldist)*nzc,
     802           0 :                   pc = pz*nzc;
     803           0 :             if(pc < e[dim])
     804             :             {
     805           0 :                 low = c;
     806             :             }
     807           0 :             else if(pc > e[dim])
     808             :             {
     809           0 :                 high = c;
     810             :             }
     811             :         }
     812           0 :     };
     813             : 
     814           0 :     vec e;
     815           0 :     cammatrix.transform(center, e);
     816           0 :     if(e.z > 2*size)
     817             :     {
     818           0 :         sx1 = sy1 = sz1 =  1;
     819           0 :         sx2 = sy2 = sz2 = -1;
     820           0 :         return false;
     821             :     }
     822           0 :     if(drawtex == Draw_TexMinimap)
     823             :     {
     824           0 :         vec dir(size, size, size);
     825           0 :         if(projmatrix.a.x < 0)
     826             :         {
     827           0 :             dir.x = -dir.x;
     828             :         }
     829           0 :         if(projmatrix.b.y < 0)
     830             :         {
     831           0 :             dir.y = -dir.y;
     832             :         }
     833           0 :         if(projmatrix.c.z < 0)
     834             :         {
     835           0 :             dir.z = -dir.z;
     836             :         }
     837           0 :         sx1 = std::max(projmatrix.a.x*(e.x - dir.x) + projmatrix.d.x, -1.0f);
     838           0 :         sx2 = std::min(projmatrix.a.x*(e.x + dir.x) + projmatrix.d.x, 1.0f);
     839           0 :         sy1 = std::max(projmatrix.b.y*(e.y - dir.y) + projmatrix.d.y, -1.0f);
     840           0 :         sy2 = std::min(projmatrix.b.y*(e.y + dir.y) + projmatrix.d.y, 1.0f);
     841           0 :         sz1 = std::max(projmatrix.c.z*(e.z - dir.z) + projmatrix.d.z, -1.0f);
     842           0 :         sz2 = std::min(projmatrix.c.z*(e.z + dir.z) + projmatrix.d.z, 1.0f);
     843           0 :         return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
     844             :     }
     845           0 :     float zzrr = e.z*e.z - size*size,
     846           0 :           dx = e.x*e.x + zzrr,
     847           0 :           dy = e.y*e.y + zzrr,
     848           0 :           focaldist = 1.0f/std::tan(fovy*0.5f/RAD);
     849           0 :     sx1 = sy1 = -1;
     850           0 :     sx2 = sy2 = 1;
     851           0 :     if(dx > 0)
     852             :     {
     853           0 :         float cz  = e.x/e.z,
     854           0 :               drt = sqrtf(dx)/size;
     855           0 :         checkplane(0, dx, -1, focaldist/aspect, sx1, sx2, cz, drt, e);
     856           0 :         checkplane(0, dx,  1, focaldist/aspect, sx1, sx2, cz, drt, e);
     857             :     }
     858           0 :     if(dy > 0)
     859             :     {
     860           0 :         float cz  = e.y/e.z,
     861           0 :               drt = sqrtf(dy)/size;
     862           0 :         checkplane(1, dy, -1, focaldist, sy1, sy2, cz, drt, e);
     863           0 :         checkplane(1, dy,  1, focaldist, sy1, sy2, cz, drt, e);
     864             :     }
     865           0 :     float z1 = std::min(e.z + size, -1e-3f - nearplane),
     866           0 :           z2 = std::min(e.z - size, -1e-3f - nearplane);
     867           0 :     sz1 = (z1*projmatrix.c.z + projmatrix.d.z) / (z1*projmatrix.c.w + projmatrix.d.w);
     868           0 :     sz2 = (z2*projmatrix.c.z + projmatrix.d.z) / (z2*projmatrix.c.w + projmatrix.d.w);
     869           0 :     return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
     870             : }
     871             : 
     872           0 : bool calcbbscissor(const ivec &bbmin, const ivec &bbmax, float &sx1, float &sy1, float &sx2, float &sy2)
     873             : {
     874           0 :     auto addxyscissor = [&] (const vec4<float> &p)
     875             :     {
     876           0 :         if(p.z >= -p.w)
     877             :         {
     878           0 :             float x = p.x / p.w,
     879           0 :                   y = p.y / p.w;
     880           0 :             sx1 = std::min(sx1, x);
     881           0 :             sy1 = std::min(sy1, y);
     882           0 :             sx2 = std::max(sx2, x);
     883           0 :             sy2 = std::max(sy2, y);
     884             :         }
     885           0 :     };
     886             : 
     887           0 :     vec4<float> v[8];
     888           0 :     sx1 = sy1 = 1;
     889           0 :     sx2 = sy2 = -1;
     890           0 :     camprojmatrix.transform(vec(bbmin.x, bbmin.y, bbmin.z), v[0]);
     891           0 :     addxyscissor(v[0]);
     892           0 :     camprojmatrix.transform(vec(bbmax.x, bbmin.y, bbmin.z), v[1]);
     893           0 :     addxyscissor(v[1]);
     894           0 :     camprojmatrix.transform(vec(bbmin.x, bbmax.y, bbmin.z), v[2]);
     895           0 :     addxyscissor(v[2]);
     896           0 :     camprojmatrix.transform(vec(bbmax.x, bbmax.y, bbmin.z), v[3]);
     897           0 :     addxyscissor(v[3]);
     898           0 :     camprojmatrix.transform(vec(bbmin.x, bbmin.y, bbmax.z), v[4]);
     899           0 :     addxyscissor(v[4]);
     900           0 :     camprojmatrix.transform(vec(bbmax.x, bbmin.y, bbmax.z), v[5]);
     901           0 :     addxyscissor(v[5]);
     902           0 :     camprojmatrix.transform(vec(bbmin.x, bbmax.y, bbmax.z), v[6]);
     903           0 :     addxyscissor(v[6]);
     904           0 :     camprojmatrix.transform(vec(bbmax.x, bbmax.y, bbmax.z), v[7]);
     905           0 :     addxyscissor(v[7]);
     906           0 :     if(sx1 > sx2 || sy1 > sy2)
     907             :     {
     908           0 :         return false;
     909             :     }
     910           0 :     for(int i = 0; i < 8; ++i)
     911             :     {
     912           0 :         const vec4<float> &p = v[i];
     913           0 :         if(p.z >= -p.w)
     914             :         {
     915           0 :             continue;
     916             :         }
     917           0 :         for(int j = 0; j < 3; ++j)
     918             :         {
     919           0 :             const vec4<float> &o = v[i^(1<<j)];
     920           0 :             if(o.z <= -o.w)
     921             :             {
     922           0 :                 continue;
     923             :             }
     924             : 
     925           0 :             float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
     926           0 :                   w = p.w + t*(o.w - p.w),
     927           0 :                   x = (p.x + t*(o.x - p.x))/w,
     928           0 :                   y = (p.y + t*(o.y - p.y))/w;
     929           0 :             sx1 = std::min(sx1, x);
     930           0 :             sy1 = std::min(sy1, y);
     931           0 :             sx2 = std::max(sx2, x);
     932           0 :             sy2 = std::max(sy2, y);
     933             :         }
     934             :     }
     935             : 
     936             : 
     937           0 :     sx1 = std::max(sx1, -1.0f);
     938           0 :     sy1 = std::max(sy1, -1.0f);
     939           0 :     sx2 = std::min(sx2, 1.0f);
     940           0 :     sy2 = std::min(sy2, 1.0f);
     941           0 :     return true;
     942             : }
     943             : 
     944           0 : bool calcspotscissor(const vec &origin, float radius, const vec &dir, int spot, const vec &spotx, const vec &spoty, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2)
     945             : {
     946           0 :     static auto addxyzscissor = [] (const vec4<float> &p, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2)
     947             :     {
     948           0 :         if(p.z >= -p.w)
     949             :         {
     950           0 :             float x = p.x / p.w,
     951           0 :                   y = p.y / p.w,
     952           0 :                   z = p.z / p.w;
     953           0 :             sx1 = std::min(sx1, x);
     954           0 :             sy1 = std::min(sy1, y);
     955           0 :             sz1 = std::min(sz1, z);
     956           0 :             sx2 = std::max(sx2, x);
     957           0 :             sy2 = std::max(sy2, y);
     958           0 :             sz2 = std::max(sz2, z);
     959             :         }
     960           0 :     };
     961           0 :     float spotscale = radius * tan360(spot);
     962           0 :     vec up     = vec(spotx).mul(spotscale),
     963           0 :         right  = vec(spoty).mul(spotscale),
     964           0 :         center = vec(dir).mul(radius).add(origin);
     965           0 :     vec4<float> v[5];
     966           0 :     sx1 = sy1 = sz1 = 1;
     967           0 :     sx2 = sy2 = sz2 = -1;
     968           0 :     camprojmatrix.transform(vec(center).sub(right).sub(up), v[0]);
     969           0 :     addxyzscissor(v[0], sx1, sy1, sx2, sy2, sz1, sz2);
     970           0 :     camprojmatrix.transform(vec(center).add(right).sub(up), v[1]);
     971           0 :     addxyzscissor(v[1], sx1, sy1, sx2, sy2, sz1, sz2);
     972           0 :     camprojmatrix.transform(vec(center).sub(right).add(up), v[2]);
     973           0 :     addxyzscissor(v[2], sx1, sy1, sx2, sy2, sz1, sz2);
     974           0 :     camprojmatrix.transform(vec(center).add(right).add(up), v[3]);
     975           0 :     addxyzscissor(v[3], sx1, sy1, sx2, sy2, sz1, sz2);
     976           0 :     camprojmatrix.transform(origin, v[4]);
     977           0 :     addxyzscissor(v[4], sx1, sy1, sx2, sy2, sz1, sz2);
     978             : 
     979           0 :     static auto interpxyzscissor = [] (const vec4<float> &p, const vec4<float> &o, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1)
     980             :     {
     981           0 :         float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
     982           0 :               w = p.w + t*(o.w - p.w),
     983           0 :               x = (p.x + t*(o.x - p.x))/w,
     984           0 :               y = (p.y + t*(o.y - p.y))/w;
     985           0 :         sx1 = std::min(sx1, x);
     986           0 :         sy1 = std::min(sy1, y);
     987           0 :         sz1 = std::min(sz1, -1.0f);
     988           0 :         sx2 = std::max(sx2, x);
     989           0 :         sy2 = std::max(sy2, y);
     990           0 :     };
     991             : 
     992           0 :     if(sx1 > sx2 || sy1 > sy2 || sz1 > sz2)
     993             :     {
     994           0 :         return false;
     995             :     }
     996           0 :     for(int i = 0; i < 4; ++i)
     997             :     {
     998           0 :         const vec4<float> &p = v[i];
     999           0 :         if(p.z >= -p.w)
    1000             :         {
    1001           0 :             continue;
    1002             :         }
    1003           0 :         for(int j = 0; j < 2; ++j)
    1004             :         {
    1005           0 :             const vec4<float> &o = v[i^(1<<j)];
    1006           0 :             if(o.z <= -o.w)
    1007             :             {
    1008           0 :                 continue;
    1009             :             }
    1010             : 
    1011           0 :             interpxyzscissor(p, o, sx1, sy1, sx2, sy2, sz1);
    1012             :         }
    1013           0 :         if(v[4].z > -v[4].w)
    1014             :         {
    1015           0 :             interpxyzscissor(p, v[4], sx1, sy1, sx2, sy2, sz1);
    1016             :         }
    1017             :     }
    1018           0 :     if(v[4].z < -v[4].w)
    1019             :     {
    1020           0 :         for(int j = 0; j < 4; ++j)
    1021             :         {
    1022           0 :             const vec4<float> &o = v[j];
    1023           0 :             if(o.z <= -o.w)
    1024             :             {
    1025           0 :                 continue;
    1026             :             }
    1027           0 :             interpxyzscissor(v[4], o, sx1, sy1, sx2, sy2, sz1);
    1028             :         }
    1029             :     }
    1030             : 
    1031           0 :     sx1 = std::max(sx1, -1.0f);
    1032           0 :     sy1 = std::max(sy1, -1.0f);
    1033           0 :     sz1 = std::max(sz1, -1.0f);
    1034           0 :     sx2 = std::min(sx2,  1.0f);
    1035           0 :     sy2 = std::min(sy2,  1.0f);
    1036           0 :     sz2 = std::min(sz2,  1.0f);
    1037           0 :     return true;
    1038             : }
    1039             : 
    1040             : static GLuint screenquadvbo = 0;
    1041             : 
    1042           0 : static void setupscreenquad()
    1043             : {
    1044           0 :     if(!screenquadvbo)
    1045             :     {
    1046           0 :         glGenBuffers(1, &screenquadvbo);
    1047           0 :         gle::bindvbo(screenquadvbo);
    1048           0 :         vec2 verts[4] = { vec2(1, -1), vec2(-1, -1), vec2(1, 1), vec2(-1, 1) };
    1049           0 :         glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
    1050           0 :         gle::clearvbo();
    1051             :     }
    1052           0 : }
    1053             : 
    1054           0 : static void cleanupscreenquad()
    1055             : {
    1056           0 :     if(screenquadvbo)
    1057             :     {
    1058           0 :         glDeleteBuffers(1, &screenquadvbo);
    1059           0 :         screenquadvbo = 0;
    1060             :     }
    1061           0 : }
    1062             : 
    1063           0 : void screenquad()
    1064             : {
    1065           0 :     setupscreenquad();
    1066           0 :     gle::bindvbo(screenquadvbo);
    1067           0 :     gle::enablevertex();
    1068           0 :     gle::vertexpointer(sizeof(vec2), nullptr, GL_FLOAT, 2);
    1069           0 :     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    1070           0 :     gle::disablevertex();
    1071           0 :     gle::clearvbo();
    1072           0 : }
    1073             : 
    1074             : //sets screentexcoord0,screentexcoord1 in glsl
    1075           0 : static void setscreentexcoord(int i, float w, float h, float x = 0, float y = 0)
    1076             : {
    1077             :     static std::array<LocalShaderParam, 2> screentexcoord =
    1078             :     {
    1079             :         LocalShaderParam("screentexcoord0"),
    1080             :         LocalShaderParam("screentexcoord1")
    1081           0 :     };
    1082           0 :     screentexcoord[i].setf(w*0.5f, h*0.5f, x + w*0.5f, y + std::fabs(h)*0.5f);
    1083           0 : }
    1084             : 
    1085           0 : void screenquad(float sw, float sh)
    1086             : {
    1087           0 :     setscreentexcoord(0, sw, sh);
    1088           0 :     screenquad();
    1089           0 : }
    1090             : 
    1091           0 : void screenquad(float sw, float sh, float sw2, float sh2)
    1092             : {
    1093           0 :     setscreentexcoord(0, sw, sh);
    1094           0 :     setscreentexcoord(1, sw2, sh2);
    1095           0 :     screenquad();
    1096           0 : }
    1097             : 
    1098           0 : void screenquadoffset(float x, float y, float w, float h)
    1099             : {
    1100           0 :     setscreentexcoord(0, w, h, x, y);
    1101           0 :     screenquad();
    1102           0 : }
    1103             : 
    1104             : // creates a hud quad for hudquad, debugquad
    1105           0 : static void createhudquad(float x1, float y1, float x2, float y2, float sx1, float sy1, float sx2, float sy2) {
    1106           0 :     gle::defvertex(2);
    1107           0 :     gle::deftexcoord0();
    1108           0 :     gle::begin(GL_TRIANGLE_STRIP);
    1109           0 :     gle::attribf(x2, y1); gle::attribf(sx2, sy1);
    1110           0 :     gle::attribf(x1, y1); gle::attribf(sx1, sy1);
    1111           0 :     gle::attribf(x2, y2); gle::attribf(sx2, sy2);
    1112           0 :     gle::attribf(x1, y2); gle::attribf(sx1, sy2);
    1113           0 :     gle::end();
    1114           0 : }
    1115             : 
    1116           0 : void hudquad(float x, float y, float w, float h, float tx, float ty, float tw, float th)
    1117             : {
    1118           0 :     createhudquad(x, y, x+w, y+h, tx, ty, tx+tw, ty+th);
    1119           0 : }
    1120             : 
    1121           0 : void debugquad(float x, float y, float w, float h, float tx, float ty, float tw, float th)
    1122             : {
    1123           0 :     createhudquad(x, y, x+w, y+h, tx, ty+th, tx+tw, ty);
    1124           0 : }
    1125             : 
    1126             : VARR(fog, 16, 4000, 1000024);
    1127           0 : CVARR(fogcolor, 0x8099B3);
    1128             : VAR(fogoverlay, 0, 1, 1);
    1129             : 
    1130           0 : static float findsurface(int fogmat, const vec &v, int &abovemat)
    1131             : {
    1132           0 :     fogmat &= MatFlag_Volume;
    1133           0 :     ivec o(v), co;
    1134             :     int csize;
    1135             :     do
    1136             :     {
    1137           0 :         const cube &c = rootworld.lookupcube(o, 0, co, csize);
    1138           0 :         int mat = c.material&MatFlag_Volume;
    1139           0 :         if(mat != fogmat)
    1140             :         {
    1141           0 :             abovemat = IS_LIQUID(mat) ? c.material : Mat_Air;
    1142           0 :             return o.z;
    1143             :         }
    1144           0 :         o.z = co.z + csize;
    1145           0 :     } while(o.z < rootworld.mapsize());
    1146           0 :     abovemat = Mat_Air;
    1147           0 :     return rootworld.mapsize();
    1148             : }
    1149             : 
    1150           0 : static void blendfog(int fogmat, float below, float blend, float logblend, float &start, float &end, vec &fogc)
    1151             : {
    1152           0 :     switch(fogmat&MatFlag_Volume)
    1153             :     {
    1154           0 :         case Mat_Water:
    1155             :         {
    1156           0 :             const bvec &wcol = getwatercolor(fogmat),
    1157           0 :                        &wdeepcol = getwaterdeepcolor(fogmat);
    1158           0 :             int wfog = getwaterfog(fogmat),
    1159           0 :                 wdeep = getwaterdeep(fogmat);
    1160           0 :             float deepfade = std::clamp(below/std::max(wdeep, wfog), 0.0f, 1.0f);
    1161           0 :             vec color;
    1162           0 :             color.lerp(wcol.tocolor(), wdeepcol.tocolor(), deepfade);
    1163           0 :             fogc.add(vec(color).mul(blend));
    1164           0 :             end += logblend*std::min(fog, std::max(wfog*2, 16));
    1165           0 :             break;
    1166             :         }
    1167           0 :         default:
    1168             :         {
    1169           0 :             fogc.add(fogcolor.tocolor().mul(blend));
    1170           0 :             start += logblend*(fog+64)/8;
    1171           0 :             end += logblend*fog;
    1172           0 :             break;
    1173             :         }
    1174             :     }
    1175           0 : }
    1176             : 
    1177             : static vec curfogcolor(0, 0, 0);
    1178             : 
    1179           0 : void setfogcolor(const vec &v)
    1180             : {
    1181           0 :     GLOBALPARAM(fogcolor, v);
    1182           0 : }
    1183             : 
    1184           0 : void zerofogcolor()
    1185             : {
    1186           0 :     setfogcolor(vec(0, 0, 0));
    1187           0 : }
    1188             : 
    1189           0 : void resetfogcolor()
    1190             : {
    1191           0 :     setfogcolor(curfogcolor);
    1192           0 : }
    1193             : 
    1194             : FVAR(fogintensity, 0, 0.15f, 1);
    1195             : 
    1196           0 : float calcfogdensity(float dist)
    1197             : {
    1198           0 :     return std::log(fogintensity)/(M_LN2*dist);
    1199             : }
    1200             : 
    1201             : FVAR(fogcullintensity, 0, 1e-3f, 1);
    1202             : 
    1203           0 : float calcfogcull()
    1204             : {
    1205           0 :     return std::log(fogcullintensity) / (M_LN2*calcfogdensity(fog - (fog+64)/8));
    1206             : }
    1207             : 
    1208           0 : static void setfog(int fogmat, float below = 0, float blend = 1, int abovemat = Mat_Air)
    1209             : {
    1210           0 :     float start = 0,
    1211           0 :           end = 0,
    1212           0 :           logscale = 256,
    1213           0 :           logblend = std::log(1 + (logscale - 1)*blend) / std::log(logscale);
    1214             : 
    1215           0 :     curfogcolor = vec(0, 0, 0);
    1216           0 :     blendfog(fogmat, below, blend, logblend, start, end, curfogcolor);
    1217           0 :     if(blend < 1)
    1218             :     {
    1219           0 :         blendfog(abovemat, 0, 1-blend, 1-logblend, start, end, curfogcolor);
    1220             :     }
    1221           0 :     curfogcolor.mul(ldrscale);
    1222           0 :     GLOBALPARAM(fogcolor, curfogcolor);
    1223           0 :     float fogdensity = calcfogdensity(end-start);
    1224           0 :     GLOBALPARAMF(fogdensity, fogdensity, 1/std::exp(M_LN2*start*fogdensity));
    1225           0 : }
    1226             : 
    1227           0 : static void blendfogoverlay(int fogmat, float below, float blend, vec &overlay)
    1228             : {
    1229           0 :     switch(fogmat&MatFlag_Volume)
    1230             :     {
    1231           0 :         case Mat_Water:
    1232             :         {
    1233           0 :             const bvec &wcol = getwatercolor(fogmat),
    1234           0 :                        &wdeepcol = getwaterdeepcolor(fogmat);
    1235           0 :             int wfog = getwaterfog(fogmat),
    1236           0 :                 wdeep = getwaterdeep(fogmat);
    1237           0 :             float deepfade = std::clamp(below/std::max(wdeep, wfog), 0.0f, 1.0f);
    1238           0 :             vec color = vec(wcol.r, wcol.g, wcol.b).lerp(vec(wdeepcol.r, wdeepcol.g, wdeepcol.b), deepfade);
    1239           0 :             overlay.add(color.div(std::min(32.0f + std::max(color.r, std::max(color.g, color.b))*7.0f/8.0f, 255.0f)).max(0.4f).mul(blend));
    1240           0 :             break;
    1241             :         }
    1242           0 :         default:
    1243             :         {
    1244           0 :             overlay.add(blend);
    1245           0 :             break;
    1246             :         }
    1247             :     }
    1248           0 : }
    1249             : 
    1250           0 : void drawfogoverlay(int fogmat, float fogbelow, float fogblend, int abovemat)
    1251             : {
    1252           0 :     SETSHADER(fogoverlay);
    1253             : 
    1254           0 :     glEnable(GL_BLEND);
    1255           0 :     glBlendFunc(GL_ZERO, GL_SRC_COLOR);
    1256           0 :     vec overlay(0, 0, 0);
    1257           0 :     blendfogoverlay(fogmat, fogbelow, fogblend, overlay);
    1258           0 :     blendfogoverlay(abovemat, 0, 1-fogblend, overlay);
    1259             : 
    1260           0 :     gle::color(overlay);
    1261           0 :     screenquad();
    1262             : 
    1263           0 :     glDisable(GL_BLEND);
    1264           0 : }
    1265             : 
    1266             : int drawtex = 0;
    1267             : 
    1268             : /* =========================== minimap functionality ======================== */
    1269             : 
    1270             : static GLuint minimaptex = 0;
    1271             : vec minimapcenter(0, 0, 0),
    1272             :     minimapradius(0, 0, 0),
    1273             :     minimapscale(0, 0, 0);
    1274             : 
    1275           0 : float calcfrustumboundsphere(float nearplane, float farplane,  const vec &pos, const vec &view, vec &center)
    1276             : {
    1277           0 :     if(drawtex == Draw_TexMinimap)
    1278             :     {
    1279           0 :         center = minimapcenter;
    1280           0 :         return minimapradius.magnitude();
    1281             :     }
    1282             : 
    1283           0 :     float width = std::tan(fov/(2.0f*RAD)),
    1284           0 :           height = width / aspect,
    1285           0 :           cdist = ((nearplane + farplane)/2)*(1 + width*width + height*height);
    1286           0 :     if(cdist <= farplane)
    1287             :     {
    1288           0 :         center = vec(view).mul(cdist).add(pos);
    1289           0 :         return vec(width*nearplane, height*nearplane, cdist-nearplane).magnitude();
    1290             :     }
    1291             :     else
    1292             :     {
    1293           0 :         center = vec(view).mul(farplane).add(pos);
    1294           0 :         return vec(width*farplane, height*farplane, 0).magnitude();
    1295             :     }
    1296             : }
    1297             : 
    1298           0 : void clearminimap()
    1299             : {
    1300           0 :     if(minimaptex)
    1301             :     {
    1302           0 :         glDeleteTextures(1, &minimaptex);
    1303           0 :         minimaptex = 0;
    1304             :     }
    1305           0 : }
    1306             : 
    1307             : VARR(minimapheight, 0, 0, 2<<16); //height above bottom of map to render at
    1308           0 : CVARR(minimapcolor, 0);
    1309             : VARR(minimapclip, 0, 0, 1);
    1310             : VARP(minimapsize, 7, 10, 12);      //2^n size of the minimap texture (along edge)
    1311             : VARP(showminimap, 0, 1, 1);
    1312           0 : CVARP(nominimapcolor, 0x101010);  //color for the part of the minimap that isn't the map texture
    1313             : 
    1314           0 : void bindminimap()
    1315             : {
    1316           0 :     glBindTexture(GL_TEXTURE_2D, minimaptex);
    1317           0 : }
    1318             : 
    1319           0 : void clipminimap(ivec &bbmin, ivec &bbmax, const std::array<cube, 8> &c, const ivec &co = ivec(0, 0, 0), int size = rootworld.mapsize()>>1)
    1320             : {
    1321           0 :     for(int i = 0; i < 8; ++i)
    1322             :     {
    1323           0 :         ivec o(i, co, size);
    1324           0 :         if(c[i].children)
    1325             :         {
    1326           0 :             clipminimap(bbmin, bbmax, *(c[i].children), o, size>>1);
    1327             :         }
    1328           0 :         else if(!(c[i].issolid()) && (c[i].material&MatFlag_Clip)!=Mat_Clip)
    1329             :         {
    1330           0 :             for(int k = 0; k < 3; ++k)
    1331             :             {
    1332           0 :                 bbmin[k] = std::min(bbmin[k], o[k]);
    1333             :             }
    1334           0 :             for(int k = 0; k < 3; ++k)
    1335             :             {
    1336           0 :                 bbmax[k] = std::max(bbmax[k], o[k] + size);
    1337             :             }
    1338             :         }
    1339             :     }
    1340           0 : }
    1341             : 
    1342           0 : void drawminimap(int yaw, int pitch, vec loc, const cubeworld& world, int scalefactor)
    1343             : {
    1344           0 :     if(!showminimap)
    1345             :     {
    1346           0 :         if(!minimaptex)
    1347             :         {
    1348           0 :             glGenTextures(1, &minimaptex);
    1349             :         }
    1350           0 :         createtexture(minimaptex, 1, 1, nominimapcolor.v, 3, 0, GL_RGB, GL_TEXTURE_2D);
    1351           0 :         return;
    1352             :     }
    1353             : 
    1354           0 :     glerror();
    1355             : 
    1356           0 :     drawtex = Draw_TexMinimap;
    1357             : 
    1358           0 :     glerror();
    1359           0 :     gl_setupframe(true);
    1360             : 
    1361           0 :     int size = 1<<minimapsize,
    1362           0 :         sizelimit = std::min(hwtexsize, std::min(gw, gh));
    1363           0 :     while(size > sizelimit)
    1364             :     {
    1365           0 :         size = size - 128;
    1366             :     }
    1367           0 :     if(!minimaptex)
    1368             :     {
    1369           0 :         glGenTextures(1, &minimaptex);
    1370             :     }
    1371           0 :     ivec bbmin(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize()),
    1372           0 :          bbmax(0, 0, 0);
    1373           0 :     for(uint i = 0; i < valist.size(); i++)
    1374             :     {
    1375           0 :         const vtxarray *va = valist[i];
    1376           0 :         for(int k = 0; k < 3; ++k)
    1377             :         {
    1378           0 :             if(va->geommin[k]>va->geommax[k])
    1379             :             {
    1380           0 :                 continue;
    1381             :             }
    1382           0 :             bbmin[k] = std::min(bbmin[k], va->geommin[k]);
    1383           0 :             bbmax[k] = std::max(bbmax[k], va->geommax[k]);
    1384             :         }
    1385             :     }
    1386           0 :     if(minimapclip)
    1387             :     {
    1388           0 :         ivec clipmin(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize()),
    1389           0 :              clipmax(0, 0, 0);
    1390           0 :         clipminimap(clipmin, clipmax, *world.worldroot);
    1391           0 :         for(int k = 0; k < 2; ++k)
    1392             :         {
    1393           0 :             bbmin[k] = std::max(bbmin[k], clipmin[k]);
    1394             :         }
    1395           0 :         for(int k = 0; k < 2; ++k)
    1396             :         {
    1397           0 :             bbmax[k] = std::min(bbmax[k], clipmax[k]);
    1398             :         }
    1399             :     }
    1400             : 
    1401           0 :     minimapradius = vec(bbmax).sub(vec(bbmin)).div(scalefactor);
    1402           0 :     minimapcenter = loc;
    1403           0 :     minimapradius.x = minimapradius.y = std::max(minimapradius.x, minimapradius.y);
    1404           0 :     minimapscale = vec((0.5f - 1.0f/size)/minimapradius.x, (0.5f - 1.0f/size)/minimapradius.y, 1.0f);
    1405             : 
    1406           0 :     physent *oldcamera = camera1;
    1407           0 :     physent cmcamera = *player;
    1408           0 :     cmcamera.reset();
    1409           0 :     cmcamera.type = physent::PhysEnt_Camera;
    1410           0 :     cmcamera.o = loc;
    1411           0 :     cmcamera.yaw = yaw;
    1412           0 :     cmcamera.pitch = pitch;
    1413           0 :     cmcamera.roll = 0;
    1414           0 :     camera1 = &cmcamera;
    1415             : 
    1416           0 :     float oldldrscale = ldrscale;
    1417           0 :     int oldfarplane = farplane,
    1418           0 :         oldvieww    = vieww,
    1419           0 :         oldviewh    = viewh;
    1420           0 :     farplane = rootworld.mapsize()*2;
    1421           0 :     vieww = viewh = size;
    1422             : 
    1423           0 :     float zscale = std::max(static_cast<float>(minimapheight), minimapcenter.z + minimapradius.z + 1) + 1;
    1424             : 
    1425           0 :     projmatrix.ortho(-minimapradius.x, minimapradius.x, -minimapradius.y, minimapradius.y, 0, 2*zscale);
    1426           0 :     setcamprojmatrix();
    1427             : 
    1428           0 :     glEnable(GL_CULL_FACE);
    1429           0 :     glEnable(GL_DEPTH_TEST);
    1430             : 
    1431           0 :     xtravertsva = xtraverts = glde = gbatches = vtris = vverts = 0;
    1432           0 :     occlusionengine.flipqueries();
    1433             : 
    1434           0 :     ldrscale = 1;
    1435             : 
    1436           0 :     view.visiblecubes(false);
    1437           0 :     gbuf.rendergbuffer();
    1438           0 :     gbuf.rendershadowatlas();
    1439             : 
    1440           0 :     gbuf.shademinimap(minimapcolor.tocolor().mul(ldrscale));
    1441             : 
    1442           0 :     if(minimapheight > 0 && minimapheight < minimapcenter.z + minimapradius.z)
    1443             :     {
    1444           0 :         camera1->o.z = minimapcenter.z + minimapradius.z + 1;
    1445           0 :         projmatrix.ortho(-minimapradius.x, minimapradius.x, -minimapradius.y, minimapradius.y, -zscale, zscale);
    1446           0 :         setcamprojmatrix();
    1447           0 :         gbuf.rendergbuffer(false);
    1448           0 :         gbuf.shademinimap();
    1449             :     }
    1450             : 
    1451           0 :     glDisable(GL_DEPTH_TEST);
    1452           0 :     glDisable(GL_CULL_FACE);
    1453             : 
    1454           0 :     farplane = oldfarplane;
    1455           0 :     vieww = oldvieww;
    1456           0 :     viewh = oldviewh;
    1457           0 :     ldrscale = oldldrscale;
    1458             : 
    1459           0 :     camera1 = oldcamera;
    1460           0 :     drawtex = 0;
    1461             : 
    1462           0 :     createtexture(minimaptex, size, size, nullptr, 3, 1, GL_RGB5, GL_TEXTURE_2D);
    1463           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    1464           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    1465           0 :     GLfloat border[4] = { minimapcolor.x/255.0f, minimapcolor.y/255.0f, minimapcolor.z/255.0f, 1.0f };
    1466           0 :     glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
    1467           0 :     glBindTexture(GL_TEXTURE_2D, 0);
    1468             : 
    1469           0 :     GLuint fbo = 0;
    1470           0 :     glGenFramebuffers(1, &fbo);
    1471           0 :     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    1472           0 :     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, minimaptex, 0);
    1473           0 :     copyhdr(size, size, fbo);
    1474           0 :     glBindFramebuffer(GL_FRAMEBUFFER, 0);
    1475           0 :     glDeleteFramebuffers(1, &fbo);
    1476             : 
    1477           0 :     glViewport(0, 0, hudw(), hudh());
    1478             : }
    1479             : 
    1480             : VAR(modelpreviewfov, 10, 20, 100);    //y axis field of view
    1481             : VAR(modelpreviewpitch, -90, -15, 90); //pitch above model to render
    1482             : 
    1483             : /* ======================== model preview windows =========================== */
    1484             : 
    1485             : 
    1486           0 : void ModelPreview::start(int xcoord, int ycoord, int width, int height, bool bg, bool usescissor)
    1487             : {
    1488           0 :     x = xcoord;
    1489           0 :     y = ycoord;
    1490           0 :     w = width;
    1491           0 :     h = height;
    1492           0 :     background = bg;
    1493           0 :     scissor = usescissor;
    1494             : 
    1495           0 :     gbuf.setupgbuffer();
    1496             : 
    1497           0 :     useshaderbyname("modelpreview");
    1498             : 
    1499           0 :     drawtex = Draw_TexModelPreview;
    1500             : 
    1501           0 :     oldcamera = camera1;
    1502           0 :     camera = *camera1;
    1503           0 :     camera.reset();
    1504           0 :     camera.type = physent::PhysEnt_Camera;
    1505           0 :     camera.o = vec(0, 0, 0);
    1506           0 :     camera.yaw = 0;
    1507           0 :     camera.pitch = modelpreviewpitch;
    1508           0 :     camera.roll = 0;
    1509           0 :     camera1 = &camera;
    1510             : 
    1511           0 :     oldaspect = aspect;
    1512           0 :     oldfovy = fovy;
    1513           0 :     oldfov = curfov;
    1514           0 :     oldldrscale = ldrscale;
    1515           0 :     oldfarplane = farplane;
    1516           0 :     oldvieww = vieww;
    1517           0 :     oldviewh = viewh;
    1518           0 :     oldprojmatrix = projmatrix;
    1519             : 
    1520           0 :     aspect = w/static_cast<float>(h);
    1521           0 :     fovy = modelpreviewfov;
    1522           0 :     curfov = 2*std::atan2(std::tan(fovy/(2*RAD)), 1/aspect)*RAD;
    1523           0 :     farplane = 1024;
    1524           0 :     vieww = std::min(gw, w);
    1525           0 :     viewh = std::min(gh, h);
    1526           0 :     ldrscale = 1;
    1527             : 
    1528           0 :     projmatrix.perspective(fovy, aspect, nearplane, farplane);
    1529           0 :     setcamprojmatrix();
    1530             : 
    1531           0 :     glEnable(GL_CULL_FACE);
    1532           0 :     glEnable(GL_DEPTH_TEST);
    1533           0 : }
    1534             : 
    1535           0 : void ModelPreview::end()
    1536             : {
    1537           0 :     gbuf.rendermodelbatches();
    1538             : 
    1539           0 :     glDisable(GL_DEPTH_TEST);
    1540           0 :     glDisable(GL_CULL_FACE);
    1541             : 
    1542           0 :     gbuf.shademodelpreview(x, y, w, h, background, scissor);
    1543             : 
    1544           0 :     aspect = oldaspect;
    1545           0 :     fovy = oldfovy;
    1546           0 :     curfov = oldfov;
    1547           0 :     farplane = oldfarplane;
    1548           0 :     vieww = oldvieww;
    1549           0 :     viewh = oldviewh;
    1550           0 :     ldrscale = oldldrscale;
    1551             : 
    1552           0 :     camera1 = oldcamera;
    1553           0 :     drawtex = 0;
    1554             : 
    1555           0 :     projmatrix = oldprojmatrix;
    1556           0 :     setcamprojmatrix();
    1557           0 : }
    1558             : 
    1559           0 : vec calcmodelpreviewpos(const vec &radius, float &yaw)
    1560             : {
    1561           0 :     yaw = std::fmod(lastmillis/10000.0f*360.0f, 360.0f);
    1562           0 :     float dist = std::max(radius.magnitude2()/aspect, radius.magnitude())/std::sin(fovy/(2*RAD));
    1563           0 :     return vec(0, dist, 0).rotate_around_x(camera1->pitch/RAD);
    1564             : }
    1565             : 
    1566             : int xtraverts, xtravertsva;
    1567             : 
    1568             : /* ============================= core rendering ============================= */
    1569             : 
    1570             : //main scene rendering function
    1571           0 : void gl_drawview(void (*gamefxn)(), void(*hudfxn)(), void(*editfxn)())
    1572             : {
    1573           0 :     GLuint scalefbo = gbuf.shouldscale();
    1574           0 :     if(scalefbo)
    1575             :     {
    1576           0 :         vieww = gw;
    1577           0 :         viewh = gh;
    1578             :     }
    1579           0 :     float fogmargin = 1 + wateramplitude + nearplane;
    1580           0 :     int fogmat = rootworld.lookupmaterial(vec(camera1->o.x, camera1->o.y, camera1->o.z - fogmargin))&(MatFlag_Volume|MatFlag_Index),
    1581           0 :         abovemat = Mat_Air;
    1582           0 :     float fogbelow = 0;
    1583           0 :     if(IS_LIQUID(fogmat&MatFlag_Volume)) //if in the water
    1584             :     {
    1585           0 :         float z = findsurface(fogmat, vec(camera1->o.x, camera1->o.y, camera1->o.z - fogmargin), abovemat) - wateroffset;
    1586           0 :         if(camera1->o.z < z + fogmargin)
    1587             :         {
    1588           0 :             fogbelow = z - camera1->o.z;
    1589             :         }
    1590             :         else
    1591             :         {
    1592           0 :             fogmat = abovemat;
    1593             :         }
    1594             :     }
    1595             :     else
    1596             :     {
    1597           0 :         fogmat = Mat_Air; //use air fog
    1598             :     }
    1599           0 :     setfog(abovemat);
    1600             :     //setfog(fogmat, fogbelow, 1, abovemat);
    1601             : 
    1602           0 :     farplane = rootworld.mapsize()*2;
    1603             :     //set the camera location
    1604           0 :     projmatrix.perspective(fovy, aspect, nearplane, farplane);
    1605           0 :     setcamprojmatrix();
    1606             : 
    1607           0 :     glEnable(GL_CULL_FACE);
    1608           0 :     glEnable(GL_DEPTH_TEST);
    1609             : 
    1610           0 :     ldrscale = 0.5f;
    1611             :     //do occlusion culling
    1612           0 :     view.visiblecubes();
    1613             :     //set to wireframe if applicable
    1614           0 :     if(wireframe && editmode)
    1615             :     {
    1616           0 :         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    1617             :     }
    1618             :     //construct g-buffer (build basic scene)
    1619           0 :     gbuf.rendergbuffer(true, gamefxn);
    1620           0 :     if(wireframe && editmode) //done with wireframe mode now
    1621             :     {
    1622           0 :         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    1623             :     }
    1624           0 :     else if(limitsky() && editmode)
    1625             :     {
    1626           0 :         renderexplicitsky(true);
    1627             :     }
    1628             : 
    1629             :     //ambient obscurance (ambient occlusion) on geometry & models only
    1630           0 :     gbuf.renderao();
    1631           0 :     glerror();
    1632             : 
    1633             :     // render avatar after AO to avoid weird contact shadows
    1634           0 :     renderavatar(hudfxn);
    1635           0 :     glerror();
    1636             : 
    1637             :     // render grass after AO to avoid disturbing shimmering patterns
    1638           0 :     generategrass();
    1639           0 :     rendergrass();
    1640           0 :     glerror();
    1641             : 
    1642           0 :     glFlush();
    1643             :     //global illumination
    1644           0 :     gbuf.renderradiancehints();
    1645           0 :     glerror();
    1646             :     //lighting
    1647           0 :     gbuf.rendershadowatlas();
    1648           0 :     glerror();
    1649             :     //shading
    1650           0 :     shadegbuffer();
    1651           0 :     glerror();
    1652             : 
    1653             :     //fog
    1654           0 :     if(fogmat)
    1655             :     {
    1656           0 :         setfog(fogmat, fogbelow, 1, abovemat);
    1657             : 
    1658           0 :         gbuf.renderwaterfog(fogmat, fogbelow);
    1659             : 
    1660           0 :         setfog(fogmat, fogbelow, std::clamp(fogbelow, 0.0f, 1.0f), abovemat);
    1661             :     }
    1662             : 
    1663             :     //alpha
    1664           0 :     gbuf.rendertransparent();
    1665           0 :     glerror();
    1666             : 
    1667           0 :     if(fogmat)
    1668             :     {
    1669           0 :         setfog(fogmat, fogbelow, 1, abovemat);
    1670             :     }
    1671             : 
    1672             :     //volumetric lights
    1673           0 :     gbuf.rendervolumetric();
    1674           0 :     glerror();
    1675             : 
    1676           0 :     if(editmode)
    1677             :     {
    1678           0 :         if(!wireframe && outline)
    1679             :         {
    1680           0 :             renderoutline(); //edit mode geometry outline
    1681             :         }
    1682           0 :         glerror();
    1683           0 :         rendereditmaterials();
    1684           0 :         glerror();
    1685           0 :         gbuf.renderparticles();
    1686           0 :         glerror();
    1687           0 :         if(showhud)
    1688             :         {
    1689           0 :             glDepthMask(GL_FALSE);
    1690           0 :             editfxn(); //edit cursor, passed as pointer
    1691           0 :             glDepthMask(GL_TRUE);
    1692             :         }
    1693             :     }
    1694             : 
    1695             :     //we're done with depth/geometry stuff so we don't need this functionality
    1696           0 :     glDisable(GL_CULL_FACE);
    1697           0 :     glDisable(GL_DEPTH_TEST);
    1698             : 
    1699           0 :     if(fogoverlay && fogmat != Mat_Air)
    1700             :     {
    1701           0 :         drawfogoverlay(fogmat, fogbelow, std::clamp(fogbelow, 0.0f, 1.0f), abovemat);
    1702             :     }
    1703             :     //antialiasing
    1704           0 :     doaa(setuppostfx(gbuf, vieww, viewh, scalefbo), gbuf);
    1705             :     //postfx
    1706           0 :     renderpostfx(scalefbo);
    1707           0 :     if(scalefbo)
    1708             :     {
    1709           0 :         gbuf.doscale();
    1710             :     }
    1711           0 : }
    1712             : 
    1713           0 : int renderw()
    1714             : {
    1715           0 :     return std::min(scr_w, screenw);
    1716             : }
    1717             : 
    1718           0 : int renderh()
    1719             : {
    1720           0 :     return std::min(scr_h, screenh);
    1721             : }
    1722             : 
    1723           0 : int hudw()
    1724             : {
    1725           0 :     return screenw;
    1726             : }
    1727             : 
    1728           0 : int hudh()
    1729             : {
    1730           0 :     return screenh;
    1731             : }
    1732             : 
    1733           0 : void gl_setupframe(bool force)
    1734             : {
    1735           0 :     if(!force)
    1736             :     {
    1737           0 :         return;
    1738             :     }
    1739           0 :     setuplights(gbuf);
    1740             : }
    1741             : 
    1742           0 : void gl_drawframe(int crosshairindex, void (*gamefxn)(), void (*hudfxn)(), void (*editfxn)(), void (*hud2d)())
    1743             : {
    1744           0 :     synctimers();
    1745           0 :     xtravertsva = xtraverts = glde = gbatches = vtris = vverts = 0;
    1746           0 :     occlusionengine.flipqueries();
    1747           0 :     aspect = forceaspect ? forceaspect : hudw()/static_cast<float>(hudh());
    1748           0 :     fovy = 2*std::atan2(std::tan(curfov/(2*RAD)), aspect)*RAD;
    1749           0 :     vieww = hudw();
    1750           0 :     viewh = hudh();
    1751           0 :     if(mainmenu)
    1752             :     {
    1753           0 :         gl_drawmainmenu();
    1754             :     }
    1755             :     else
    1756             :     {
    1757           0 :         gl_drawview(gamefxn, hudfxn, editfxn);
    1758             :     }
    1759           0 :     UI::render();
    1760           0 :     gl_drawhud(crosshairindex, hud2d);
    1761           0 : }
    1762             : 
    1763           0 : void cleanupgl()
    1764             : {
    1765           0 :     clearminimap();
    1766           0 :     cleanuptimers();
    1767           0 :     cleanupscreenquad();
    1768           0 :     gle::cleanup();
    1769           0 : }
    1770             : 
    1771           1 : void initrenderglcmds()
    1772             : {
    1773           1 :     addcommand("glext", reinterpret_cast<identfun>(glext), "s", Id_Command);
    1774           2 :     addcommand("getcamyaw", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->yaw : 0);}), "", Id_Command);
    1775           2 :     addcommand("getcampitch", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->pitch : 0);}), "", Id_Command);
    1776           2 :     addcommand("getcamroll", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->roll : 0);}), "", Id_Command);
    1777           1 :     addcommand("getcampos", reinterpret_cast<identfun>(+[]()
    1778             :     {
    1779           1 :         if(!camera1)
    1780             :         {
    1781           1 :             result("no camera");
    1782             :         }
    1783             :         else
    1784             :         {
    1785           0 :             DEF_FORMAT_STRING(pos, "%s %s %s", floatstr(camera1->o.x), floatstr(camera1->o.y), floatstr(camera1->o.z));
    1786           0 :             result(pos);
    1787             :         }
    1788           2 :     }), "", Id_Command);
    1789           1 : }

Generated by: LCOV version 1.14