LCOV - code coverage report
Current view: top level - engine/render - hud.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 10.0 % 221 22
Test Date: 2025-02-18 06:21:28 Functions: 31.2 % 16 5

            Line data    Source code
       1              : /* hud.cpp: hud & main menu rendering code
       2              :  *
       3              :  * includes hud compass, hud readouts, crosshair handling
       4              :  * main hud rendering
       5              :  */
       6              : #include "../libprimis-headers/cube.h"
       7              : #include "../../shared/geomexts.h"
       8              : #include "../../shared/glemu.h"
       9              : #include "../../shared/glexts.h"
      10              : 
      11              : #include "hud.h"
      12              : #include "rendergl.h"
      13              : #include "renderlights.h"
      14              : #include "renderparticles.h"
      15              : #include "rendertext.h"
      16              : #include "renderttf.h"
      17              : #include "rendertimers.h"
      18              : #include "renderwindow.h"
      19              : #include "shader.h"
      20              : #include "shaderparam.h"
      21              : #include "texture.h"
      22              : 
      23              : #include "interface/console.h"
      24              : #include "interface/control.h"
      25              : #include "interface/input.h"
      26              : #include "interface/menus.h"
      27              : #include "interface/ui.h"
      28              : 
      29              : #include "world/octaedit.h"
      30              : 
      31              : //internal functionality not seen by other files
      32              : namespace
      33              : {
      34              :     //damagecompass* vars control display of directional hints as to damage location
      35              :     VARNP(damagecompass, usedamagecompass, 0, 1, 1);
      36              :     VARP(damagecompassfade, 1, 1000, 10000); //sets milliseconds before damage hints fade
      37              :     VARP(damagecompasssize, 1, 30, 100);
      38              :     VARP(damagecompassalpha, 1, 25, 100);
      39              :     VARP(damagecompassmin, 1, 25, 1000);
      40              :     VARP(damagecompassmax, 1, 200, 1000);
      41              : 
      42              :     std::array<float, 8> damagedirs = { 0, 0, 0, 0, 0, 0, 0, 0 };
      43              : 
      44            0 :     void drawdamagecompass(int w, int h)
      45              :     {
      46            0 :         hudnotextureshader->set();
      47              : 
      48            0 :         int dirs = 0;
      49            0 :         float size = damagecompasssize/100.0f*std::min(h, w)/2.0f;
      50            0 :         for(float &dir : damagedirs)
      51              :         {
      52            0 :             if(dir > 0)
      53              :             {
      54            0 :                 if(!dirs)
      55              :                 {
      56            0 :                     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      57            0 :                     gle::colorf(1, 0, 0, damagecompassalpha/100.0f);
      58            0 :                     gle::defvertex();
      59            0 :                     gle::begin(GL_TRIANGLES);
      60              :                 }
      61            0 :                 dirs++;
      62              : 
      63            0 :                 float logscale = 32,
      64            0 :                       scale = log(1 + (logscale - 1)*dir) / std::log(logscale),
      65            0 :                       offset = -size/2.0f-std::min(h, w)/4.0f;
      66            0 :                 matrix4x3 m;
      67            0 :                 m.identity();
      68            0 :                 m.settranslation(w/2, h/2, 0);
      69            0 :                 m.rotate_around_z(dir*45/RAD);
      70            0 :                 m.translate(0, offset, 0);
      71            0 :                 m.scale(size*scale);
      72              : 
      73            0 :                 gle::attrib(m.transform(vec2(1, 1)));
      74            0 :                 gle::attrib(m.transform(vec2(-1, 1)));
      75            0 :                 gle::attrib(m.transform(vec2(0, 0)));
      76              : 
      77              :                 // fade in log space so short blips don't disappear too quickly
      78            0 :                 scale -= static_cast<float>(curtime)/damagecompassfade;
      79            0 :                 dir = scale > 0 ? (std::pow(logscale, scale) - 1) / (logscale - 1) : 0;
      80              :             }
      81              :         }
      82            0 :         if(dirs)
      83              :         {
      84            0 :             gle::end();
      85              :         }
      86            0 :     }
      87              : 
      88              :     int damageblendmillis = 0;
      89              : 
      90              :     //damagescreen variables control the display of a texture upon player being damaged
      91            0 :     VARFP(damagescreen, 0, 1, 1, { if(!damagescreen) damageblendmillis = 0; });
      92              :     VARP(damagescreenfactor, 1, 75, 100);
      93              :     VARP(damagescreenalpha, 1, 45, 100);
      94              :     VARP(damagescreenfade, 0, 1000, 1000); //number of ms before screen damage fades
      95              :     VARP(damagescreenmin, 1, 10, 1000);
      96              :     VARP(damagescreenmax, 1, 100, 1000);
      97              : 
      98            0 :     void drawdamagescreen(int w, int h)
      99              :     {
     100              :         static Texture *damagetex = nullptr;
     101              :         //preload this texture even if not going to draw, to prevent stutter when first hit
     102            0 :         if(!damagetex)
     103              :         {
     104            0 :             damagetex = textureload("media/interface/hud/damage.png", 3);
     105              :         }
     106            0 :         if(lastmillis >= damageblendmillis)
     107              :         {
     108            0 :             return;
     109              :         }
     110            0 :         hudshader->set();
     111            0 :         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
     112            0 :         glBindTexture(GL_TEXTURE_2D, damagetex->id);
     113            0 :         float fade = damagescreenalpha/100.0f;
     114            0 :         if(damageblendmillis - lastmillis < damagescreenfade)
     115              :         {
     116            0 :             fade *= static_cast<float>(damageblendmillis - lastmillis)/damagescreenfade;
     117              :         }
     118            0 :         gle::colorf(fade, fade, fade, fade);
     119              : 
     120            0 :         hudquad(0, 0, w, h);
     121              :     }
     122              : 
     123              :     VAR(showstats, 0, 1, 1);
     124              : 
     125              :     //crosshair & cursor vars
     126              :     VARP(crosshairsize, 0, 15, 50);
     127              :     VARP(cursorsize, 0, 15, 30);
     128              :     VARP(crosshairfx, 0, 1, 1); // hit fx
     129              : 
     130              : 
     131            2 :     const char *defaultcrosshair(int index)
     132              :     {
     133            2 :         switch(index)
     134              :         {
     135            0 :             case 2:
     136              :             {
     137            0 :                 return "media/interface/crosshair/default_hit.png";
     138              :             }
     139            0 :             case 1:
     140              :             {
     141            0 :                 return "media/interface/crosshair/teammate.png";
     142              :             }
     143            2 :             default:
     144              :             {
     145            2 :                 return "media/interface/crosshair/default.png";
     146              :             }
     147              :         }
     148              :     }
     149              : 
     150              :     const int maxcrosshairs = 4;
     151              :     std::array<Texture *, maxcrosshairs> crosshairs = { nullptr, nullptr, nullptr, nullptr };
     152              : 
     153            1 :     void loadcrosshair(const char *name, int i)
     154              :     {
     155            1 :         if(i < 0 || i >= maxcrosshairs)
     156              :         {
     157            0 :             return;
     158              :         }
     159            1 :         crosshairs[i] = name ? textureload(name, 3, true) : notexture;
     160            1 :         if(crosshairs[i] == notexture)
     161              :         {
     162            1 :             name = defaultcrosshair(i);
     163            1 :             if(!name)
     164              :             {
     165            0 :                 name = "media/interface/crosshair/default.png";
     166              :             }
     167            1 :             crosshairs[i] = textureload(name, 3, true);
     168              :         }
     169              :     }
     170              : 
     171            1 :     void getcrosshair(int *i)
     172              :     {
     173            1 :         const char *name = "";
     174            1 :         if(*i >= 0 && *i < maxcrosshairs)
     175              :         {
     176            1 :             name = crosshairs[*i] ? crosshairs[*i]->name : defaultcrosshair(*i);
     177            1 :             if(!name)
     178              :             {
     179            0 :                 name = "media/interface/crosshair/default.png";
     180              :             }
     181              :         }
     182            1 :         result(name);
     183            1 :     }
     184              : 
     185            0 :     void drawcrosshair(int w, int h, int crosshairindex)
     186              :     {
     187            0 :         bool windowhit = UI::hascursor();
     188            0 :         if(!windowhit && (!showhud || mainmenu))
     189              :         {
     190            0 :             return; //(!showhud || player->state==CS_SPECTATOR || player->state==CS_DEAD)) return;
     191              :         }
     192            0 :         float cx = 0.5f,
     193            0 :               cy = 0.5f,
     194              :               chsize;
     195              :         Texture *crosshair;
     196            0 :         if(windowhit)
     197              :         {
     198              :             static Texture *cursor = nullptr;
     199            0 :             if(!cursor)
     200              :             {
     201            0 :                 cursor = textureload("media/interface/cursor.png", 3, true);
     202              :             }
     203            0 :             crosshair = cursor;
     204            0 :             chsize = cursorsize*w/900.0f;
     205            0 :             UI::getcursorpos(cx, cy);
     206              :         }
     207              :         else
     208              :         {
     209            0 :             int index = crosshairindex;
     210            0 :             if(index < 0)
     211              :             {
     212            0 :                 return;
     213              :             }
     214            0 :             if(!crosshairfx)
     215              :             {
     216            0 :                 index = 0;
     217              :             }
     218            0 :             crosshair = crosshairs[index];
     219            0 :             if(!crosshair)
     220              :             {
     221            0 :                 loadcrosshair(nullptr, index);
     222            0 :                 crosshair = crosshairs[index];
     223              :             }
     224            0 :             chsize = crosshairsize*w/900.0f;
     225              :         }
     226            0 :         vec color = vec(1, 1, 1);
     227            0 :         if(crosshair->type&Texture::ALPHA)
     228              :         {
     229            0 :             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     230              :         }
     231              :         else
     232              :         {
     233            0 :             glBlendFunc(GL_ONE, GL_ONE);
     234              :         }
     235            0 :         hudshader->set();
     236            0 :         gle::color(color);
     237            0 :         float x = cx*w - (windowhit ? 0 : chsize/2.0f),
     238            0 :               y = cy*h - (windowhit ? 0 : chsize/2.0f);
     239            0 :         glBindTexture(GL_TEXTURE_2D, crosshair->id);
     240              : 
     241            0 :         hudquad(x, y, chsize, chsize);
     242              :     }
     243              : 
     244              :     //hud time displays
     245              :     VARP(wallclock, 0, 0, 1);     //toggles hud readout
     246              :     VARP(wallclock24, 0, 0, 1);   //toggles 12h (US) or 24h time
     247              :     VARP(wallclocksecs, 0, 0, 1); //seconds precision on hud readout
     248              : 
     249              :     time_t walltime = 0;
     250              : 
     251              :     //hud fps displays
     252              :     VARP(showfps, 0, 1, 1);      //toggles showing game framerate
     253              :     VARP(showfpsrange, 0, 0, 1); //toggles showing min/max framerates as well
     254              : }
     255              : 
     256              : //externally relevant functionality
     257              : //from here to iengine section, functions stay local to the libprimis codebase
     258              : 
     259            0 : void gl_drawmainmenu()
     260              : {
     261            0 :     renderbackground(nullptr, nullptr, nullptr, nullptr, true);
     262            0 : }
     263              : 
     264            0 : void gl_drawhud(int crosshairindex, void(* hud2d)())
     265              : {
     266              :     /* we want to get the length of the frame at the end of the frame,
     267              :      * not the middle, so we have a persistent variable inside the
     268              :      * function scope
     269              :      */
     270              :     static int framemillis = 0;
     271              : 
     272            0 :     int w = hudw(),
     273            0 :         h = hudh();
     274            0 :     if(forceaspect)
     275              :     {
     276            0 :         w = static_cast<int>(ceil(h*forceaspect));
     277              :     }
     278              : 
     279            0 :     gettextres(w, h);
     280              : 
     281            0 :     hudmatrix.ortho(0, w, h, 0, -1, 1);
     282            0 :     resethudmatrix();
     283            0 :     resethudshader();
     284              : 
     285            0 :     pushfont();
     286            0 :     setfont("default_outline");
     287              : 
     288            0 :     debuglights();
     289              : 
     290            0 :     glEnable(GL_BLEND);
     291              : 
     292            0 :     if(!mainmenu)
     293              :     {
     294            0 :         drawdamagescreen(w, h);
     295            0 :         drawdamagecompass(w, h);
     296              :     }
     297              : 
     298            0 :     float conw = w/conscale,
     299            0 :           conh = h/conscale,
     300            0 :           abovehud = conh - FONTH;
     301            0 :     if(showhud && !mainmenu)
     302              :     {
     303            0 :         if(showstats)
     304              :         {
     305            0 :             pushhudscale(conscale);
     306            0 :             ttr.fontsize(42);
     307            0 :             int roffset = 0;
     308            0 :             if(showfps)
     309              :             {
     310              :                 static int lastfps = 0;
     311              :                 static std::array<int, 3> prevfps = { 0, 0, 0 },
     312              :                                           curfps = { 0, 0, 0 };
     313            0 :                 if(totalmillis - lastfps >= statrate)
     314              :                 {
     315            0 :                     prevfps = curfps;
     316            0 :                     lastfps = totalmillis - (totalmillis%statrate);
     317              :                 }
     318              :                 std::array<int, 3> nextfps;
     319            0 :                 getfps(nextfps[0], nextfps[1], nextfps[2]);
     320            0 :                 for(size_t i = 0; i < curfps.size(); ++i)
     321              :                 {
     322            0 :                     if(prevfps[i]==curfps[i])
     323              :                     {
     324            0 :                         curfps[i] = nextfps[i];
     325              :                     }
     326              :                 }
     327            0 :                 if(showfpsrange)
     328              :                 {
     329              :                     char fpsstring[20];
     330            0 :                     std::sprintf(fpsstring, "fps %d+%d-%d", curfps[0], curfps[1], curfps[2]);
     331            0 :                     ttr.renderttf(fpsstring, {0xFF, 0xFF, 0xFF, 0},  conw-(1000*conscale), conh-(360*conscale));
     332              :                     //draw_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]);
     333              :                 }
     334              :                 else
     335              :                 {
     336              :                     char fpsstring[20];
     337            0 :                     std::sprintf(fpsstring, "fps %d", curfps[0]);
     338            0 :                     ttr.renderttf(fpsstring, {0xFF, 0xFF, 0xFF, 0},  conw-(1000*conscale), conh-(360*conscale));
     339              :                 }
     340            0 :                 roffset += FONTH;
     341              :             }
     342            0 :             printtimers(conw, framemillis);
     343            0 :             if(wallclock)
     344              :             {
     345            0 :                 if(!walltime)
     346              :                 {
     347            0 :                     walltime = std::time(nullptr);
     348            0 :                     walltime -= totalmillis/1000;
     349            0 :                     if(!walltime)
     350              :                     {
     351            0 :                         walltime++;
     352              :                     }
     353              :                 }
     354            0 :                 time_t walloffset = walltime + totalmillis/1000;
     355            0 :                 std::tm* localvals = std::localtime(&walloffset);
     356              :                 static string buf;
     357            0 :                 if(localvals && std::strftime(buf, sizeof(buf), wallclocksecs ? (wallclock24 ? "%H:%M:%S" : "%I:%M:%S%p") : (wallclock24 ? "%H:%M" : "%I:%M%p"), localvals))
     358              :                 {
     359              :                     // hack because not all platforms (windows) support %P lowercase option
     360              :                     // also strip leading 0 from 12 hour time
     361            0 :                     char *dst = buf;
     362            0 :                     const char *src = &buf[!wallclock24 && buf[0]=='0' ? 1 : 0];
     363            0 :                     while(*src)
     364              :                     {
     365            0 :                         *dst++ = tolower(*src++);
     366              :                     }
     367            0 :                     *dst++ = '\0';
     368              : 
     369            0 :                     ttr.renderttf(buf, { 0xFF, 0xFF, 0xFF, 0 }, conw-(1000*conscale), conh-(540*conscale));
     370              :                     //draw_text(buf, conw-5*FONTH, conh-FONTH*3/2-roffset);
     371            0 :                     roffset += FONTH;
     372              :                 }
     373              :             }
     374            0 :             pophudmatrix();
     375              :         }
     376            0 :         if(!editmode)
     377              :         {
     378            0 :             resethudshader();
     379            0 :             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     380            0 :             hud2d();
     381            0 :             abovehud = std::min(abovehud, conh);
     382              :         }
     383            0 :         rendertexturepanel(w, h);
     384              :     }
     385              : 
     386            0 :     abovehud = std::min(abovehud, conh*UI::abovehud());
     387              : 
     388            0 :     pushhudscale(conscale);
     389            0 :     abovehud -= rendercommand(FONTH/2, abovehud - FONTH/2, conw-FONTH);
     390            0 :     if(showhud && !UI::uivisible("fullconsole"))
     391              :     {
     392            0 :         renderconsole(conw, conh, abovehud - FONTH/2);
     393              :     }
     394            0 :     pophudmatrix();
     395            0 :     drawcrosshair(w, h, crosshairindex);
     396            0 :     glDisable(GL_BLEND);
     397            0 :     popfont();
     398            0 :     if(frametimer)
     399              :     {
     400            0 :         glFinish();
     401            0 :         framemillis = getclockmillis() - totalmillis;
     402              :     }
     403            0 : }
     404              : 
     405            0 : void writecrosshairs(std::fstream& f)
     406              : {
     407            0 :     for(int i = 0; i < maxcrosshairs; ++i)
     408              :     {
     409            0 :         if(crosshairs[i] && crosshairs[i]!=notexture)
     410              :         {
     411            0 :             f << "loadcrosshair " << escapestring(crosshairs[i]->name) << " " << i << std::endl;
     412              :         }
     413              :     }
     414            0 :     f << std::endl;
     415            0 : }
     416              : 
     417            0 : void resethudshader()
     418              : {
     419            0 :     hudshader->set();
     420            0 :     gle::colorf(1, 1, 1);
     421            0 : }
     422              : 
     423              : FVARP(conscale, 1e-3f, 0.33f, 1e3f); //size of readouts, console, and history
     424              : //note: fps displayed is the average over the statrate duration
     425              : VAR(statrate, 1, 200, 1000);  //update time for fps and edit stats
     426              : VAR(showhud, 0, 1, 1);
     427              : 
     428            0 : void vectoryawpitch(const vec &v, float &yaw, float &pitch)
     429              : {
     430            0 :     if(v.iszero())
     431              :     {
     432            0 :         yaw = pitch = 0;
     433              :     }
     434              :     else
     435              :     {
     436            0 :         yaw = -std::atan2(v.x, v.y)*RAD;
     437            0 :         pitch = std::asin(v.z/v.magnitude())*RAD;
     438              :     }
     439            0 : }
     440              : 
     441              : // iengine functionality
     442            0 : void damagecompass(int n, const vec &loc)
     443              : {
     444            0 :     if(!usedamagecompass || minimized)
     445              :     {
     446            0 :         return;
     447              :     }
     448            0 :     vec delta(loc);
     449            0 :     delta.sub(camera1->o);
     450            0 :     float yaw = 0,
     451              :           pitch;
     452            0 :     if(delta.magnitude() > 4)
     453              :     {
     454            0 :         vectoryawpitch(delta, yaw, pitch);
     455            0 :         yaw -= camera1->yaw;
     456              :     }
     457            0 :     if(yaw >= 360)
     458              :     {
     459            0 :         yaw = std::fmod(yaw, 360);
     460              :     }
     461            0 :     else if(yaw < 0)
     462              :     {
     463            0 :         yaw = 360 - std::fmod(-yaw, 360);
     464              :     }
     465            0 :     int dir = (static_cast<int>(yaw+22.5f)%360)/45; //360/45 = 8, so divide into 8 octants with 0 degrees centering octant 0
     466            0 :     damagedirs[dir] += std::max(n, damagecompassmin)/static_cast<float>(damagecompassmax);
     467            0 :     if(damagedirs[dir]>1)
     468              :     {
     469            0 :         damagedirs[dir] = 1;
     470              :     }
     471              : }
     472              : 
     473            0 : void damageblend(int n)
     474              : {
     475            0 :     if(!damagescreen || minimized)
     476              :     {
     477            0 :         return;
     478              :     }
     479            0 :     if(lastmillis > damageblendmillis)
     480              :     {
     481            0 :         damageblendmillis = lastmillis;
     482              :     }
     483            0 :     damageblendmillis += std::clamp(n, damagescreenmin, damagescreenmax)*damagescreenfactor;
     484              : }
     485              : 
     486            1 : void inithudcmds()
     487              : {
     488            2 :     addcommand("loadcrosshair", reinterpret_cast<identfun>(+[](const char *name, int *i){loadcrosshair(name, *i);}), "si", Id_Command);
     489            1 :     addcommand("getcrosshair", reinterpret_cast<identfun>(getcrosshair), "i", Id_Command);
     490            1 : }
        

Generated by: LCOV version 2.0-1