LCOV - code coverage report
Current view: top level - engine/render - hud.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 22 221 10.0 %
Date: 2024-11-22 05:07:59 Functions: 5 16 31.2 %

          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, conh, 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 1.14