LCOV - code coverage report
Current view: top level - engine/render - stain.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 0 585 0.0 %
Date: 2024-11-22 05:07:59 Functions: 0 46 0.0 %

          Line data    Source code
       1             : /* stain.cpp: dynamic world geometry decals
       2             :  *
       3             :  * Stains are mostly useful for player-created effects, mainly those left behind
       4             :  * by weapons. They fade at a fixed period (not controllable by individual stains)
       5             :  * and can be culled if there are too many (`maxstaintris`).
       6             :  *
       7             :  * Stains apply to world (octree) geometry only and cannot be applied to models
       8             :  * (players, mapmodels, or otherwise).
       9             :  *
      10             :  * The performance of stains is generally high enough that many thousands of stain
      11             :  * particles must be present at once for there to be a noticable performance drop.
      12             :  */
      13             : 
      14             : #include "../libprimis-headers/cube.h"
      15             : #include "../../shared/geomexts.h"
      16             : #include "../../shared/glemu.h"
      17             : #include "../../shared/glexts.h"
      18             : 
      19             : #include <memory>
      20             : #include <optional>
      21             : 
      22             : #include "octarender.h"
      23             : #include "rendergl.h"
      24             : #include "renderlights.h"
      25             : #include "rendermodel.h"
      26             : #include "renderwindow.h"
      27             : #include "stain.h"
      28             : #include "shader.h"
      29             : #include "shaderparam.h"
      30             : #include "texture.h"
      31             : 
      32             : #include "interface/console.h"
      33             : #include "interface/control.h"
      34             : 
      35             : #include "world/bih.h"
      36             : #include "world/entities.h"
      37             : #include "world/material.h"
      38             : #include "world/octaworld.h"
      39             : #include "world/world.h"
      40             : 
      41             : #include "model/model.h"
      42             : 
      43             : void initstains();
      44             : 
      45           0 : VARFP(maxstaintris, 1, 2048, 16384, initstains());  //need to call initstains to potentially cull extra stain tris
      46             : VARP(stainfade, 1000, 15000, 60000);                //number of milliseconds before stain geom fades
      47             : VAR(debugstain, 0, 0, 1);                           //toggles printout of stain information to console
      48             : 
      49             : //stainrenderer: handles rendering to the gbuffer of a single class of particle
      50             : //each stainrenderer handles the rendering of a single type of particle
      51             : //all the level's particles of a single type will be handled by a single stainrenderer object
      52             : class stainrenderer
      53             : {
      54             :     public:
      55             :         enum
      56             :         {
      57             :             StainFlag_Rnd4       = 1<<0,
      58             :             StainFlag_Rotate     = 1<<1,
      59             :             StainFlag_InvMod     = 1<<2,
      60             :             StainFlag_Overbright = 1<<3,
      61             :             StainFlag_Glow       = 1<<4,
      62             :             StainFlag_Saturate   = 1<<5
      63             :         };
      64             : 
      65           0 :         stainrenderer(const char *texname, int flags = 0, int fadeintime = 0, int fadeouttime = 1000, int timetolive = -1)
      66           0 :             : flags(flags),
      67           0 :               fadeintime(fadeintime), fadeouttime(fadeouttime), timetolive(timetolive),
      68           0 :               maxstains(0), startstain(0), endstain(0),
      69           0 :               stainu(0), stainv(0), tex(nullptr), stains(nullptr), texname(texname)
      70             :         {
      71           0 :         }
      72             : 
      73           0 :         ~stainrenderer()
      74             :         {
      75           0 :             delete[] stains;
      76           0 :         }
      77             : 
      78           0 :         bool usegbuffer() const
      79             :         {
      80           0 :             return !(flags&(StainFlag_InvMod|StainFlag_Glow));
      81             :         }
      82             : 
      83           0 :         void init(int tris)
      84             :         {
      85           0 :             if(stains)
      86             :             {
      87           0 :                 delete[] stains; //do not need to set null, immediately reassigned below
      88           0 :                 maxstains = startstain = endstain = 0;
      89             :             }
      90           0 :             stains = new staininfo[tris];
      91           0 :             maxstains = tris;
      92           0 :             for(int i = 0; i < StainBuffer_Number; ++i)
      93             :             {
      94           0 :                 verts[i].init(i == StainBuffer_Transparent ? tris/2 : tris);
      95             :             }
      96           0 :         }
      97             : 
      98           0 :         void preload()
      99             :         {
     100           0 :             tex = textureload(texname, 3);
     101           0 :         }
     102             : 
     103           0 :         bool hasstains(int sbuf)
     104             :         {
     105           0 :             return verts[sbuf].hasverts();
     106             :         }
     107             : 
     108           0 :         void clearstains()
     109             :         {
     110           0 :             startstain = endstain = 0;
     111           0 :             for(stainbuffer &i : verts)
     112             :             {
     113           0 :                 i.clear();
     114             :             }
     115           0 :         }
     116             : 
     117           0 :         void clearfadedstains()
     118             :         {
     119           0 :             int threshold = lastmillis - (timetolive>=0 ? timetolive : stainfade) - fadeouttime;
     120           0 :             staininfo *d = &stains[startstain],
     121           0 :                       *end = &stains[endstain < startstain ? maxstains : endstain],
     122           0 :                       *cleared[StainBuffer_Number] = {nullptr};
     123           0 :             for(; d < end && d->millis <= threshold; d++)
     124           0 :                 cleared[d->owner] = d;
     125           0 :             if(d >= end && endstain < startstain)
     126             :             {
     127           0 :                 for(d = stains, end = &stains[endstain]; d < end && d->millis <= threshold; d++)
     128             :                 {
     129           0 :                     cleared[d->owner] = d;
     130             :                 }
     131             :             }
     132           0 :             startstain = d - stains;
     133           0 :             if(startstain == endstain)
     134             :             {
     135           0 :                 for(stainbuffer &i : verts)
     136             :                 {
     137           0 :                     i.clear();
     138             :                 }
     139             :             }
     140             :             else
     141             :             {
     142           0 :                 for(int i = 0; i < StainBuffer_Number; ++i)
     143             :                 {
     144           0 :                     if(cleared[i])
     145             :                     {
     146           0 :                         verts[i].clearstains(*cleared[i]);
     147             :                     }
     148             :                 }
     149             :             }
     150           0 :         }
     151             : 
     152           0 :         void fadeinstains()
     153             :         {
     154           0 :             if(!fadeintime)
     155             :             {
     156           0 :                 return;
     157             :             }
     158           0 :             staininfo *d = &stains[endstain],
     159           0 :                       *end = &stains[endstain < startstain ? 0 : startstain];
     160           0 :             while(d > end)
     161             :             {
     162           0 :                 d--;
     163           0 :                 int fade = lastmillis - d->millis;
     164           0 :                 if(fade < fadeintime)
     165             :                 {
     166           0 :                     fadestain(*d, (fade<<8)/fadeintime);
     167             :                 }
     168           0 :                 else if(faded(*d))
     169             :                 {
     170           0 :                     fadestain(*d, 255);
     171             :                 }
     172             :                 else
     173             :                 {
     174           0 :                     return;
     175             :                 }
     176             :             }
     177           0 :             if(endstain < startstain)
     178             :             {
     179           0 :                 d = &stains[maxstains];
     180           0 :                 end = &stains[startstain];
     181           0 :                 while(d > end)
     182             :                 {
     183           0 :                     d--;
     184           0 :                     int fade = lastmillis - d->millis;
     185           0 :                     if(fade < fadeintime)
     186             :                     {
     187           0 :                         fadestain(*d, (fade<<8)/fadeintime);
     188             :                     }
     189           0 :                     else if(faded(*d))
     190             :                     {
     191           0 :                         fadestain(*d, 255);
     192             :                     }
     193             :                     else
     194             :                     {
     195           0 :                         return;
     196             :                     }
     197             :                 }
     198             :             }
     199             :         }
     200             : 
     201           0 :         void fadeoutstains()
     202             :         {
     203           0 :             staininfo *d = &stains[startstain],
     204           0 :                       *end = &stains[endstain < startstain ? maxstains : endstain];
     205           0 :             int offset = (timetolive>=0 ? timetolive : stainfade) + fadeouttime - lastmillis;
     206           0 :             while(d < end)
     207             :             {
     208           0 :                 int fade = d->millis + offset;
     209           0 :                 if(fade >= fadeouttime)
     210             :                 {
     211           0 :                     return;
     212             :                 }
     213           0 :                 fadestain(*d, (fade<<8)/fadeouttime);
     214           0 :                 d++;
     215             :             }
     216           0 :             if(endstain < startstain)
     217             :             {
     218           0 :                 d = stains;
     219           0 :                 end = &stains[endstain];
     220           0 :                 while(d < end)
     221             :                 {
     222           0 :                     int fade = d->millis + offset;
     223           0 :                     if(fade >= fadeouttime)
     224             :                     {
     225           0 :                         return;
     226             :                     }
     227           0 :                     fadestain(*d, (fade<<8)/fadeouttime);
     228           0 :                     d++;
     229             :                 }
     230             :             }
     231             :         }
     232             : 
     233           0 :         static void setuprenderstate(int sbuf, bool gbuf, int layer)
     234             :         {
     235           0 :             if(gbuf)
     236             :             {
     237           0 :                 maskgbuffer(sbuf == StainBuffer_Transparent ? "cg" : "c");
     238             :             }
     239             :             else
     240             :             {
     241           0 :                 zerofogcolor();
     242             :             }
     243             : 
     244           0 :             if(layer && ghasstencil)
     245             :             {
     246           0 :                 glStencilFunc(GL_EQUAL, layer, 0x07);
     247           0 :                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
     248             :             }
     249             : 
     250           0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
     251             : 
     252           0 :             enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
     253             : 
     254           0 :             glDepthMask(GL_FALSE);
     255           0 :             glEnable(GL_BLEND);
     256             : 
     257           0 :             gle::enablevertex();
     258           0 :             gle::enabletexcoord0();
     259           0 :             gle::enablecolor();
     260           0 :         }
     261             : 
     262           0 :         static void cleanuprenderstate(int sbuf, bool gbuf, int layer)
     263             :         {
     264           0 :             gle::clearvbo();
     265             : 
     266           0 :             gle::disablevertex();
     267           0 :             gle::disabletexcoord0();
     268           0 :             gle::disablecolor();
     269             : 
     270           0 :             glDepthMask(GL_TRUE);
     271           0 :             glDisable(GL_BLEND);
     272             : 
     273           0 :             disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
     274             : 
     275           0 :             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
     276             : 
     277           0 :             if(gbuf)
     278             :             {
     279           0 :                 maskgbuffer(sbuf == StainBuffer_Transparent ? "cndg" : "cnd");
     280             :             }
     281             :             else
     282             :             {
     283           0 :                 resetfogcolor();
     284             :             }
     285           0 :         }
     286             : 
     287           0 :         void cleanup()
     288             :         {
     289           0 :             for(stainbuffer &i : verts)
     290             :             {
     291           0 :                 i.cleanup();
     292             :             }
     293           0 :         }
     294             : 
     295           0 :         void render(int sbuf)
     296             :         {
     297           0 :             float colorscale = 1,
     298           0 :                   alphascale = 1;
     299           0 :             if(flags&StainFlag_Overbright)
     300             :             {
     301           0 :                 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
     302           0 :                 SETVARIANT(overbrightstain, sbuf == StainBuffer_Transparent ? 0 : -1, 0);
     303             :             }
     304           0 :             else if(flags&StainFlag_Glow)
     305             :             {
     306           0 :                 glBlendFunc(GL_ONE, GL_ONE);
     307           0 :                 colorscale = ldrscale;
     308           0 :                 if(flags&StainFlag_Saturate)
     309             :                 {
     310           0 :                     colorscale *= 2;
     311             :                 }
     312           0 :                 alphascale = 0;
     313           0 :                 SETSHADER(foggedstain);
     314             :             }
     315           0 :             else if(flags&StainFlag_InvMod)
     316             :             {
     317           0 :                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
     318           0 :                 alphascale = 0;
     319           0 :                 SETSHADER(foggedstain);
     320             :             }
     321             :             else
     322             :             {
     323           0 :                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     324           0 :                 colorscale = ldrscale;
     325           0 :                 if(flags&StainFlag_Saturate)
     326             :                 {
     327           0 :                     colorscale *= 2;
     328             :                 }
     329           0 :                 SETVARIANT(stain, sbuf == StainBuffer_Transparent ? 0 : -1, 0);
     330             :             }
     331           0 :             LOCALPARAMF(colorscale, colorscale, colorscale, colorscale, alphascale);
     332             : 
     333           0 :             glBindTexture(GL_TEXTURE_2D, tex->id);
     334             : 
     335           0 :             verts[sbuf].render();
     336           0 :         }
     337             : 
     338           0 :         void addstain(const vec &center, const vec &dir, float radius, const bvec &color, int info, const cubeworld &world)
     339             :         {
     340           0 :             if(dir.iszero())
     341             :             {
     342           0 :                 return;
     343             :             }
     344           0 :             bbmin = ivec(center).sub(radius);
     345           0 :             bbmax = ivec(center).add(radius).add(1);
     346             : 
     347           0 :             staincolor = vec4<uchar>(color, 255);
     348           0 :             staincenter = center;
     349           0 :             stainradius = radius;
     350           0 :             stainnormal = dir;
     351             : 
     352           0 :             staintangent = vec(dir.z, -dir.x, dir.y);
     353           0 :             staintangent.project(dir);
     354             : 
     355           0 :             if(flags&StainFlag_Rotate)
     356             :             {
     357           0 :                 staintangent.rotate(sincos360[randomint(360)], dir);
     358             :             }
     359           0 :             staintangent.normalize();
     360           0 :             stainbitangent.cross(staintangent, dir);
     361           0 :             if(flags&StainFlag_Rnd4)
     362             :             {
     363           0 :                 stainu = 0.5f*(info&1);
     364           0 :                 stainv = 0.5f*((info>>1)&1);
     365             :             }
     366             : 
     367           0 :             for(int i = 0; i < StainBuffer_Number; ++i)
     368             :             {
     369           0 :                 verts[i].lastvert = verts[i].endvert;
     370             :             }
     371           0 :             gentris(*world.worldroot, ivec(0, 0, 0), rootworld.mapsize()>>1);
     372           0 :             for(int i = 0; i < StainBuffer_Number; ++i)
     373             :             {
     374           0 :                 stainbuffer &buf = verts[i];
     375           0 :                 if(buf.endvert == buf.lastvert)
     376             :                 {
     377           0 :                     continue;
     378             :                 }
     379           0 :                 if(debugstain)
     380             :                 {
     381           0 :                     int nverts = buf.nextverts();
     382             :                     static const char * const sbufname[StainBuffer_Number] = { "opaque", "transparent", "mapmodel" };
     383           0 :                     conoutf(Console_Debug, "tris = %d, verts = %d, total tris = %d, %s", nverts/3, nverts, buf.totaltris(), sbufname[i]);
     384             :                 }
     385             : 
     386           0 :                 staininfo &d = newstain();
     387           0 :                 d.owner = i;
     388           0 :                 d.color = color;
     389           0 :                 d.millis = lastmillis;
     390           0 :                 d.startvert = buf.lastvert;
     391           0 :                 d.endvert = buf.endvert;
     392           0 :                 buf.addstain();
     393             :             }
     394             :         }
     395             : 
     396           0 :         void genmmtri(const std::array<vec, 3> &v) // gen map model triangles
     397             :         {
     398           0 :             vec n;
     399           0 :             n.cross(v[0], v[1], v[2]).normalize();
     400           0 :             float facing = n.dot(stainnormal);
     401           0 :             if(facing <= 0)
     402             :             {
     403           0 :                 return;
     404             :             }
     405           0 :             vec p = vec(v[0]).sub(staincenter);
     406           0 :             float dist = n.dot(p);
     407           0 :             if(std::fabs(dist) > stainradius)
     408             :             {
     409           0 :                 return;
     410             :             }
     411           0 :             vec pcenter = vec(n).mul(dist).add(staincenter);
     412           0 :             vec ft, fb;
     413           0 :             ft.orthogonal(n);
     414           0 :             ft.normalize();
     415           0 :             fb.cross(ft, n);
     416           0 :             vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
     417           0 :                 pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
     418           0 :             vec v1[3+4],
     419           0 :                 v2[3+4];
     420           0 :             float ptc = pt.dot(pcenter),
     421           0 :                   pbc = pb.dot(pcenter);
     422           0 :             int numv = polyclip(v.data(), v.size(), pt, ptc - stainradius, ptc + stainradius, v1);
     423           0 :             if(numv<3) //check with v1
     424             :             {
     425           0 :                 return;
     426             :             }
     427           0 :             numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
     428           0 :             if(numv<3) //check again with v2
     429             :             {
     430           0 :                 return;
     431             :             }
     432           0 :             float tsz = flags&StainFlag_Rnd4 ? 0.5f : 1.0f,
     433           0 :                   scale = tsz*0.5f/stainradius,
     434           0 :                   tu = stainu + tsz*0.5f - ptc*scale,
     435           0 :                   tv = stainv + tsz*0.5f - pbc*scale;
     436           0 :             pt.mul(scale); pb.mul(scale);
     437           0 :             stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
     438           0 :                       dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
     439           0 :             int totalverts = 3*(numv-2);
     440           0 :             stainbuffer &buf = verts[StainBuffer_Mapmodel];
     441           0 :             if(totalverts > buf.maxverts-3)
     442             :             {
     443           0 :                 return;
     444             :             }
     445           0 :             while(buf.availverts < totalverts)
     446             :             {
     447           0 :                 if(!freestain())
     448             :                 {
     449           0 :                     return;
     450             :                 }
     451             :             }
     452           0 :             for(int k = 0; k < numv-2; ++k)
     453             :             {
     454           0 :                 stainvert *tri = buf.addtri();
     455           0 :                 tri[0] = dv1;
     456           0 :                 tri[1] = dv2;
     457           0 :                 dv2.pos = v2[k+2];
     458           0 :                 dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
     459           0 :                 tri[2] = dv2;
     460             :             }
     461             :         }
     462             : 
     463             :     private:
     464             :         int flags, fadeintime, fadeouttime, timetolive;
     465             :         int maxstains, startstain, endstain;
     466             : 
     467             :         ivec bbmin, bbmax;
     468             :         vec staincenter, stainnormal, staintangent, stainbitangent;
     469             :         float stainradius, stainu, stainv;
     470             :         vec4<uchar> staincolor;
     471             :         Texture *tex;
     472             : 
     473             :         struct stainvert
     474             :         {
     475             :             vec pos;
     476             :             vec4<uchar> color;
     477             :             vec2 tc;
     478             :         };
     479             : 
     480             :         struct staininfo
     481             :         {
     482             :             int millis;
     483             :             bvec color;
     484             :             uchar owner;
     485             :             ushort startvert, endvert;
     486             :         };
     487             :         staininfo *stains;
     488             : 
     489             :         class stainbuffer
     490             :         {
     491             :             public:
     492             :                 int maxverts, endvert, lastvert, availverts;
     493           0 :                 stainbuffer() : maxverts(0), endvert(0), lastvert(0), availverts(0), verts(nullptr), startvert(0), vbo(0), dirty(false)
     494           0 :                 {}
     495             : 
     496           0 :                 ~stainbuffer()
     497             :                 {
     498           0 :                     delete[] verts;
     499           0 :                 }
     500             : 
     501           0 :                 void init(int tris)
     502             :                 {
     503           0 :                     if(verts)
     504             :                     {
     505           0 :                         delete[] verts;
     506           0 :                         verts = nullptr;
     507           0 :                         maxverts = startvert = endvert = lastvert = availverts = 0;
     508             :                     }
     509           0 :                     if(tris)
     510             :                     {
     511           0 :                         maxverts = tris*3 + 3;
     512           0 :                         availverts = maxverts - 3;
     513           0 :                         verts = new stainvert[maxverts];
     514             :                     }
     515           0 :                 }
     516             : 
     517           0 :                 void cleanup()
     518             :                 {
     519           0 :                     if(vbo)
     520             :                     {
     521           0 :                         glDeleteBuffers(1, &vbo);
     522           0 :                         vbo = 0;
     523             :                     }
     524           0 :                 }
     525             : 
     526           0 :                 void clear()
     527             :                 {
     528           0 :                     startvert = endvert = lastvert = 0;
     529           0 :                     availverts = std::max(maxverts - 3, 0);
     530           0 :                     dirty = true;
     531           0 :                 }
     532             : 
     533           0 :                 int freestain(const staininfo &d)
     534             :                 {
     535           0 :                     int removed = d.endvert < d.startvert ? maxverts - (d.startvert - d.endvert) : d.endvert - d.startvert;
     536           0 :                     startvert = d.endvert;
     537           0 :                     if(startvert==endvert)
     538             :                     {
     539           0 :                         startvert = endvert = lastvert = 0;
     540             :                     }
     541           0 :                     availverts += removed;
     542           0 :                     return removed;
     543             :                 }
     544             : 
     545           0 :                 void clearstains(const staininfo &d)
     546             :                 {
     547           0 :                     startvert = d.endvert;
     548           0 :                     availverts = endvert < startvert ? startvert - endvert - 3 : maxverts - 3 - (endvert - startvert);
     549           0 :                     dirty = true;
     550           0 :                 }
     551             : 
     552           0 :                 bool faded(const staininfo &d) const
     553             :                 {
     554           0 :                     return verts[d.startvert].color.a < 255;
     555             :                 }
     556             : 
     557           0 :                 void fadestain(const staininfo &d, const vec4<uchar> &color)
     558             :                 {
     559           0 :                     stainvert *vert = &verts[d.startvert];
     560           0 :                     const stainvert *end = &verts[d.endvert < d.startvert ? maxverts : d.endvert];
     561           0 :                     while(vert < end)
     562             :                     {
     563           0 :                         vert->color = color;
     564           0 :                         vert++;
     565             :                     }
     566           0 :                     if(d.endvert < d.startvert)
     567             :                     {
     568           0 :                         vert = verts;
     569           0 :                         end = &verts[d.endvert];
     570           0 :                         while(vert < end)
     571             :                         {
     572           0 :                             vert->color = color;
     573           0 :                             vert++;
     574             :                         }
     575             :                     }
     576           0 :                     dirty = true;
     577           0 :                 }
     578             : 
     579           0 :                 void render()
     580             :                 {
     581           0 :                     if(startvert == endvert)
     582             :                     {
     583           0 :                         return;
     584             :                     }
     585           0 :                     if(!vbo)
     586             :                     {
     587           0 :                         glGenBuffers(1, &vbo);
     588           0 :                         dirty = true;
     589             :                     }
     590           0 :                     gle::bindvbo(vbo);
     591           0 :                     int count = endvert < startvert ? maxverts - startvert : endvert - startvert;
     592           0 :                     if(dirty)
     593             :                     {
     594           0 :                         glBufferData(GL_ARRAY_BUFFER, maxverts*sizeof(stainvert), nullptr, GL_STREAM_DRAW);
     595           0 :                         glBufferSubData(GL_ARRAY_BUFFER, 0, count*sizeof(stainvert), &verts[startvert]);
     596           0 :                         if(endvert < startvert)
     597             :                         {
     598           0 :                             glBufferSubData(GL_ARRAY_BUFFER, count*sizeof(stainvert), endvert*sizeof(stainvert), verts);
     599           0 :                             count += endvert;
     600             :                         }
     601           0 :                         dirty = false;
     602             :                     }
     603           0 :                     else if(endvert < startvert)
     604             :                     {
     605           0 :                         count += endvert;
     606             :                     }
     607             :                     //note: using -> on address `0` aka nullptr is undefined behavior
     608             :                     //this allows passing the location of the fields' position in the object to opengl
     609           0 :                     const stainvert *ptr = 0;
     610           0 :                     gle::vertexpointer(sizeof(stainvert), ptr->pos.v);
     611           0 :                     gle::texcoord0pointer(sizeof(stainvert), ptr->tc.v);
     612           0 :                     gle::colorpointer(sizeof(stainvert), ptr->color.v);
     613             : 
     614           0 :                     glDrawArrays(GL_TRIANGLES, 0, count);
     615           0 :                     xtravertsva += count;
     616             :                 }
     617             : 
     618           0 :                 stainvert *addtri()
     619             :                 {
     620           0 :                     stainvert *tri = &verts[endvert];
     621           0 :                     availverts -= 3;
     622           0 :                     endvert += 3;
     623           0 :                     if(endvert >= maxverts)
     624             :                     {
     625           0 :                         endvert = 0;
     626             :                     }
     627           0 :                     return tri;
     628             :                 }
     629             : 
     630           0 :                 void addstain()
     631             :                 {
     632           0 :                     dirty = true;
     633           0 :                 }
     634             : 
     635           0 :                 bool hasverts() const
     636             :                 {
     637           0 :                     return startvert != endvert;
     638             :                 }
     639             : 
     640           0 :                 int nextverts() const
     641             :                 {
     642           0 :                     return endvert < lastvert ? endvert + maxverts - lastvert : endvert - lastvert;
     643             :                 }
     644             : 
     645           0 :                 int totaltris() const
     646             :                 {
     647           0 :                     return (maxverts - 3 - availverts)/3;
     648             :                 }
     649             :             private:
     650             :                 stainvert *verts;
     651             :                 int startvert;
     652             :                 GLuint vbo;
     653             :                 bool dirty;
     654             : 
     655             :                 //debug functions, not used by any of the code
     656             :                 int totalverts() const
     657             :                 {
     658             :                     return endvert < startvert ? maxverts - (startvert - endvert) : endvert - startvert;
     659             :                 }
     660             :         };
     661             : 
     662             :         std::array<stainbuffer, StainBuffer_Number> verts;
     663             : 
     664             :         const char *texname;
     665             : 
     666           0 :         staininfo &newstain()
     667             :         {
     668           0 :             staininfo &d = stains[endstain];
     669           0 :             int next = endstain + 1;
     670           0 :             if(next>=maxstains)
     671             :             {
     672           0 :                 next = 0;
     673             :             }
     674           0 :             if(next==startstain)
     675             :             {
     676           0 :                 freestain();
     677             :             }
     678           0 :             endstain = next;
     679           0 :             return d;
     680             :         }
     681             : 
     682           0 :         bool faded(const staininfo &d) const
     683             :         {
     684           0 :             return verts[d.owner].faded(d);
     685             :         }
     686             : 
     687           0 :         void fadestain(const staininfo &d, uchar alpha)
     688             :         {
     689           0 :             bvec color = d.color;
     690           0 :             if(flags&(StainFlag_Overbright|StainFlag_Glow|StainFlag_InvMod))
     691             :             {
     692           0 :                 color.scale(alpha, 255);
     693             :             }
     694           0 :             verts[d.owner].fadestain(d, vec4<uchar>(color, alpha));
     695           0 :         }
     696             : 
     697           0 :         int freestain()
     698             :         {
     699           0 :             if(startstain==endstain)
     700             :             {
     701           0 :                 return 0;
     702             :             }
     703           0 :             staininfo &d = stains[startstain];
     704           0 :             startstain++;
     705           0 :             if(startstain >= maxstains)
     706             :             {
     707           0 :                 startstain = 0;
     708             :             }
     709           0 :             return verts[d.owner].freestain(d);
     710             :         }
     711             : 
     712           0 :         void findmaterials(vtxarray *va)
     713             :         {
     714           0 :             int matsurfs = va->matsurfs;
     715           0 :             for(int i = 0; i < matsurfs; ++i)
     716             :             {
     717           0 :                 materialsurface &m = va->matbuf[i];
     718           0 :                 if(!IS_CLIPPED(m.material&MatFlag_Volume))
     719             :                 {
     720           0 :                     i += m.skip;
     721           0 :                     continue;
     722             :                 }
     723           0 :                 int dim = DIMENSION(m.orient),
     724           0 :                     dc = DIM_COORD(m.orient);
     725           0 :                 if(dc ? stainnormal[dim] <= 0 : stainnormal[dim] >= 0)
     726             :                 {
     727           0 :                     i += m.skip;
     728           0 :                     continue;
     729             :                 }
     730           0 :                 int c = C[dim],
     731           0 :                     r = R[dim];
     732             :                 for(;;)
     733             :                 {
     734           0 :                     const materialsurface &m = va->matbuf[i];
     735           0 :                     if(m.o[dim] >= bbmin[dim] && m.o[dim] <= bbmax[dim] &&
     736           0 :                        m.o[c] + m.csize >= bbmin[c] && m.o[c] <= bbmax[c] &&
     737           0 :                        m.o[r] + m.rsize >= bbmin[r] && m.o[r] <= bbmax[r])
     738             :                     {
     739             :                         static cube dummy;
     740           0 :                         gentris(dummy, m.orient, m.o, std::max(m.csize, m.rsize), &m);
     741             :                     }
     742           0 :                     if(i+1 >= matsurfs)
     743             :                     {
     744           0 :                         break;
     745             :                     }
     746           0 :                     const materialsurface &n = va->matbuf[i+1];
     747           0 :                     if(n.material != m.material || n.orient != m.orient)
     748             :                     {
     749             :                         break;
     750             :                     }
     751           0 :                     i++;
     752           0 :                 }
     753             :             }
     754           0 :         }
     755             : 
     756           0 :         void findescaped(const std::array<cube, 8> &c, const ivec &o, int size, int escaped)
     757             :         {
     758           0 :             for(int i = 0; i < 8; ++i)
     759             :             {
     760           0 :                 const cube &cu = c[i];
     761           0 :                 if(escaped&(1<<i))
     762             :                 {
     763           0 :                     ivec co(i, o, size);
     764           0 :                     if(cu.children)
     765             :                     {
     766           0 :                         findescaped(*cu.children, co, size>>1, cu.escaped);
     767             :                     }
     768             :                     else
     769             :                     {
     770           0 :                         int vismask = cu.merged;
     771           0 :                         if(vismask)
     772             :                         {
     773           0 :                             for(int j = 0; j < 6; ++j)
     774             :                             {
     775           0 :                                 if(vismask&(1<<j))
     776             :                                 {
     777           0 :                                     gentris(cu, j, co, size);
     778             :                                 }
     779             :                             }
     780             :                         }
     781             :                     }
     782             :                 }
     783             :             }
     784           0 :         }
     785             : 
     786           0 :         void gentris(const std::array<cube, 8> &c, const ivec &o, int size, int escaped = 0)
     787             :         {
     788           0 :             int overlap = octaboxoverlap(o, size, bbmin, bbmax);
     789           0 :             for(int i = 0; i < 8; ++i)
     790             :             {
     791           0 :                 const cube &cu = c[i];
     792           0 :                 if(overlap&(1<<i))
     793             :                 {
     794           0 :                     ivec co(i, o, size);
     795           0 :                     if(cu.ext)
     796             :                     {
     797           0 :                         if(cu.ext->va && cu.ext->va->matsurfs)
     798             :                         {
     799           0 :                             findmaterials(cu.ext->va);
     800             :                         }
     801           0 :                         if(cu.ext->ents && cu.ext->ents->mapmodels.size())
     802             :                         {
     803           0 :                             genmmtris(*cu.ext->ents);
     804             :                         }
     805             :                     }
     806           0 :                     if(cu.children)
     807             :                     {
     808           0 :                         gentris(*cu.children, co, size>>1, cu.escaped);
     809             :                     }
     810             :                     else
     811             :                     {
     812           0 :                         int vismask = cu.visible; //visibility mask
     813           0 :                         if(vismask&0xC0)
     814             :                         {
     815           0 :                             if(vismask&0x80)
     816             :                             {
     817           0 :                                 for(int j = 0; j < 6; ++j)
     818             :                                 {
     819           0 :                                     gentris(cu, j, co, size, nullptr, vismask);
     820             :                                 }
     821             :                             }
     822             :                             else
     823             :                             {
     824           0 :                                 for(int j = 0; j < 6; ++j)
     825             :                                 {
     826           0 :                                     if(vismask&(1<<j))
     827             :                                     {
     828           0 :                                         gentris(cu, j, co, size);
     829             :                                     }
     830             :                                 }
     831             :                             }
     832             :                         }
     833             :                     }
     834             :                 }
     835           0 :                 else if(escaped&(1<<i))
     836             :                 {
     837           0 :                     ivec co(i, o, size);
     838           0 :                     if(cu.children)
     839             :                     {
     840           0 :                         findescaped(*cu.children, co, size>>1, cu.escaped);
     841             :                     }
     842             :                     else
     843             :                     {
     844           0 :                         int vismask = cu.merged; //visibility mask
     845           0 :                         if(vismask)
     846             :                         {
     847           0 :                             for(int j = 0; j < 6; ++j)
     848             :                             {
     849           0 :                                 if(vismask&(1<<j))
     850             :                                 {
     851           0 :                                     gentris(cu, j, co, size);
     852             :                                 }
     853             :                             }
     854             :                         }
     855             :                     }
     856             :                 }
     857             :             }
     858           0 :         }
     859             : 
     860           0 :         void genmmtris(const octaentities &oe)
     861             :         {
     862           0 :             const std::vector<extentity *> &ents = entities::getents();
     863           0 :             for(uint i = 0; i < oe.mapmodels.size(); i++)
     864             :             {
     865           0 :                 const extentity &e = *ents[oe.mapmodels[i]];
     866           0 :                 model *m = loadmapmodel(e.attr1);
     867           0 :                 if(!m)
     868             :                 {
     869           0 :                     continue;
     870             :                 }
     871           0 :                 vec center, radius;
     872           0 :                 float rejectradius = m->collisionbox(center, radius),
     873           0 :                       scale = e.attr5 > 0 ? e.attr5/100.0f : 1;
     874           0 :                 center.mul(scale);
     875           0 :                 if(staincenter.reject(vec(e.o).add(center), stainradius + rejectradius*scale))
     876             :                 {
     877           0 :                     continue;
     878             :                 }
     879           0 :                 if(m->animated() || (!m->bih && !m->setBIH()))
     880             :                 {
     881           0 :                     continue;
     882             :                 }
     883           0 :                 int yaw = e.attr2,
     884           0 :                     pitch = e.attr3,
     885           0 :                     roll = e.attr4;
     886           0 :                 std::vector<std::array<vec, 3>> tris;
     887           0 :                 m->bih->genstaintris(tris, staincenter, stainradius, e.o, yaw, pitch, roll, scale);
     888           0 :                 for(const std::array<vec, 3> &t : tris)
     889             :                 {
     890           0 :                     genmmtri(t);
     891             :                 }
     892           0 :             }
     893           0 :         }
     894             : 
     895           0 :         void gentris(const cube &cu, int orient, const ivec &o, int size, const materialsurface *mat = nullptr, int vismask = 0)
     896             :         {
     897           0 :             vec pos[Face_MaxVerts+4];
     898           0 :             int numverts = 0,
     899           0 :                 numplanes = 1;
     900           0 :             vec planes[2];
     901           0 :             if(mat)
     902             :             {
     903           0 :                 planes[0] = vec(0, 0, 0);
     904           0 :                 switch(orient)
     905             :                 {
     906             :                 //want to define GENFACEORIENT and GENFACEVERT to pass the appropriate code to GENFACEVERTS
     907             :                 //GENFACEVERTS has different GENFACEORIENT and GENFACEVERT for many different calls in other files
     908             :                 #define GENFACEORIENT(orient, v0, v1, v2, v3) \
     909             :                     case orient: \
     910             :                         planes[0][DIMENSION(orient)] = DIM_COORD(orient) ? 1 : -1; \
     911             :                         v0 v1 v2 v3 \
     912             :                         break;
     913             :                 #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
     914             :                         pos[numverts++] = vec(x xv, y yv, z zv);
     915           0 :                     GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
     916             :                 #undef GENFACEORIENT
     917             :                 #undef GENFACEVERT
     918             :                 }
     919             :             }
     920           0 :             else if(cu.texture[orient] == Default_Sky)
     921             :             {
     922           0 :                 return;
     923             :             }
     924           0 :             else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&Face_MaxVerts))
     925             :             {
     926           0 :                 const vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
     927           0 :                 ivec vo = ivec(o).mask(~0xFFF).shl(3);
     928           0 :                 for(int j = 0; j < numverts; ++j)
     929             :                 {
     930           0 :                     pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
     931             :                 }
     932           0 :                 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
     933           0 :                 if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size))
     934             :                 {
     935           0 :                     planes[1].cross(pos[0], pos[2], pos[3]).normalize();
     936           0 :                     numplanes++;
     937             :                 }
     938             :             }
     939           0 :             else if(cu.merged&(1<<orient))
     940             :             {
     941           0 :                 return;
     942             :             }
     943           0 :             else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, Mat_Air, (cu.material&Mat_Alpha)^Mat_Alpha, Mat_Alpha)))
     944             :             {
     945           0 :                 std::array<ivec, 4> v;
     946           0 :                 genfaceverts(cu, orient, v);
     947           0 :                 int vis = 3,
     948           0 :                     convex = faceconvexity(v, vis),
     949           0 :                     order = convex < 0 ? 1 : 0;
     950           0 :                 vec vo(o);
     951           0 :                 pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
     952           0 :                 if(vis&1)
     953             :                 {
     954           0 :                     pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
     955             :                 }
     956           0 :                 pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
     957           0 :                 if(vis&2)
     958             :                 {
     959           0 :                     pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
     960             :                 }
     961           0 :                 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
     962           0 :                 if(convex)
     963             :                 {
     964           0 :                     planes[1].cross(pos[0], pos[2], pos[3]).normalize();
     965           0 :                     numplanes++;
     966             :                 }
     967             :             }
     968             :             else
     969             :             {
     970           0 :                 return;
     971             :             }
     972             : 
     973           0 :             stainbuffer &buf = verts[mat || cu.material&Mat_Alpha ? StainBuffer_Transparent : StainBuffer_Opaque];
     974           0 :             for(int l = 0; l < numplanes; ++l) //note this is a loop l (level 4)
     975             :             {
     976           0 :                 const vec &n = planes[l];
     977           0 :                 float facing = n.dot(stainnormal);
     978           0 :                 if(facing <= 0)
     979             :                 {
     980           0 :                     continue;
     981             :                 }
     982           0 :                 vec p = vec(pos[0]).sub(staincenter);
     983             :                 // travel back along plane normal from the stain center
     984           0 :                 float dist = n.dot(p);
     985           0 :                 if(std::fabs(dist) > stainradius)
     986             :                 {
     987           0 :                     continue;
     988             :                 }
     989           0 :                 vec pcenter = vec(n).mul(dist).add(staincenter);
     990           0 :                 vec ft, fb;
     991           0 :                 ft.orthogonal(n);
     992           0 :                 ft.normalize();
     993           0 :                 fb.cross(ft, n);
     994           0 :                 vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
     995           0 :                     pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
     996           0 :                 vec v1[Face_MaxVerts+4],
     997           0 :                     v2[Face_MaxVerts+4];
     998           0 :                 float ptc = pt.dot(pcenter),
     999           0 :                       pbc = pb.dot(pcenter);
    1000             :                 int numv;
    1001           0 :                 if(numplanes >= 2)
    1002             :                 {
    1003           0 :                     if(l)
    1004             :                     {
    1005           0 :                         pos[1] = pos[2];
    1006           0 :                         pos[2] = pos[3];
    1007             :                     }
    1008           0 :                     numv = polyclip(pos, 3, pt, ptc - stainradius, ptc + stainradius, v1);
    1009           0 :                     if(numv<3)
    1010             :                     {
    1011           0 :                         continue;
    1012             :                     }
    1013             :                 }
    1014             :                 else
    1015             :                 {
    1016           0 :                     numv = polyclip(pos, numverts, pt, ptc - stainradius, ptc + stainradius, v1);
    1017           0 :                     if(numv<3)
    1018             :                     {
    1019           0 :                         continue;
    1020             :                     }
    1021             :                 }
    1022           0 :                 numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
    1023           0 :                 if(numv<3)
    1024             :                 {
    1025           0 :                     continue;
    1026             :                 }
    1027           0 :                 float tsz = flags&StainFlag_Rnd4 ? 0.5f : 1.0f,
    1028           0 :                       scale = tsz*0.5f/stainradius,
    1029           0 :                       tu = stainu + tsz*0.5f - ptc*scale,
    1030           0 :                       tv = stainv + tsz*0.5f - pbc*scale;
    1031           0 :                 pt.mul(scale); pb.mul(scale);
    1032           0 :                 stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
    1033           0 :                           dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
    1034           0 :                 int totalverts = 3*(numv-2);
    1035           0 :                 if(totalverts > buf.maxverts-3)
    1036             :                 {
    1037           0 :                     return;
    1038             :                 }
    1039           0 :                 while(buf.availverts < totalverts)
    1040             :                 {
    1041           0 :                     if(!freestain())
    1042             :                     {
    1043           0 :                         return;
    1044             :                     }
    1045             :                 }
    1046           0 :                 for(int k = 0; k < numv-2; ++k)
    1047             :                 {
    1048           0 :                     stainvert *tri = buf.addtri();
    1049           0 :                     tri[0] = dv1;
    1050           0 :                     tri[1] = dv2;
    1051           0 :                     dv2.pos = v2[k+2];
    1052           0 :                     dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
    1053           0 :                     tri[2] = dv2;
    1054             :                 }
    1055             :             }
    1056             :         }
    1057             : };
    1058             : 
    1059             : std::vector<stainrenderer> stains;
    1060             : 
    1061             : /* initstains: sets up each entry in the stains global variable array using init() method
    1062             :  * and then preloads them
    1063             :  *
    1064             :  * fails to do anything if initing is set (early game loading time)
    1065             :  */
    1066           0 : void initstains()
    1067             : {
    1068           0 :     if(initing)
    1069             :     {
    1070           0 :         return;
    1071             :     }
    1072           0 :     stains.emplace_back("<grey>media/particle/blood.png", stainrenderer::StainFlag_Rnd4|stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_InvMod);
    1073           0 :     stains.emplace_back("<grey>media/particle/pulse_scorch.png", stainrenderer::StainFlag_Rotate, 500);
    1074           0 :     stains.emplace_back("<grey>media/particle/rail_hole.png", stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Overbright);
    1075           0 :     stains.emplace_back("<grey>media/particle/pulse_glow.png", stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Glow|stainrenderer::StainFlag_Saturate, 250, 1500, 250);
    1076           0 :     stains.emplace_back("<grey>media/particle/rail_glow.png",  stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Glow|stainrenderer::StainFlag_Saturate, 100, 1100, 100);
    1077           0 :     for(stainrenderer &i : stains)
    1078             :     {
    1079           0 :         i.init(maxstaintris);
    1080             :     }
    1081           0 :     for(uint i = 0; i < stains.size(); ++i)
    1082             :     {
    1083           0 :         loadprogress = static_cast<float>(i+1)/stains.size();
    1084           0 :         stains[i].preload();
    1085             :     }
    1086           0 :     loadprogress = 0;
    1087             : }
    1088             : 
    1089             : /* clearstains: loops through the stains[] global variable array and runs clearstains for each entry
    1090             :  */
    1091           0 : void clearstains()
    1092             : {
    1093           0 :     for(stainrenderer &i : stains)
    1094             :     {
    1095           0 :         i.clearstains();
    1096             :     }
    1097           0 : }
    1098             : 
    1099             : VARNP(stains, showstains, 0, 1, 1); // toggles rendering stains at all
    1100             : 
    1101           0 : bool renderstains(int sbuf, bool gbuf, int layer)
    1102             : {
    1103           0 :     bool rendered = false;
    1104           0 :     for(stainrenderer& d : stains)
    1105             :     {
    1106           0 :         if(d.usegbuffer() != gbuf)
    1107             :         {
    1108           0 :             continue;
    1109             :         }
    1110           0 :         if(sbuf == StainBuffer_Opaque)
    1111             :         {
    1112           0 :             d.clearfadedstains();
    1113           0 :             d.fadeinstains();
    1114           0 :             d.fadeoutstains();
    1115             :         }
    1116           0 :         if(!showstains || !d.hasstains(sbuf))
    1117             :         {
    1118           0 :             continue;
    1119             :         }
    1120           0 :         if(!rendered)
    1121             :         {
    1122           0 :             rendered = true;
    1123           0 :             stainrenderer::setuprenderstate(sbuf, gbuf, layer);
    1124             :         }
    1125           0 :         d.render(sbuf);
    1126             :     }
    1127           0 :     if(!rendered)
    1128             :     {
    1129           0 :         return false;
    1130             :     }
    1131           0 :     stainrenderer::cleanuprenderstate(sbuf, gbuf, layer);
    1132           0 :     return true;
    1133             : }
    1134             : 
    1135           0 : void cleanupstains()
    1136             : {
    1137           0 :     for(stainrenderer& i : stains)
    1138             :     {
    1139           0 :         i.cleanup();
    1140             :     }
    1141           0 : }
    1142             : 
    1143           0 : void addstain(int type, const vec &center, const vec &surface, float radius, const bvec &color, int info)
    1144             : {
    1145           0 :     static VARP(maxstaindistance, 1, 512, 10000); //distance in cubes before stains stop rendering
    1146           0 :     if(!showstains || type<0 || static_cast<size_t>(type) >= stains.size() || center.dist(camera1->o) - radius > maxstaindistance)
    1147             :     {
    1148           0 :         return;
    1149             :     }
    1150           0 :     stainrenderer &d = stains[type];
    1151           0 :     d.addstain(center, surface, radius, color, info, rootworld);
    1152             : }

Generated by: LCOV version 1.14