LCOV - code coverage report
Current view: top level - engine/render - stain.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.0 % 590 0
Test Date: 2025-05-13 05:24:23 Functions: 0.0 % 46 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)
     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              :                 {
     303              :                     static Shader *overbrightstainshader = nullptr;
     304            0 :                     if(!overbrightstainshader)
     305              :                     {
     306            0 :                         overbrightstainshader = lookupshaderbyname("overbrightstain");
     307              :                     }
     308            0 :                     overbrightstainshader->setvariant(sbuf == StainBuffer_Transparent ? 0 : -1, 0);
     309              :                 }
     310              :             }
     311            0 :             else if(flags&StainFlag_Glow)
     312              :             {
     313            0 :                 glBlendFunc(GL_ONE, GL_ONE);
     314            0 :                 colorscale = ldrscale;
     315            0 :                 if(flags&StainFlag_Saturate)
     316              :                 {
     317            0 :                     colorscale *= 2;
     318              :                 }
     319            0 :                 alphascale = 0;
     320            0 :                 SETSHADER(foggedstain);
     321              :             }
     322            0 :             else if(flags&StainFlag_InvMod)
     323              :             {
     324            0 :                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
     325            0 :                 alphascale = 0;
     326            0 :                 SETSHADER(foggedstain);
     327              :             }
     328              :             else
     329              :             {
     330            0 :                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     331            0 :                 colorscale = ldrscale;
     332            0 :                 if(flags&StainFlag_Saturate)
     333              :                 {
     334            0 :                     colorscale *= 2;
     335              :                 }
     336              :                 {
     337              :                     static Shader *stainshader = nullptr;
     338            0 :                     if(!stainshader)
     339              :                     {
     340            0 :                         stainshader = lookupshaderbyname("stain");
     341              :                     }
     342            0 :                     stainshader->setvariant(sbuf == StainBuffer_Transparent ? 0 : -1, 0);
     343              :                 }
     344              :             }
     345            0 :             LOCALPARAMF(colorscale, colorscale, colorscale, colorscale, alphascale);
     346              : 
     347            0 :             glBindTexture(GL_TEXTURE_2D, tex->id);
     348              : 
     349            0 :             verts[sbuf].render();
     350            0 :         }
     351              : 
     352              : 
     353            0 :         void addstain(const vec &center, const vec &dir, float radius, const bvec &color, int info, const cubeworld &world)
     354              :         {
     355            0 :             if(dir.iszero())
     356              :             {
     357            0 :                 return;
     358              :             }
     359            0 :             bbmin = ivec(center).sub(radius);
     360            0 :             bbmax = ivec(center).add(radius).add(1);
     361              : 
     362            0 :             staincolor = vec4<uchar>(color, 255);
     363            0 :             staincenter = center;
     364            0 :             stainradius = radius;
     365            0 :             stainnormal = dir;
     366              : 
     367            0 :             staintangent = vec(dir.z, -dir.x, dir.y);
     368            0 :             staintangent.project(dir);
     369              : 
     370            0 :             if(flags&StainFlag_Rotate)
     371              :             {
     372            0 :                 staintangent.rotate(sincos360[randomint(360)], dir);
     373              :             }
     374            0 :             staintangent.normalize();
     375            0 :             stainbitangent.cross(staintangent, dir);
     376            0 :             if(flags&StainFlag_Rnd4)
     377              :             {
     378            0 :                 stainu = 0.5f*(info&1);
     379            0 :                 stainv = 0.5f*((info>>1)&1);
     380              :             }
     381              : 
     382            0 :             for(int i = 0; i < StainBuffer_Number; ++i)
     383              :             {
     384            0 :                 verts[i].lastvert = verts[i].endvert;
     385              :             }
     386            0 :             gentris(*world.worldroot, ivec(0, 0, 0), rootworld.mapsize()>>1);
     387            0 :             for(int i = 0; i < StainBuffer_Number; ++i)
     388              :             {
     389            0 :                 stainbuffer &buf = verts[i];
     390            0 :                 if(buf.endvert == buf.lastvert)
     391              :                 {
     392            0 :                     continue;
     393              :                 }
     394            0 :                 if(debugstain)
     395              :                 {
     396            0 :                     int nverts = buf.nextverts();
     397              :                     static const char * const sbufname[StainBuffer_Number] = { "opaque", "transparent", "mapmodel" };
     398            0 :                     conoutf(Console_Debug, "tris = %d, verts = %d, total tris = %d, %s", nverts/3, nverts, buf.totaltris(), sbufname[i]);
     399              :                 }
     400              : 
     401            0 :                 staininfo &d = newstain();
     402            0 :                 d.owner = i;
     403            0 :                 d.color = color;
     404            0 :                 d.millis = lastmillis;
     405            0 :                 d.startvert = buf.lastvert;
     406            0 :                 d.endvert = buf.endvert;
     407            0 :                 buf.addstain();
     408              :             }
     409              :         }
     410              : 
     411            0 :         void genmmtri(const std::array<vec, 3> &v) // gen map model triangles
     412              :         {
     413            0 :             vec n;
     414            0 :             n.cross(v[0], v[1], v[2]).normalize();
     415            0 :             float facing = n.dot(stainnormal);
     416            0 :             if(facing <= 0)
     417              :             {
     418            0 :                 return;
     419              :             }
     420            0 :             vec p = vec(v[0]).sub(staincenter);
     421            0 :             float dist = n.dot(p);
     422            0 :             if(std::fabs(dist) > stainradius)
     423              :             {
     424            0 :                 return;
     425              :             }
     426            0 :             vec pcenter = vec(n).mul(dist).add(staincenter);
     427            0 :             vec ft, fb;
     428            0 :             ft.orthogonal(n);
     429            0 :             ft.normalize();
     430            0 :             fb.cross(ft, n);
     431            0 :             vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
     432            0 :                 pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
     433            0 :             vec v1[3+4],
     434            0 :                 v2[3+4];
     435            0 :             float ptc = pt.dot(pcenter),
     436            0 :                   pbc = pb.dot(pcenter);
     437            0 :             int numv = polyclip(v.data(), v.size(), pt, ptc - stainradius, ptc + stainradius, v1);
     438            0 :             if(numv<3) //check with v1
     439              :             {
     440            0 :                 return;
     441              :             }
     442            0 :             numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
     443            0 :             if(numv<3) //check again with v2
     444              :             {
     445            0 :                 return;
     446              :             }
     447            0 :             float tsz = flags&StainFlag_Rnd4 ? 0.5f : 1.0f,
     448            0 :                   scale = tsz*0.5f/stainradius,
     449            0 :                   tu = stainu + tsz*0.5f - ptc*scale,
     450            0 :                   tv = stainv + tsz*0.5f - pbc*scale;
     451            0 :             pt.mul(scale); pb.mul(scale);
     452            0 :             stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
     453            0 :                       dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
     454            0 :             int totalverts = 3*(numv-2);
     455            0 :             stainbuffer &buf = verts[StainBuffer_Mapmodel];
     456            0 :             if(totalverts > buf.maxverts-3)
     457              :             {
     458            0 :                 return;
     459              :             }
     460            0 :             while(buf.availverts < totalverts)
     461              :             {
     462            0 :                 if(!freestain())
     463              :                 {
     464            0 :                     return;
     465              :                 }
     466              :             }
     467            0 :             for(int k = 0; k < numv-2; ++k)
     468              :             {
     469            0 :                 stainvert *tri = buf.addtri();
     470            0 :                 tri[0] = dv1;
     471            0 :                 tri[1] = dv2;
     472            0 :                 dv2.pos = v2[k+2];
     473            0 :                 dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
     474            0 :                 tri[2] = dv2;
     475              :             }
     476              :         }
     477              : 
     478              :     private:
     479              :         int flags, fadeintime, fadeouttime, timetolive;
     480              :         int maxstains, startstain, endstain;
     481              : 
     482              :         ivec bbmin, bbmax;
     483              :         vec staincenter, stainnormal, staintangent, stainbitangent;
     484              :         float stainradius, stainu, stainv;
     485              :         vec4<uchar> staincolor;
     486              :         Texture *tex;
     487              : 
     488              :         struct stainvert
     489              :         {
     490              :             vec pos;
     491              :             vec4<uchar> color;
     492              :             vec2 tc;
     493              :         };
     494              : 
     495              :         struct staininfo
     496              :         {
     497              :             int millis;
     498              :             bvec color;
     499              :             uchar owner;
     500              :             ushort startvert, endvert;
     501              :         };
     502              :         staininfo *stains;
     503              : 
     504              :         class stainbuffer
     505              :         {
     506              :             public:
     507              :                 int maxverts, endvert, lastvert, availverts;
     508            0 :                 stainbuffer() : maxverts(0), endvert(0), lastvert(0), availverts(0), verts(nullptr), startvert(0), vbo(0), dirty(false)
     509            0 :                 {}
     510              : 
     511            0 :                 ~stainbuffer()
     512              :                 {
     513            0 :                     delete[] verts;
     514            0 :                 }
     515              : 
     516            0 :                 void init(int tris)
     517              :                 {
     518            0 :                     if(verts)
     519              :                     {
     520            0 :                         delete[] verts;
     521            0 :                         verts = nullptr;
     522            0 :                         maxverts = startvert = endvert = lastvert = availverts = 0;
     523              :                     }
     524            0 :                     if(tris)
     525              :                     {
     526            0 :                         maxverts = tris*3 + 3;
     527            0 :                         availverts = maxverts - 3;
     528            0 :                         verts = new stainvert[maxverts];
     529              :                     }
     530            0 :                 }
     531              : 
     532            0 :                 void cleanup()
     533              :                 {
     534            0 :                     if(vbo)
     535              :                     {
     536            0 :                         glDeleteBuffers(1, &vbo);
     537            0 :                         vbo = 0;
     538              :                     }
     539            0 :                 }
     540              : 
     541            0 :                 void clear()
     542              :                 {
     543            0 :                     startvert = endvert = lastvert = 0;
     544            0 :                     availverts = std::max(maxverts - 3, 0);
     545            0 :                     dirty = true;
     546            0 :                 }
     547              : 
     548            0 :                 int freestain(const staininfo &d)
     549              :                 {
     550            0 :                     int removed = d.endvert < d.startvert ? maxverts - (d.startvert - d.endvert) : d.endvert - d.startvert;
     551            0 :                     startvert = d.endvert;
     552            0 :                     if(startvert==endvert)
     553              :                     {
     554            0 :                         startvert = endvert = lastvert = 0;
     555              :                     }
     556            0 :                     availverts += removed;
     557            0 :                     return removed;
     558              :                 }
     559              : 
     560            0 :                 void clearstains(const staininfo &d)
     561              :                 {
     562            0 :                     startvert = d.endvert;
     563            0 :                     availverts = endvert < startvert ? startvert - endvert - 3 : maxverts - 3 - (endvert - startvert);
     564            0 :                     dirty = true;
     565            0 :                 }
     566              : 
     567            0 :                 bool faded(const staininfo &d) const
     568              :                 {
     569            0 :                     return verts[d.startvert].color.a() < 255;
     570              :                 }
     571              : 
     572            0 :                 void fadestain(const staininfo &d, const vec4<uchar> &color)
     573              :                 {
     574            0 :                     stainvert *vert = &verts[d.startvert];
     575            0 :                     const stainvert *end = &verts[d.endvert < d.startvert ? maxverts : d.endvert];
     576            0 :                     while(vert < end)
     577              :                     {
     578            0 :                         vert->color = color;
     579            0 :                         vert++;
     580              :                     }
     581            0 :                     if(d.endvert < d.startvert)
     582              :                     {
     583            0 :                         vert = verts;
     584            0 :                         end = &verts[d.endvert];
     585            0 :                         while(vert < end)
     586              :                         {
     587            0 :                             vert->color = color;
     588            0 :                             vert++;
     589              :                         }
     590              :                     }
     591            0 :                     dirty = true;
     592            0 :                 }
     593              : 
     594            0 :                 void render()
     595              :                 {
     596            0 :                     if(startvert == endvert)
     597              :                     {
     598            0 :                         return;
     599              :                     }
     600            0 :                     if(!vbo)
     601              :                     {
     602            0 :                         glGenBuffers(1, &vbo);
     603            0 :                         dirty = true;
     604              :                     }
     605            0 :                     gle::bindvbo(vbo);
     606            0 :                     int count = endvert < startvert ? maxverts - startvert : endvert - startvert;
     607            0 :                     if(dirty)
     608              :                     {
     609            0 :                         glBufferData(GL_ARRAY_BUFFER, maxverts*sizeof(stainvert), nullptr, GL_STREAM_DRAW);
     610            0 :                         glBufferSubData(GL_ARRAY_BUFFER, 0, count*sizeof(stainvert), &verts[startvert]);
     611            0 :                         if(endvert < startvert)
     612              :                         {
     613            0 :                             glBufferSubData(GL_ARRAY_BUFFER, count*sizeof(stainvert), endvert*sizeof(stainvert), verts);
     614            0 :                             count += endvert;
     615              :                         }
     616            0 :                         dirty = false;
     617              :                     }
     618            0 :                     else if(endvert < startvert)
     619              :                     {
     620            0 :                         count += endvert;
     621              :                     }
     622              :                     //note: using -> on address `0` aka nullptr is undefined behavior
     623              :                     //this allows passing the location of the fields' position in the object to opengl
     624            0 :                     const stainvert *ptr = 0;
     625            0 :                     gle::vertexpointer(sizeof(stainvert), ptr->pos.data());
     626            0 :                     gle::texcoord0pointer(sizeof(stainvert), ptr->tc.data());
     627            0 :                     gle::colorpointer(sizeof(stainvert), ptr->color.data());
     628              : 
     629            0 :                     glDrawArrays(GL_TRIANGLES, 0, count);
     630            0 :                     xtravertsva += count;
     631              :                 }
     632              : 
     633            0 :                 stainvert *addtri()
     634              :                 {
     635            0 :                     stainvert *tri = &verts[endvert];
     636            0 :                     availverts -= 3;
     637            0 :                     endvert += 3;
     638            0 :                     if(endvert >= maxverts)
     639              :                     {
     640            0 :                         endvert = 0;
     641              :                     }
     642            0 :                     return tri;
     643              :                 }
     644              : 
     645            0 :                 void addstain()
     646              :                 {
     647            0 :                     dirty = true;
     648            0 :                 }
     649              : 
     650            0 :                 bool hasverts() const
     651              :                 {
     652            0 :                     return startvert != endvert;
     653              :                 }
     654              : 
     655            0 :                 int nextverts() const
     656              :                 {
     657            0 :                     return endvert < lastvert ? endvert + maxverts - lastvert : endvert - lastvert;
     658              :                 }
     659              : 
     660            0 :                 int totaltris() const
     661              :                 {
     662            0 :                     return (maxverts - 3 - availverts)/3;
     663              :                 }
     664              :             private:
     665              :                 stainvert *verts;
     666              :                 int startvert;
     667              :                 GLuint vbo;
     668              :                 bool dirty;
     669              : 
     670              :                 //debug functions, not used by any of the code
     671              :                 int totalverts() const
     672              :                 {
     673              :                     return endvert < startvert ? maxverts - (startvert - endvert) : endvert - startvert;
     674              :                 }
     675              :         };
     676              : 
     677              :         std::array<stainbuffer, StainBuffer_Number> verts;
     678              : 
     679              :         const char *texname;
     680              : 
     681            0 :         staininfo &newstain()
     682              :         {
     683            0 :             staininfo &d = stains[endstain];
     684            0 :             int next = endstain + 1;
     685            0 :             if(next>=maxstains)
     686              :             {
     687            0 :                 next = 0;
     688              :             }
     689            0 :             if(next==startstain)
     690              :             {
     691            0 :                 freestain();
     692              :             }
     693            0 :             endstain = next;
     694            0 :             return d;
     695              :         }
     696              : 
     697            0 :         bool faded(const staininfo &d) const
     698              :         {
     699            0 :             return verts[d.owner].faded(d);
     700              :         }
     701              : 
     702            0 :         void fadestain(const staininfo &d, uchar alpha)
     703              :         {
     704            0 :             bvec color = d.color;
     705            0 :             if(flags&(StainFlag_Overbright|StainFlag_Glow|StainFlag_InvMod))
     706              :             {
     707            0 :                 color.scale(alpha, 255);
     708              :             }
     709            0 :             verts[d.owner].fadestain(d, vec4<uchar>(color, alpha));
     710            0 :         }
     711              : 
     712            0 :         int freestain()
     713              :         {
     714            0 :             if(startstain==endstain)
     715              :             {
     716            0 :                 return 0;
     717              :             }
     718            0 :             staininfo &d = stains[startstain];
     719            0 :             startstain++;
     720            0 :             if(startstain >= maxstains)
     721              :             {
     722            0 :                 startstain = 0;
     723              :             }
     724            0 :             return verts[d.owner].freestain(d);
     725              :         }
     726              : 
     727            0 :         void findmaterials(vtxarray *va)
     728              :         {
     729            0 :             int matsurfs = va->matsurfs;
     730            0 :             for(int i = 0; i < matsurfs; ++i)
     731              :             {
     732            0 :                 materialsurface &m = va->matbuf[i];
     733            0 :                 if(!IS_CLIPPED(m.material&MatFlag_Volume))
     734              :                 {
     735            0 :                     i += m.skip;
     736            0 :                     continue;
     737              :                 }
     738            0 :                 int dim = DIMENSION(m.orient),
     739            0 :                     dc = DIM_COORD(m.orient);
     740            0 :                 if(dc ? stainnormal[dim] <= 0 : stainnormal[dim] >= 0)
     741              :                 {
     742            0 :                     i += m.skip;
     743            0 :                     continue;
     744              :                 }
     745            0 :                 int c = C[dim],
     746            0 :                     r = R[dim];
     747              :                 for(;;)
     748              :                 {
     749            0 :                     const materialsurface &m = va->matbuf[i];
     750            0 :                     if(m.o[dim] >= bbmin[dim] && m.o[dim] <= bbmax[dim] &&
     751            0 :                        m.o[c] + m.csize >= bbmin[c] && m.o[c] <= bbmax[c] &&
     752            0 :                        m.o[r] + m.rsize >= bbmin[r] && m.o[r] <= bbmax[r])
     753              :                     {
     754              :                         static cube dummy;
     755            0 :                         gentris(dummy, m.orient, m.o, std::max(m.csize, m.rsize), &m);
     756              :                     }
     757            0 :                     if(i+1 >= matsurfs)
     758              :                     {
     759            0 :                         break;
     760              :                     }
     761            0 :                     const materialsurface &n = va->matbuf[i+1];
     762            0 :                     if(n.material != m.material || n.orient != m.orient)
     763              :                     {
     764              :                         break;
     765              :                     }
     766            0 :                     i++;
     767            0 :                 }
     768              :             }
     769            0 :         }
     770              : 
     771            0 :         void findescaped(const std::array<cube, 8> &c, const ivec &o, int size, int escaped)
     772              :         {
     773            0 :             for(int i = 0; i < 8; ++i)
     774              :             {
     775            0 :                 const cube &cu = c[i];
     776            0 :                 if(escaped&(1<<i))
     777              :                 {
     778            0 :                     ivec co(i, o, size);
     779            0 :                     if(cu.children)
     780              :                     {
     781            0 :                         findescaped(*cu.children, co, size>>1, cu.escaped);
     782              :                     }
     783              :                     else
     784              :                     {
     785            0 :                         int vismask = cu.merged;
     786            0 :                         if(vismask)
     787              :                         {
     788            0 :                             for(int j = 0; j < 6; ++j)
     789              :                             {
     790            0 :                                 if(vismask&(1<<j))
     791              :                                 {
     792            0 :                                     gentris(cu, j, co, size);
     793              :                                 }
     794              :                             }
     795              :                         }
     796              :                     }
     797              :                 }
     798              :             }
     799            0 :         }
     800              : 
     801            0 :         void gentris(const std::array<cube, 8> &c, const ivec &o, int size, int escaped = 0)
     802              :         {
     803            0 :             int overlap = octaboxoverlap(o, size, bbmin, bbmax);
     804            0 :             for(int i = 0; i < 8; ++i)
     805              :             {
     806            0 :                 const cube &cu = c[i];
     807            0 :                 if(overlap&(1<<i))
     808              :                 {
     809            0 :                     ivec co(i, o, size);
     810            0 :                     if(cu.ext)
     811              :                     {
     812            0 :                         if(cu.ext->va && cu.ext->va->matsurfs)
     813              :                         {
     814            0 :                             findmaterials(cu.ext->va);
     815              :                         }
     816            0 :                         if(cu.ext->ents && cu.ext->ents->mapmodels.size())
     817              :                         {
     818            0 :                             genmmtris(*cu.ext->ents);
     819              :                         }
     820              :                     }
     821            0 :                     if(cu.children)
     822              :                     {
     823            0 :                         gentris(*cu.children, co, size>>1, cu.escaped);
     824              :                     }
     825              :                     else
     826              :                     {
     827            0 :                         int vismask = cu.visible; //visibility mask
     828            0 :                         if(vismask&0xC0)
     829              :                         {
     830            0 :                             if(vismask&0x80)
     831              :                             {
     832            0 :                                 for(int j = 0; j < 6; ++j)
     833              :                                 {
     834            0 :                                     gentris(cu, j, co, size, nullptr, vismask);
     835              :                                 }
     836              :                             }
     837              :                             else
     838              :                             {
     839            0 :                                 for(int j = 0; j < 6; ++j)
     840              :                                 {
     841            0 :                                     if(vismask&(1<<j))
     842              :                                     {
     843            0 :                                         gentris(cu, j, co, size);
     844              :                                     }
     845              :                                 }
     846              :                             }
     847              :                         }
     848              :                     }
     849              :                 }
     850            0 :                 else if(escaped&(1<<i))
     851              :                 {
     852            0 :                     ivec co(i, o, size);
     853            0 :                     if(cu.children)
     854              :                     {
     855            0 :                         findescaped(*cu.children, co, size>>1, cu.escaped);
     856              :                     }
     857              :                     else
     858              :                     {
     859            0 :                         int vismask = cu.merged; //visibility mask
     860            0 :                         if(vismask)
     861              :                         {
     862            0 :                             for(int j = 0; j < 6; ++j)
     863              :                             {
     864            0 :                                 if(vismask&(1<<j))
     865              :                                 {
     866            0 :                                     gentris(cu, j, co, size);
     867              :                                 }
     868              :                             }
     869              :                         }
     870              :                     }
     871              :                 }
     872              :             }
     873            0 :         }
     874              : 
     875            0 :         void genmmtris(const octaentities &oe)
     876              :         {
     877            0 :             const std::vector<extentity *> &ents = entities::getents();
     878            0 :             for(uint i = 0; i < oe.mapmodels.size(); i++)
     879              :             {
     880            0 :                 const extentity &e = *ents[oe.mapmodels[i]];
     881            0 :                 model *m = loadmapmodel(e.attr1);
     882            0 :                 if(!m)
     883              :                 {
     884            0 :                     continue;
     885              :                 }
     886            0 :                 vec center, radius;
     887            0 :                 float rejectradius = m->collisionbox(center, radius),
     888            0 :                       scale = e.attr5 > 0 ? e.attr5/100.0f : 1;
     889            0 :                 center.mul(scale);
     890            0 :                 if(staincenter.reject(vec(e.o).add(center), stainradius + rejectradius*scale))
     891              :                 {
     892            0 :                     continue;
     893              :                 }
     894            0 :                 m->setBIH();
     895            0 :                 if(m->animated())
     896              :                 {
     897            0 :                     continue;
     898              :                 }
     899            0 :                 int yaw = e.attr2,
     900            0 :                     pitch = e.attr3,
     901            0 :                     roll = e.attr4;
     902            0 :                 std::vector<std::array<vec, 3>> tris;
     903            0 :                 m->bih->genstaintris(tris, staincenter, stainradius, e.o, yaw, pitch, roll, scale);
     904            0 :                 for(const std::array<vec, 3> &t : tris)
     905              :                 {
     906            0 :                     genmmtri(t);
     907              :                 }
     908            0 :             }
     909            0 :         }
     910              : 
     911            0 :         void gentris(const cube &cu, int orient, const ivec &o, int size, const materialsurface *mat = nullptr, int vismask = 0)
     912              :         {
     913            0 :             std::array<vec, Face_MaxVerts+4> pos;
     914            0 :             int numverts = 0,
     915            0 :                 numplanes = 1;
     916            0 :             std::array<vec, 2> planes;
     917            0 :             if(mat)
     918              :             {
     919            0 :                 planes[0] = vec(0, 0, 0);
     920            0 :                 switch(orient)
     921              :                 {
     922              :                 //want to define GENFACEORIENT and GENFACEVERT to pass the appropriate code to GENFACEVERTS
     923              :                 //GENFACEVERTS has different GENFACEORIENT and GENFACEVERT for many different calls in other files
     924              :                 #define GENFACEORIENT(orient, v0, v1, v2, v3) \
     925              :                     case orient: \
     926              :                         planes[0][DIMENSION(orient)] = DIM_COORD(orient) ? 1 : -1; \
     927              :                         v0 v1 v2 v3 \
     928              :                         break;
     929              :                 #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
     930              :                         pos[numverts++] = vec(x xv, y yv, z zv);
     931            0 :                     GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
     932              :                 #undef GENFACEORIENT
     933              :                 #undef GENFACEVERT
     934              :                 }
     935              :             }
     936            0 :             else if(cu.texture[orient] == Default_Sky)
     937              :             {
     938            0 :                 return;
     939              :             }
     940            0 :             else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&Face_MaxVerts))
     941              :             {
     942            0 :                 const vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
     943            0 :                 ivec vo = ivec(o).mask(~0xFFF).shl(3);
     944            0 :                 for(int j = 0; j < numverts; ++j)
     945              :                 {
     946            0 :                     pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
     947              :                 }
     948            0 :                 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
     949            0 :                 if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size))
     950              :                 {
     951            0 :                     planes[1].cross(pos[0], pos[2], pos[3]).normalize();
     952            0 :                     numplanes++;
     953              :                 }
     954              :             }
     955            0 :             else if(cu.merged&(1<<orient))
     956              :             {
     957            0 :                 return;
     958              :             }
     959            0 :             else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, Mat_Air, (cu.material&Mat_Alpha)^Mat_Alpha, Mat_Alpha)))
     960              :             {
     961            0 :                 std::array<ivec, 4> v;
     962            0 :                 genfaceverts(cu, orient, v);
     963            0 :                 int vis = 3,
     964            0 :                     convex = faceconvexity(v, vis),
     965            0 :                     order = convex < 0 ? 1 : 0;
     966            0 :                 vec vo(o);
     967            0 :                 pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
     968            0 :                 if(vis&1)
     969              :                 {
     970            0 :                     pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
     971              :                 }
     972            0 :                 pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
     973            0 :                 if(vis&2)
     974              :                 {
     975            0 :                     pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
     976              :                 }
     977            0 :                 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
     978            0 :                 if(convex)
     979              :                 {
     980            0 :                     planes[1].cross(pos[0], pos[2], pos[3]).normalize();
     981            0 :                     numplanes++;
     982              :                 }
     983              :             }
     984              :             else
     985              :             {
     986            0 :                 return;
     987              :             }
     988              : 
     989            0 :             stainbuffer &buf = verts[mat || cu.material&Mat_Alpha ? StainBuffer_Transparent : StainBuffer_Opaque];
     990            0 :             for(int l = 0; l < numplanes; ++l) //note this is a loop l (level 4)
     991              :             {
     992            0 :                 const vec &n = planes[l];
     993            0 :                 float facing = n.dot(stainnormal);
     994            0 :                 if(facing <= 0)
     995              :                 {
     996            0 :                     continue;
     997              :                 }
     998            0 :                 vec p = vec(pos[0]).sub(staincenter);
     999              :                 // travel back along plane normal from the stain center
    1000            0 :                 float dist = n.dot(p);
    1001            0 :                 if(std::fabs(dist) > stainradius)
    1002              :                 {
    1003            0 :                     continue;
    1004              :                 }
    1005            0 :                 vec pcenter = vec(n).mul(dist).add(staincenter);
    1006            0 :                 vec ft, fb;
    1007            0 :                 ft.orthogonal(n);
    1008            0 :                 ft.normalize();
    1009            0 :                 fb.cross(ft, n);
    1010            0 :                 vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
    1011            0 :                     pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
    1012            0 :                 vec v1[Face_MaxVerts+4],
    1013            0 :                     v2[Face_MaxVerts+4];
    1014            0 :                 float ptc = pt.dot(pcenter),
    1015            0 :                       pbc = pb.dot(pcenter);
    1016              :                 int numv;
    1017            0 :                 if(numplanes >= 2)
    1018              :                 {
    1019            0 :                     if(l)
    1020              :                     {
    1021            0 :                         pos[1] = pos[2];
    1022            0 :                         pos[2] = pos[3];
    1023              :                     }
    1024            0 :                     numv = polyclip(pos.data(), 3, pt, ptc - stainradius, ptc + stainradius, v1);
    1025            0 :                     if(numv<3)
    1026              :                     {
    1027            0 :                         continue;
    1028              :                     }
    1029              :                 }
    1030              :                 else
    1031              :                 {
    1032            0 :                     numv = polyclip(pos.data(), numverts, pt, ptc - stainradius, ptc + stainradius, v1);
    1033            0 :                     if(numv<3)
    1034              :                     {
    1035            0 :                         continue;
    1036              :                     }
    1037              :                 }
    1038            0 :                 numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
    1039            0 :                 if(numv<3)
    1040              :                 {
    1041            0 :                     continue;
    1042              :                 }
    1043            0 :                 float tsz = flags&StainFlag_Rnd4 ? 0.5f : 1.0f,
    1044            0 :                       scale = tsz*0.5f/stainradius,
    1045            0 :                       tu = stainu + tsz*0.5f - ptc*scale,
    1046            0 :                       tv = stainv + tsz*0.5f - pbc*scale;
    1047            0 :                 pt.mul(scale); pb.mul(scale);
    1048            0 :                 stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
    1049            0 :                           dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
    1050            0 :                 int totalverts = 3*(numv-2);
    1051            0 :                 if(totalverts > buf.maxverts-3)
    1052              :                 {
    1053            0 :                     return;
    1054              :                 }
    1055            0 :                 while(buf.availverts < totalverts)
    1056              :                 {
    1057            0 :                     if(!freestain())
    1058              :                     {
    1059            0 :                         return;
    1060              :                     }
    1061              :                 }
    1062            0 :                 for(int k = 0; k < numv-2; ++k)
    1063              :                 {
    1064            0 :                     stainvert *tri = buf.addtri();
    1065            0 :                     tri[0] = dv1;
    1066            0 :                     tri[1] = dv2;
    1067            0 :                     dv2.pos = v2[k+2];
    1068            0 :                     dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
    1069            0 :                     tri[2] = dv2;
    1070              :                 }
    1071              :             }
    1072              :         }
    1073              : };
    1074              : 
    1075              : std::vector<stainrenderer> stains;
    1076              : 
    1077              : /**
    1078              :  * @brief Sets up stains array.
    1079              :  *
    1080              :  * Sets up each entry in the stains global variable array using init() method
    1081              :  * and then preloads them
    1082              :  *
    1083              :  * Fails to do anything if initing is set (early game loading time)
    1084              :  */
    1085            0 : void initstains()
    1086              : {
    1087            0 :     if(initing)
    1088              :     {
    1089            0 :         return;
    1090              :     }
    1091            0 :     stains.emplace_back("<grey>media/particle/blood.png", stainrenderer::StainFlag_Rnd4|stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_InvMod);
    1092            0 :     stains.emplace_back("<grey>media/particle/pulse_scorch.png", stainrenderer::StainFlag_Rotate, 500);
    1093            0 :     stains.emplace_back("<grey>media/particle/rail_hole.png", stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Overbright);
    1094            0 :     stains.emplace_back("<grey>media/particle/pulse_glow.png", stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Glow|stainrenderer::StainFlag_Saturate, 250, 1500, 250);
    1095            0 :     stains.emplace_back("<grey>media/particle/rail_glow.png",  stainrenderer::StainFlag_Rotate|stainrenderer::StainFlag_Glow|stainrenderer::StainFlag_Saturate, 100, 1100, 100);
    1096            0 :     for(stainrenderer &i : stains)
    1097              :     {
    1098            0 :         i.init(maxstaintris);
    1099              :     }
    1100            0 :     for(uint i = 0; i < stains.size(); ++i)
    1101              :     {
    1102            0 :         loadprogress = static_cast<float>(i+1)/stains.size();
    1103            0 :         stains[i].preload();
    1104              :     }
    1105            0 :     loadprogress = 0;
    1106              : }
    1107              : 
    1108              : /* clearstains: loops through the stains[] global variable array and runs clearstains for each entry
    1109              :  */
    1110            0 : void clearstains()
    1111              : {
    1112            0 :     for(stainrenderer &i : stains)
    1113              :     {
    1114            0 :         i.clearstains();
    1115              :     }
    1116            0 : }
    1117              : 
    1118              : VARNP(stains, showstains, 0, 1, 1); // toggles rendering stains at all
    1119              : 
    1120            0 : bool renderstains(int sbuf, bool gbuf, int layer)
    1121              : {
    1122            0 :     bool rendered = false;
    1123            0 :     for(stainrenderer& d : stains)
    1124              :     {
    1125            0 :         if(d.usegbuffer() != gbuf)
    1126              :         {
    1127            0 :             continue;
    1128              :         }
    1129            0 :         if(sbuf == StainBuffer_Opaque)
    1130              :         {
    1131            0 :             d.clearfadedstains();
    1132            0 :             d.fadeinstains();
    1133            0 :             d.fadeoutstains();
    1134              :         }
    1135            0 :         if(!showstains || !d.hasstains(sbuf))
    1136              :         {
    1137            0 :             continue;
    1138              :         }
    1139            0 :         if(!rendered)
    1140              :         {
    1141            0 :             rendered = true;
    1142            0 :             stainrenderer::setuprenderstate(sbuf, gbuf, layer);
    1143              :         }
    1144            0 :         d.render(sbuf);
    1145              :     }
    1146            0 :     if(!rendered)
    1147              :     {
    1148            0 :         return false;
    1149              :     }
    1150            0 :     stainrenderer::cleanuprenderstate(sbuf, gbuf);
    1151            0 :     return true;
    1152              : }
    1153              : 
    1154            0 : void cleanupstains()
    1155              : {
    1156            0 :     for(stainrenderer& i : stains)
    1157              :     {
    1158            0 :         i.cleanup();
    1159              :     }
    1160            0 : }
    1161              : 
    1162            0 : void addstain(int type, const vec &center, const vec &surface, float radius, const bvec &color, int info)
    1163              : {
    1164            0 :     static VARP(maxstaindistance, 1, 512, 10000); //distance in cubes before stains stop rendering
    1165            0 :     if(!showstains || type<0 || static_cast<size_t>(type) >= stains.size() || center.dist(camera1->o) - radius > maxstaindistance)
    1166              :     {
    1167            0 :         return;
    1168              :     }
    1169            0 :     stainrenderer &d = stains[type];
    1170            0 :     d.addstain(center, surface, radius, color, info, rootworld);
    1171              : }
        

Generated by: LCOV version 2.0-1