LCOV - code coverage report
Current view: top level - engine/render - rendersky.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.0 % 216 0
Test Date: 2025-10-23 06:06:06 Functions: 0.0 % 16 0

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

Generated by: LCOV version 2.0-1