LCOV - code coverage report
Current view: top level - engine/render - rendersky.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.0 % 213 0
Test Date: 2025-04-16 12:09:02 Functions: 0.0 % 15 0

            Line data    Source code
       1              : /* rendersky.cpp: skybox and sky environment rendering
       2              :  *
       3              :  * Libprimis supports standard static cubemap skyboxes as well as cloud layers,
       4              :  * which are both static (though capable of translation/rotation)
       5              :  *
       6              :  * as well as a parameterized sky generation system, referred to the code as the
       7              :  * atmo" functionality
       8              :  *
       9              :  * as expected for distant scenery, like the sky and clouds are, there is no support
      10              :  * for parallax (cloud layers do not move relative to background sky as the player
      11              :  * moves)
      12              :  */
      13              : #include "../libprimis-headers/cube.h"
      14              : #include "../../shared/geomexts.h"
      15              : #include "../../shared/glemu.h"
      16              : #include "../../shared/glexts.h"
      17              : #include "../../shared/stream.h"
      18              : 
      19              : #include <format>
      20              : 
      21              : #include "octarender.h"
      22              : #include "rendergl.h"
      23              : #include "renderlights.h"
      24              : #include "rendersky.h"
      25              : #include "renderva.h"
      26              : #include "shader.h"
      27              : #include "shaderparam.h"
      28              : #include "texture.h"
      29              : 
      30              : #include "interface/console.h"
      31              : #include "interface/control.h"
      32              : 
      33              : #include "world/light.h"
      34              : #include "world/octaedit.h"
      35              : #include "world/octaworld.h"
      36              : #include "world/raycube.h"
      37              : 
      38            0 : VARFR(skyshadow, 0, 0, 1, clearshadowcache());   //toggles rsm features in renderva.cpp
      39              : 
      40              : bool explicitsky = false;
      41              : 
      42              : // internally relevant functionality
      43              : namespace
      44              : {
      45              :     VARNR(skytexture, useskytexture, 0, 0, 1);       //toggles rendering sky texture instead of nothing on skytex'd geometry
      46              : 
      47              :     std::array<const Texture *, 6> sky = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
      48              : 
      49            0 :     void loadsky(const char *basename, std::array<const Texture *, 6> &texs)
      50              :     {
      51              : 
      52              :         struct cubemapside
      53              :         {
      54              :             GLenum target;
      55              :             const char *name;
      56              :             bool flipx, flipy, swapxy;
      57              :         };
      58              :         static const cubemapside cubemapsides[6] =
      59              :         {
      60              :             { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", false, true,  true  },
      61              :             { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", true,  false, true  },
      62              :             { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "bk", false, false, false },
      63              :             { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "ft", true,  true,  false },
      64              :             { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", true,  false, true  },
      65              :             { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", true,  false, true  },
      66              :         };
      67              : 
      68            0 :         const char *wildcard = std::strchr(basename, '*');
      69            0 :         for(int i = 0; i < 6; ++i) //six sides for a cubemap
      70              :         {
      71            0 :             const char *side = cubemapsides[i].name;
      72              :             string name;
      73            0 :             copystring(name, makerelpath("media/sky", basename));
      74            0 :             if(wildcard)
      75              :             {
      76            0 :                 char *chop = std::strchr(name, '*');
      77            0 :                 if(chop)
      78              :                 {
      79            0 :                     *chop = '\0';
      80            0 :                     concatstring(name, side);
      81            0 :                     concatstring(name, wildcard+1);
      82              :                 }
      83            0 :                 texs[i] = textureload(name, 3, true, false);
      84              :             }
      85              :             else
      86              :             {
      87            0 :                 std::string ext = std::format("_{}.jpg", side);
      88            0 :                 concatstring(name, ext.c_str());
      89            0 :                 if((texs[i] = textureload(name, 3, true, false))==notexture)
      90              :                 {
      91            0 :                     std::strcpy(name+std::strlen(name)-3, "png");
      92            0 :                     texs[i] = textureload(name, 3, true, false);
      93              :                 }
      94            0 :             }
      95            0 :             if(texs[i]==notexture)
      96              :             {
      97            0 :                 conoutf(Console_Error, "could not load side %s of sky texture %s", side, basename);
      98              :             }
      99              :         }
     100            0 :     }
     101              : 
     102              :     Texture *cloudoverlay = nullptr;
     103              : 
     104            0 :     Texture *loadskyoverlay(std::string_view basename)
     105              :     {
     106            0 :         const char *ext = std::strrchr(basename.data(), '.');
     107              :         string name;
     108            0 :         copystring(name, makerelpath("media/sky", basename.data()));
     109            0 :         Texture *t = notexture;
     110            0 :         if(ext)
     111              :         {
     112            0 :             t = textureload(name, 0, true, false);
     113              :         }
     114              :         else
     115              :         {
     116            0 :             concatstring(name, ".jpg");
     117            0 :             if((t = textureload(name, 0, true, false)) == notexture)
     118              :             {
     119            0 :                 std::strcpy(name+std::strlen(name)-3, "png");
     120            0 :                 t = textureload(name, 0, true, false);
     121              :             }
     122              :         }
     123            0 :         if(t==notexture)
     124              :         {
     125            0 :             conoutf(Console_Error, "could not load sky overlay texture %s", basename.data());
     126              :         }
     127            0 :         return t;
     128              :     }
     129              : 
     130            0 :     SVARFR(skybox, "",
     131              :     {
     132              :         if(skybox[0])
     133              :         {
     134              :             loadsky(skybox, sky);
     135              :         }
     136              :     });                                             //path to skybox
     137            0 :     CVARR(skyboxcolor, 0xFFFFFF);                   //color to multiply skybox texture by
     138              :     FVARR(skyboxoverbright, 1, 2, 16);              //amount by which skybox can exceed 0xFFFFFF
     139              :     FVARR(skyboxoverbrightmin, 0, 1, 16);
     140              :     FVARR(skyboxoverbrightthreshold, 0, 0.7f, 1);
     141              :     FVARR(skyboxspin, -720, 0, 720);                //skybox spin rate in degrees per second
     142              :     VARR (skyboxyaw, 0, 0, 360);                    //skybox rotation offset in degrees
     143              : 
     144              :     //cloud layer variables
     145              :     FVARR(cloudclip, 0, 0.5f, 1);
     146            0 :     SVARFR(cloudlayer, "",
     147              :     {
     148              :         if(cloudlayer[0])
     149              :         {
     150              :             cloudoverlay = loadskyoverlay(cloudlayer);
     151              :         }
     152              :     });
     153              :     FVARR(cloudoffsetx, 0, 0, 1); //offset of cloud texture: 1 is equal to 1 tex-width x
     154              :     FVARR(cloudoffsety, 0, 0, 1); //offset of cloud texture: 1 is equal to 1 tex-width y
     155              :     FVARR(cloudscrollx, -16, 0, 16);
     156              :     FVARR(cloudscrolly, -16, 0, 16);
     157              :     FVARR(cloudscale, 0.001, 1, 64);
     158              :     FVARR(cloudspin, -720, 0, 720);
     159              :     VARR (cloudyaw, 0, 0, 360);
     160              :     FVARR(cloudheight, -1, 0.2f, 1);
     161              :     FVARR(cloudfade, 0, 0.2f, 1);
     162              :     FVARR(cloudalpha, 0, 1, 1);
     163              :     VARR (cloudsubdiv, 4, 16, 64);
     164            0 :     CVARR(cloudcolor, 0xFFFFFF);
     165              : 
     166            0 :     void drawenvboxface(float s0, float t0, int x0, int y0, int z0,
     167              :                         float s1, float t1, int x1, int y1, int z1,
     168              :                         float s2, float t2, int x2, int y2, int z2,
     169              :                         float s3, float t3, int x3, int y3, int z3,
     170              :                         const Texture *tex)
     171              :     {
     172            0 :         glBindTexture(GL_TEXTURE_2D, (tex ? tex : notexture)->id);
     173            0 :         gle::begin(GL_TRIANGLE_STRIP);
     174            0 :         gle::attribf(x3, y3, z3); gle::attribf(s3, t3);
     175            0 :         gle::attribf(x2, y2, z2); gle::attribf(s2, t2);
     176            0 :         gle::attribf(x0, y0, z0); gle::attribf(s0, t0);
     177            0 :         gle::attribf(x1, y1, z1); gle::attribf(s1, t1);
     178            0 :         xtraverts += gle::end();
     179            0 :     }
     180              : 
     181            0 :     void drawenvbox(const std::array<const Texture *, 6> &sky, float z1clip = 0.0f, float z2clip = 1.0f, int faces = 0x3F)
     182              :     {
     183            0 :         if(z1clip >= z2clip)
     184              :         {
     185            0 :             return;
     186              :         }
     187            0 :         float v1 = 1-z1clip,
     188            0 :               v2 = 1-z2clip;
     189            0 :         int w  = farplane/2,
     190            0 :             z1 = static_cast<int>(std::ceil(2*w*(z1clip-0.5f))),
     191            0 :             z2 = static_cast<int>(std::ceil(2*w*(z2clip-0.5f)));
     192              : 
     193            0 :         gle::defvertex();
     194            0 :         gle::deftexcoord0();
     195              : 
     196              :         //daw the six faces of the skybox's cubemap
     197            0 :         if(faces&0x01)
     198              :         {
     199            0 :             drawenvboxface(1.0f, v2,  -w, -w, z2,
     200              :                            0.0f, v2,  -w,  w, z2,
     201              :                            0.0f, v1,  -w,  w, z1,
     202            0 :                            1.0f, v1,  -w, -w, z1, sky[0]);
     203              :         }
     204            0 :         if(faces&0x02)
     205              :         {
     206            0 :             drawenvboxface(0.0f, v1, w, -w, z1,
     207              :                            1.0f, v1, w,  w, z1,
     208              :                            1.0f, v2, w,  w, z2,
     209            0 :                            0.0f, v2, w, -w, z2, sky[1]);
     210              :         }
     211            0 :         if(faces&0x04)
     212              :         {
     213            0 :             drawenvboxface(0.0f, v1, -w, -w, z1,
     214              :                            1.0f, v1,  w, -w, z1,
     215              :                            1.0f, v2,  w, -w, z2,
     216            0 :                            0.0f, v2, -w, -w, z2, sky[2]);
     217              :         }
     218            0 :         if(faces&0x08)
     219              :         {
     220            0 :             drawenvboxface(0.0f, v1,  w,  w, z1,
     221              :                            1.0f, v1, -w,  w, z1,
     222              :                            1.0f, v2, -w,  w, z2,
     223            0 :                            0.0f, v2,  w,  w, z2, sky[3]);
     224              :         }
     225            0 :         if(z1clip <= 0 && faces&0x10)
     226              :         {
     227            0 :             drawenvboxface(1.0f, 1.0f, -w,  w,  -w,
     228              :                            1.0f, 0.0f,  w,  w,  -w,
     229              :                            0.0f, 0.0f,  w, -w,  -w,
     230            0 :                            0.0f, 1.0f, -w, -w,  -w, sky[4]);
     231              :         }
     232            0 :         if(z2clip >= 1 && faces&0x20)
     233              :         {
     234            0 :             drawenvboxface(1.0f, 1.0f,  w,  w, w,
     235              :                            1.0f, 0.0f, -w,  w, w,
     236              :                            0.0f, 0.0f, -w, -w, w,
     237            0 :                            0.0f, 1.0f,  w, -w, w, sky[5]);
     238              :         }
     239              :     }
     240              : 
     241            0 :     void drawenvoverlay(const Texture *overlay = nullptr, float tx = 0, float ty = 0)
     242              :     {
     243            0 :         const int w = farplane/2;
     244            0 :         const float z = w*cloudheight,
     245            0 :                         tsz = 0.5f*(1-cloudfade)/cloudscale,
     246            0 :                         psz = w*(1-cloudfade);
     247            0 :         glBindTexture(GL_TEXTURE_2D, (overlay ? overlay : notexture)->id);
     248            0 :         vec color = cloudcolor.tocolor();
     249            0 :         gle::color(color, cloudalpha);
     250            0 :         gle::defvertex();
     251            0 :         gle::deftexcoord0();
     252            0 :         gle::begin(GL_TRIANGLE_FAN);
     253            0 :         for(int i = 0; i < cloudsubdiv+1; ++i)
     254              :         {
     255            0 :             vec p(1, 1, 0);
     256            0 :             p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
     257            0 :             gle::attribf(p.x*psz, p.y*psz, z);
     258            0 :             gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
     259              :         }
     260            0 :         xtraverts += gle::end();
     261            0 :         const float tsz2 = 0.5f/cloudscale;
     262            0 :         gle::defvertex();
     263            0 :         gle::deftexcoord0();
     264            0 :         gle::defcolor(4);
     265            0 :         gle::begin(GL_TRIANGLE_STRIP);
     266            0 :         for(int i = 0; i < cloudsubdiv+1; ++i)
     267              :         {
     268            0 :             vec p(1, 1, 0);
     269            0 :             p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
     270            0 :             gle::attribf(p.x*psz, p.y*psz, z);
     271            0 :             gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
     272            0 :             gle::attrib(color, cloudalpha);
     273            0 :             gle::attribf(p.x*w, p.y*w, z);
     274            0 :             gle::attribf(tx - p.x*tsz2, ty + p.y*tsz2);
     275            0 :             gle::attrib(color, 0.0f);
     276              :         }
     277            0 :         xtraverts += gle::end();
     278            0 :     }
     279              : 
     280              :     /* === "atmo" parameterized, procedurally generated sky === */
     281              :     VARR(atmo, 0, 0, 1);
     282              :     FVARR(atmoplanetsize, 1e-3f, 8, 1e3f);
     283              :     FVARR(atmoheight, 1e-3f, 1, 1e3f);
     284              :     FVARR(atmobright, 0, 4, 16);
     285            0 :     CVAR1R(atmosunlight, 0);
     286              :     FVARR(atmosunlightscale, 0, 1, 16);
     287              :     FVARR(atmosundisksize, 0, 1, 10);
     288              :     FVARR(atmosundiskbright, 0, 1, 16);
     289              :     FVARR(atmohaze, 0, 0.03f, 1);
     290            0 :     CVAR0R(atmohazefade, 0xAEACA9);
     291              :     FVARR(atmohazefadescale, 0, 1, 1);
     292              :     FVARR(atmoclarity, 0, 0.2f, 10);
     293              :     FVARR(atmodensity, 1e-3f, 0.99f, 10);
     294              :     FVARR(atmoalpha, 0, 1, 1);
     295              : 
     296            0 :     void drawatmosphere()
     297              :     {
     298            0 :         SETSHADER(atmosphere);
     299              : 
     300            0 :         matrix4 sunmatrix = cammatrix.inverse();
     301            0 :         sunmatrix.settranslation(0, 0, 0);
     302            0 :         sunmatrix.mul(projmatrix.inverse());
     303            0 :         LOCALPARAM(sunmatrix, sunmatrix);
     304              : 
     305            0 :         LOCALPARAM(sunlight, (!atmosunlight.iszero() ? atmosunlight.tocolor().mul(atmosunlightscale) : sunlight.tocolor().mul(sunlightscale)).mul(atmobright*ldrscale));
     306            0 :         LOCALPARAM(sundir, sunlightdir);
     307              : 
     308            0 :         vec sundiskparams;
     309            0 :         sundiskparams.y = -(1 - 0.0075f * atmosundisksize);
     310            0 :         sundiskparams.x = 1/(1 + sundiskparams.y);
     311            0 :         sundiskparams.y *= sundiskparams.x;
     312            0 :         sundiskparams.z = atmosundiskbright;
     313            0 :         LOCALPARAM(sundiskparams, sundiskparams);
     314              : 
     315            0 :         const float earthradius     = 6.371e6f, //radius of earth in meters
     316            0 :                     earthatmoheight = 100e3f; //atmospheric height (100km)
     317            0 :         float planetradius = earthradius*atmoplanetsize,
     318            0 :               atmoradius   = planetradius + earthatmoheight*atmoheight;
     319            0 :         LOCALPARAMF(atmoradius, planetradius, atmoradius*atmoradius, atmoradius*atmoradius - planetradius*planetradius);
     320              : 
     321            0 :         float gm = (1 - atmohaze)*0.2f + 0.75f;
     322            0 :         LOCALPARAMF(gm, gm);
     323              : 
     324            0 :         vec lambda(680e-9f, 550e-9f, 450e-9f),
     325            0 :             betar = vec(lambda).square().square().recip().mul(1.86e-31f / atmodensity),
     326            0 :             betam = vec(lambda).recip().mul(2*M_PI).square().mul(atmohazefade.tocolor().mul(atmohazefadescale)).mul(1.36e-19f * std::max(atmohaze, 1e-3f)),
     327            0 :             betarm = vec(betar).div(1+atmoclarity).add(betam);
     328            0 :         betar.div(betarm).mul(3/(16*M_PI));
     329            0 :         betam.div(betarm).mul((1-gm)*(1-gm)/(4*M_PI));
     330            0 :         LOCALPARAM(betar, betar);
     331            0 :         LOCALPARAM(betam, betam);
     332            0 :         LOCALPARAM(betarm, betarm.div(M_LN2));
     333              : 
     334            0 :         LOCALPARAMF(atmoalpha, atmoalpha);
     335              : 
     336            0 :         gle::defvertex();
     337            0 :         gle::begin(GL_TRIANGLE_STRIP);
     338            0 :         gle::attribf(-1, 1, 1);
     339            0 :         gle::attribf(1, 1, 1);
     340            0 :         gle::attribf(-1, -1, 1);
     341            0 :         gle::attribf(1, -1, 1);
     342            0 :         xtraverts += gle::end();
     343            0 :     }
     344              : 
     345              :     /* === general sky rendering === */
     346              :     VAR(showsky, 0, 1, 1);
     347              :     VAR(clampsky, 0, 1, 1);
     348              : }
     349              : 
     350              : // externally relevant functionality
     351              : 
     352            0 : bool limitsky()
     353              : {
     354            0 :     return explicitsky && (useskytexture || editmode);
     355              : }
     356              : 
     357            0 : void drawskybox(bool clear)
     358              : {
     359            0 :     bool limited = false;
     360            0 :     if(limitsky())
     361              :     {
     362            0 :         for(vtxarray *va = visibleva; va; va = va->next)
     363              :         {
     364            0 :             if(va->sky && va->occluded < Occlude_BB &&
     365            0 :                ((va->skymax.x >= 0 && view.isvisiblebb(va->skymin, ivec(va->skymax).sub(va->skymin)) != ViewFrustumCull_NotVisible) ||
     366            0 :                 !insideworld(camera1->o)))
     367              :             {
     368            0 :                 limited = true;
     369            0 :                 break;
     370              :             }
     371              :         }
     372              :     }
     373            0 :     if(limited)
     374              :     {
     375            0 :         glDisable(GL_DEPTH_TEST);
     376              :     }
     377              :     else
     378              :     {
     379            0 :         glDepthFunc(GL_LEQUAL);
     380            0 :         glDepthMask(GL_FALSE);
     381              :     }
     382            0 :     if(clampsky)
     383              :     {
     384            0 :         glDepthRange(1, 1); //set gl depth range min and max to 1 (all far away)
     385              :     }
     386            0 :     if(clear || (!skybox[0] && (!atmo || atmoalpha < 1)))
     387              :     {
     388            0 :         vec skyboxcol = skyboxcolor.tocolor().mul(ldrscale); //local skyboxcol was skyboxcolor before skyboxcolour -> skyboxcolor uniformity change
     389            0 :         glClearColor(skyboxcol.x, skyboxcol.y, skyboxcol.z, 0);
     390            0 :         glClear(GL_COLOR_BUFFER_BIT);
     391              :     }
     392            0 :     if(skybox[0])
     393              :     {
     394            0 :         if(ldrscale < 1 && (skyboxoverbrightmin != 1 || (skyboxoverbright > 1 && skyboxoverbrightthreshold < 1)))
     395              :         {
     396            0 :             SETSHADER(skyboxoverbright);
     397            0 :             LOCALPARAMF(overbrightparams, skyboxoverbrightmin, std::max(skyboxoverbright, skyboxoverbrightmin), skyboxoverbrightthreshold);
     398            0 :         }
     399              :         else
     400              :         {
     401            0 :             SETSHADER(skybox);
     402              :         }
     403            0 :         gle::color(skyboxcolor);
     404              : 
     405            0 :         matrix4 skymatrix = cammatrix,
     406            0 :                 skyprojmatrix;
     407            0 :         skymatrix.settranslation(0, 0, 0);
     408            0 :         skymatrix.rotate_around_z((skyboxspin*lastmillis/1000.0f+skyboxyaw)/RAD);
     409            0 :         skyprojmatrix.mul(projmatrix, skymatrix);
     410            0 :         LOCALPARAM(skymatrix, skyprojmatrix);
     411              : 
     412            0 :         drawenvbox(sky);
     413              :     }
     414            0 :     if(atmo && (!skybox[0] || atmoalpha < 1))
     415              :     {
     416            0 :         if(atmoalpha < 1)
     417              :         {
     418            0 :             glEnable(GL_BLEND);
     419            0 :             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     420              :         }
     421              : 
     422            0 :         drawatmosphere();
     423              : 
     424            0 :         if(atmoalpha < 1)
     425              :         {
     426            0 :             glDisable(GL_BLEND);
     427              :         }
     428              :     }
     429            0 :     if(cloudlayer[0] && cloudheight)
     430              :     {
     431            0 :         SETSHADER(skybox);
     432              : 
     433            0 :         glDisable(GL_CULL_FACE);
     434              : 
     435            0 :         glEnable(GL_BLEND);
     436            0 :         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     437              : 
     438            0 :         matrix4 skymatrix = cammatrix,
     439            0 :                 skyprojmatrix;
     440            0 :         skymatrix.settranslation(0, 0, 0);
     441            0 :         skymatrix.rotate_around_z((cloudspin*lastmillis/1000.0f+cloudyaw)/RAD);
     442            0 :         skyprojmatrix.mul(projmatrix, skymatrix);
     443            0 :         LOCALPARAM(skymatrix, skyprojmatrix);
     444              : 
     445            0 :         drawenvoverlay(cloudoverlay, cloudoffsetx + cloudscrollx * lastmillis/1000.0f, cloudoffsety + cloudscrolly * lastmillis/1000.0f);
     446              : 
     447            0 :         glDisable(GL_BLEND);
     448              : 
     449            0 :         glEnable(GL_CULL_FACE);
     450              :     }
     451            0 :     if(clampsky)
     452              :     {
     453            0 :         glDepthRange(0, 1); //return depth range to normal
     454              :     }
     455            0 :     if(limited)
     456              :     {
     457            0 :         glEnable(GL_DEPTH_TEST);
     458              :     }
     459              :     else
     460              :     {
     461            0 :         glDepthMask(GL_TRUE);
     462            0 :         glDepthFunc(GL_LESS);
     463              :     }
     464            0 : }
        

Generated by: LCOV version 2.0-1