LCOV - code coverage report
Current view: top level - engine/render - rendergl.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 1.9 % 990 19
Test Date: 2026-06-16 06:16:16 Functions: 10.8 % 83 9

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

Generated by: LCOV version 2.0-1