LCOV - code coverage report
Current view: top level - engine/render - texture.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 296 1500 19.7 %
Date: 2024-11-22 05:07:59 Functions: 45 119 37.8 %

          Line data    Source code
       1             : // texture.cpp: texture slot management
       2             : 
       3             : #include "../libprimis-headers/cube.h"
       4             : #include "../../shared/geomexts.h"
       5             : #include "../../shared/glexts.h"
       6             : #include "../../shared/stream.h"
       7             : 
       8             : #include "SDL_image.h"
       9             : 
      10             : #include "imagedata.h"
      11             : #include "octarender.h"
      12             : #include "renderwindow.h"
      13             : #include "shader.h"
      14             : #include "shaderparam.h"
      15             : #include "texture.h"
      16             : 
      17             : #include "world/light.h"
      18             : #include "world/material.h"
      19             : #include "world/octaedit.h"
      20             : #include "world/octaworld.h"
      21             : #include "world/world.h"
      22             : 
      23             : #include "interface/console.h"
      24             : #include "interface/control.h"
      25             : 
      26             : extern const texrotation texrotations[8] =
      27             : {
      28             :     { false, false, false }, // 0: default
      29             :     { false,  true,  true }, // 1: 90 degrees
      30             :     {  true,  true, false }, // 2: 180 degrees
      31             :     {  true, false,  true }, // 3: 270 degrees
      32             :     {  true, false, false }, // 4: flip X
      33             :     { false,  true, false }, // 5: flip Y
      34             :     { false, false,  true }, // 6: transpose
      35             :     {  true,  true,  true }, // 7: flipped transpose
      36             : };
      37             : 
      38             : //copies every other pixel into a destination buffer
      39             : //sw,sh are source width/height
      40             : template<int BPP>
      41           3 : static void halvetexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst)
      42             : {
      43          10 :     for(const uchar *yend = &src[sh*stride]; src < yend;)
      44             :     {
      45          28 :         for(const uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP)
      46             :         {
      47          42 :             for(int i = 0; i < BPP; ++i)
      48             :             {
      49          21 :                 dst[i] = (static_cast<uint>(xsrc[i]) + static_cast<uint>(xsrc[i+BPP]) + static_cast<uint>(xsrc[stride+i]) + static_cast<uint>(xsrc[stride+i+BPP]))>>2;
      50             :             }
      51             :         }
      52           7 :         src += 2*stride;
      53             :     }
      54           3 : }
      55             : 
      56             : template<int BPP>
      57           0 : static void shifttexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
      58             : {
      59           0 :     uint wfrac = sw/dw,
      60           0 :          hfrac = sh/dh,
      61           0 :          wshift = 0,
      62           0 :          hshift = 0;
      63           0 :     while(dw<<wshift < sw)
      64             :     {
      65           0 :         wshift++;
      66             :     }
      67           0 :     while(dh<<hshift < sh)
      68             :     {
      69           0 :         hshift++;
      70             :     }
      71           0 :     uint tshift = wshift + hshift;
      72           0 :     for(const uchar *yend = &src[sh*stride]; src < yend;)
      73             :     {
      74           0 :         for(const uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += wfrac*BPP, dst += BPP)
      75             :         {
      76             : 
      77           0 :             uint t[BPP] = {0};
      78           0 :             for(const uchar *ycur = xsrc, *xend = &ycur[wfrac*BPP], *yend = &src[hfrac*stride];
      79           0 :                 ycur < yend;
      80           0 :                 ycur += stride, xend += stride)
      81             :             {
      82             :                 //going to (xend - 1) seems to be necessary to avoid buffer overrun
      83           0 :                 for(const uchar *xcur = ycur; xcur < xend -1; xcur += BPP)
      84             :                 {
      85           0 :                     for(int i = 0; i < BPP; ++i)
      86             :                     {
      87           0 :                         t[i] += xcur[i];
      88             :                     }
      89             :                 }
      90             :             }
      91           0 :             for(int i = 0; i < BPP; ++i)
      92             :             {
      93           0 :                 dst[i] = t[i] >> tshift;
      94             :             }
      95             :         }
      96           0 :         src += hfrac*stride;
      97             :     }
      98           0 : }
      99             : 
     100             : template<int BPP>
     101           0 : static void scaletexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
     102             : {
     103           0 :     uint wfrac = (sw<<12)/dw,
     104           0 :          hfrac = (sh<<12)/dh,
     105           0 :          darea = dw*dh,
     106           0 :          sarea = sw*sh;
     107             :     int over, under;
     108             :     //the for loops here are merely to increment over & under vars which are used later
     109           0 :     for(over = 0; (darea>>over) > sarea; over++)
     110             :     {
     111             :         //(empty body)
     112             :     }
     113           0 :     for(under = 0; (darea<<under) < sarea; under++)
     114             :     {
     115             :         //(empty body)
     116             :     }
     117           0 :     uint cscale = std::clamp(under, over - 12, 12),
     118           0 :          ascale = std::clamp(12 + under - over, 0, 24),
     119           0 :          dscale = ascale + 12 - cscale,
     120           0 :          area = (static_cast<ullong>(darea)<<ascale)/sarea;
     121           0 :     dw *= wfrac;
     122           0 :     dh *= hfrac;
     123           0 :     for(uint y = 0; y < dh; y += hfrac)
     124             :     {
     125           0 :         const uint yn = y + hfrac - 1,
     126           0 :                    yi = y>>12, h = (yn>>12) - yi,
     127           0 :                    ylow = ((yn|(-static_cast<int>(h)>>24))&0xFFFU) + 1 - (y&0xFFFU),
     128           0 :                    yhigh = (yn&0xFFFU) + 1;
     129           0 :         const uchar *ysrc = &src[yi*stride];
     130           0 :         for(uint x = 0; x < dw; x += wfrac, dst += BPP)
     131             :         {
     132           0 :             const uint xn = x + wfrac - 1,
     133           0 :                        xi = x>>12,
     134           0 :                        w = (xn>>12) - xi,
     135           0 :                        xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU),
     136           0 :                        xhigh = (xn&0xFFFU) + 1;
     137           0 :             const uchar *xsrc = &ysrc[xi*BPP],
     138           0 :                         *xend = &xsrc[w*BPP];
     139           0 :             uint t[BPP] = {0};
     140           0 :             for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
     141             :             {
     142           0 :                 for(int i = 0; i < BPP; ++i)
     143             :                 {
     144           0 :                     t[i] += xcur[i];
     145             :                 }
     146             :             }
     147           0 :             for(int i = 0; i < BPP; ++i)
     148             :             {
     149           0 :                 t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
     150             :             }
     151           0 :             if(h)
     152             :             {
     153           0 :                 xsrc += stride;
     154           0 :                 xend += stride;
     155           0 :                 for(uint hcur = h; --hcur; xsrc += stride, xend += stride)
     156             :                 {
     157           0 :                     uint c[BPP] = {0};
     158           0 :                     for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
     159           0 :                         for(int i = 0; i < BPP; ++i)
     160             :                         {
     161           0 :                             c[i] += xcur[i];
     162             :                         }
     163           0 :                     for(int i = 0; i < BPP; ++i)
     164             :                     {
     165           0 :                         t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale;
     166             :                     }
     167             :                 }
     168           0 :                 uint c[BPP] = {0};
     169           0 :                 for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
     170           0 :                     for(int i = 0; i < BPP; ++i)
     171             :                     {
     172           0 :                         c[i] += xcur[i];
     173             :                     }
     174           0 :                 for(int i = 0; i < BPP; ++i)
     175             :                 {
     176           0 :                     t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
     177             :                 }
     178             :             }
     179           0 :             for(int i = 0; i < BPP; ++i)
     180             :             {
     181           0 :                 dst[i] = (t[i] * area)>>dscale;
     182             :             }
     183             :         }
     184             :     }
     185           0 : }
     186             : 
     187           3 : void scaletexture(const uchar * RESTRICT src, uint sw, uint sh, uint bpp, uint pitch, uchar * RESTRICT dst, uint dw, uint dh)
     188             : {
     189           3 :     if(sw == dw*2 && sh == dh*2)
     190             :     {
     191           3 :         switch(bpp)
     192             :         {
     193           3 :             case 1: return halvetexture<1>(src, sw, sh, pitch, dst);
     194           0 :             case 2: return halvetexture<2>(src, sw, sh, pitch, dst);
     195           0 :             case 3: return halvetexture<3>(src, sw, sh, pitch, dst);
     196           0 :             case 4: return halvetexture<4>(src, sw, sh, pitch, dst);
     197             :         }
     198             :     }
     199           0 :     else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1))
     200             :     {
     201           0 :         switch(bpp)
     202             :         {
     203           0 :             case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh);
     204           0 :             case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh);
     205           0 :             case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh);
     206           0 :             case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh);
     207             :         }
     208             :     }
     209             :     else
     210             :     {
     211           0 :         switch(bpp)
     212             :         {
     213           0 :             case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh);
     214           0 :             case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh);
     215           0 :             case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh);
     216           0 :             case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh);
     217             :         }
     218             :     }
     219             : }
     220             : 
     221             : template<int BPP>
     222           0 : static void reorienttexture(const uchar * RESTRICT src, int sw, int sh, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
     223             : {
     224           0 :     int stridex = BPP,
     225           0 :         stridey = BPP;
     226           0 :     if(swapxy)
     227             :     {
     228           0 :         stridex *= sh;
     229             :     }
     230             :     else
     231             :     {
     232           0 :         stridey *= sw;
     233             :     }
     234           0 :     if(flipx)
     235             :     {
     236           0 :         dst += (sw-1)*stridex;
     237           0 :         stridex = -stridex;
     238             :     }
     239           0 :     if(flipy)
     240             :     {
     241           0 :         dst += (sh-1)*stridey;
     242           0 :         stridey = -stridey;
     243             :     }
     244           0 :     const uchar *srcrow = src;
     245           0 :     for(int i = 0; i < sh; ++i)
     246             :     {
     247             :         uchar* curdst;
     248             :         const uchar* src;
     249             :         const uchar* end;
     250           0 :         for(curdst = dst, src = srcrow, end = &srcrow[sw*BPP]; src < end;)
     251             :         {
     252           0 :             for(int k = 0; k < BPP; ++k)
     253             :             {
     254           0 :                 curdst[k] = *src++;
     255             :             }
     256           0 :             curdst += stridex;
     257             :         }
     258           0 :         srcrow += stride;
     259           0 :         dst += stridey;
     260             :     }
     261           0 : }
     262             : 
     263           0 : void reorienttexture(const uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
     264             : {
     265           0 :     switch(bpp)
     266             :     {
     267           0 :         case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
     268           0 :         case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
     269           0 :         case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
     270           0 :         case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
     271             :     }
     272             : }
     273             : /*  var             min  default max  */
     274             : VAR(hwtexsize,      1,   0,      0);
     275             : VAR(hwcubetexsize,  1,   0,      0);
     276             : VAR(hwmaxaniso,     1,   0,      0);
     277             : VAR(hwtexunits,     1,   0,      0);
     278             : VAR(hwvtexunits,    1,   0,      0);
     279           0 : VARFP(maxtexsize,   0,   0,      1<<12, initwarning("texture quality",   Init_Load));
     280           0 : VARFP(reducefilter, 0,   1,      1,     initwarning("texture quality",   Init_Load));
     281           0 : VARF(trilinear,     0,   1,      1,     initwarning("texture filtering", Init_Load));
     282           0 : VARF(bilinear,      0,   1,      1,     initwarning("texture filtering", Init_Load));
     283           0 : VARFP(aniso,        0,   0,      16,    initwarning("texture filtering", Init_Load));
     284             : 
     285           1 : static int formatsize(GLenum format)
     286             : {
     287           1 :     switch(format)
     288             :     {
     289           1 :         case GL_RED:
     290             :         {
     291           1 :             return 1;
     292             :         }
     293           0 :         case GL_RG:
     294             :         {
     295           0 :             return 2;
     296             :         }
     297           0 :         case GL_RGB:
     298             :         {
     299           0 :             return 3;
     300             :         }
     301           0 :         case GL_RGBA:
     302             :         {
     303           0 :             return 4;
     304             :         }
     305           0 :         default:
     306             :         {
     307           0 :             return 4;
     308             :         }
     309             :     }
     310             : }
     311             : 
     312           1 : static void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int compress, int &tw, int &th)
     313             : {
     314           1 :     int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
     315           1 :         sizelimit = mipmap && maxtexsize ? std::min(maxtexsize, hwlimit) : hwlimit;
     316           1 :     if(compress > 0)
     317             :     {
     318           0 :         w = std::max(w/compress, 1);
     319           0 :         h = std::max(h/compress, 1);
     320             :     }
     321           1 :     w = std::min(w, sizelimit);
     322           1 :     h = std::min(h, sizelimit);
     323           1 :     tw = w;
     324           1 :     th = h;
     325           1 : }
     326             : 
     327           5 : static int texalign(int w, int bpp)
     328             : {
     329           5 :     int stride = w*bpp;
     330           5 :     if(stride&1)
     331             :     {
     332           1 :         return 1;
     333             :     }
     334           4 :     if(stride&2)
     335             :     {
     336           1 :         return 2;
     337             :     }
     338           3 :     return 4;
     339             : }
     340             : 
     341           1 : static void uploadtexture(GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, const void *pixels, int pw, int ph, int pitch, bool mipmap)
     342             : {
     343           1 :     int bpp = formatsize(format),
     344           1 :         row = 0,
     345           1 :         rowalign = 0;
     346           1 :     if(!pitch)
     347             :     {
     348           0 :         pitch = pw*bpp;
     349             :     }
     350           1 :     uchar *buf = nullptr;
     351           1 :     if(pw!=tw || ph!=th)
     352             :     {
     353           0 :         buf = new uchar[tw*th*bpp];
     354           0 :         scaletexture(static_cast<uchar *>(const_cast<void *>(pixels)), pw, ph, bpp, pitch, buf, tw, th);
     355             :     }
     356           1 :     else if(tw*bpp != pitch)
     357             :     {
     358           0 :         row = pitch/bpp;
     359           0 :         rowalign = texalign(pitch, 1);
     360           0 :         while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch)
     361             :         {
     362           0 :             rowalign >>= 1;
     363             :         }
     364           0 :         if(!rowalign)
     365             :         {
     366           0 :             row = 0;
     367           0 :             buf = new uchar[tw*th*bpp];
     368           0 :             for(int i = 0; i < th; ++i)
     369             :             {
     370           0 :                 std::memcpy(&buf[i*tw*bpp], &(const_cast<uchar *>(reinterpret_cast<const uchar *>(pixels)))[i*pitch], tw*bpp);
     371             :             }
     372             :         }
     373             :     }
     374           1 :     for(int level = 0, align = 0;; level++)
     375             :     {
     376           4 :         uchar *src = buf ? buf : const_cast<uchar *>(reinterpret_cast<const uchar *>(pixels));
     377           4 :         if(buf)
     378             :         {
     379           3 :             pitch = tw*bpp;
     380             :         }
     381           4 :         int srcalign = row > 0 ? rowalign : texalign(pitch, 1);
     382           4 :         if(align != srcalign)
     383             :         {
     384           3 :             glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign);
     385             :         }
     386           4 :         if(row > 0)
     387             :         {
     388           0 :             glPixelStorei(GL_UNPACK_ROW_LENGTH, row);
     389             :         }
     390           4 :         if(target==GL_TEXTURE_1D)
     391             :         {
     392           0 :             glTexImage1D(target, level, internal, tw, 0, format, type, src);
     393             :         }
     394             :         else
     395             :         {
     396           4 :             glTexImage2D(target, level, internal, tw, th, 0, format, type, src);
     397             :         }
     398           4 :         if(row > 0)
     399             :         {
     400           0 :             glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0);
     401             :         }
     402           4 :         if(!mipmap || std::max(tw, th) <= 1)
     403             :         {
     404           1 :             break;
     405             :         }
     406           3 :         int srcw = tw,
     407           3 :             srch = th;
     408           3 :         if(tw > 1)
     409             :         {
     410           3 :             tw /= 2;
     411             :         }
     412           3 :         if(th > 1)
     413             :         {
     414           3 :             th /= 2;
     415             :         }
     416           3 :         if(src)
     417             :         {
     418           3 :             if(!buf)
     419             :             {
     420           1 :                 buf = new uchar[tw*th*bpp];
     421             :             }
     422           3 :             scaletexture(src, srcw, srch, bpp, pitch, buf, tw, th);
     423             :         }
     424           3 :     }
     425           1 :     if(buf)
     426             :     {
     427           1 :         delete[] buf;
     428             :     }
     429           1 : }
     430             : 
     431           0 : static void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, const uchar *data, int align, int blocksize, int levels, bool mipmap)
     432             : {
     433           0 :     int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
     434           0 :         sizelimit = levels > 1 && maxtexsize ? std::min(maxtexsize, hwlimit) : hwlimit;
     435           0 :     int level = 0;
     436           0 :     for(int i = 0; i < levels; ++i)
     437             :     {
     438           0 :         int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize;
     439           0 :         if(w <= sizelimit && h <= sizelimit)
     440             :         {
     441           0 :             if(target==GL_TEXTURE_1D)
     442             :             {
     443           0 :                 glCompressedTexImage1D(subtarget, level, format, w, 0, size, data);
     444             :             }
     445             :             else
     446             :             {
     447           0 :                 glCompressedTexImage2D(subtarget, level, format, w, h, 0, size, data);
     448             :             }
     449           0 :             level++;
     450           0 :             if(!mipmap)
     451             :             {
     452           0 :                 break;
     453             :             }
     454             :         }
     455           0 :         if(std::max(w, h) <= 1)
     456             :         {
     457           0 :             break;
     458             :         }
     459           0 :         if(w > 1)
     460             :         {
     461           0 :             w /= 2;
     462             :         }
     463           0 :         if(h > 1)
     464             :         {
     465           0 :             h /= 2;
     466             :         }
     467           0 :         data += size;
     468             :     }
     469           0 : }
     470             : 
     471           1 : static GLenum textarget(GLenum subtarget)
     472             : {
     473           1 :     switch(subtarget)
     474             :     {
     475           0 :         case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
     476             :         case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
     477             :         case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
     478             :         case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     479             :         case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
     480             :         case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
     481             :         {
     482           0 :             return GL_TEXTURE_CUBE_MAP;
     483             :         }
     484             :     }
     485           1 :     return subtarget;
     486             : }
     487             : 
     488           1 : const GLint *swizzlemask(GLenum format)
     489             : {
     490             :     static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE },
     491             :                        luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
     492           1 :     switch(format)
     493             :     {
     494           1 :         case GL_RED:
     495             :         {
     496           1 :             return luminance;
     497             :         }
     498           0 :         case GL_RG:
     499             :         {
     500           0 :             return luminancealpha;
     501             :         }
     502             :     }
     503           0 :     return nullptr;
     504             : }
     505             : 
     506           1 : static void setuptexparameters(int tnum, const void *pixels, int clamp, int filter, GLenum format, GLenum target, bool swizzle)
     507             : {
     508           1 :     glBindTexture(target, tnum);
     509           1 :     glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT));
     510           1 :     if(target!=GL_TEXTURE_1D)
     511             :     {
     512           1 :         glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT));
     513             :     }
     514           1 :     if(target==GL_TEXTURE_3D)
     515             :     {
     516           0 :         glTexParameteri(target, GL_TEXTURE_WRAP_R, clamp&4 ? GL_CLAMP_TO_EDGE : (clamp&0x400 ? GL_MIRRORED_REPEAT : GL_REPEAT));
     517             :     }
     518           1 :     if(target==GL_TEXTURE_2D && std::min(aniso, hwmaxaniso) > 0 && filter > 1)
     519             :     {
     520           0 :         glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, std::min(aniso, hwmaxaniso));
     521             :     }
     522           1 :     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST);
     523           2 :     glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
     524             :         filter > 1 ?
     525           2 :             (trilinear ?
     526           1 :                 (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) :
     527           0 :                 (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) :
     528           0 :             (filter && bilinear ? GL_LINEAR : GL_NEAREST));
     529           1 :     if(swizzle)
     530             :     {
     531           1 :         const GLint *mask = swizzlemask(format);
     532           1 :         if(mask)
     533             :         {
     534           1 :             glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask);
     535             :         }
     536             :     }
     537           1 : }
     538             : 
     539           1 : static GLenum textype(GLenum &component, GLenum &format)
     540             : {
     541           1 :     GLenum type = GL_UNSIGNED_BYTE;
     542           1 :     switch(component)
     543             :     {
     544           0 :         case GL_R16F:
     545             :         case GL_R32F:
     546             :         {
     547           0 :             if(!format)
     548             :             {
     549           0 :                 format = GL_RED;
     550             :             }
     551           0 :             type = GL_FLOAT;
     552           0 :             break;
     553             :         }
     554           0 :         case GL_RG16F:
     555             :         case GL_RG32F:
     556             :         {
     557           0 :             if(!format)
     558             :             {
     559           0 :                 format = GL_RG;
     560             :             }
     561           0 :             type = GL_FLOAT;
     562           0 :             break;
     563             :         }
     564           0 :         case GL_RGB16F:
     565             :         case GL_RGB32F:
     566             :         case GL_R11F_G11F_B10F:
     567             :         {
     568           0 :             if(!format)
     569             :             {
     570           0 :                 format = GL_RGB;
     571             :             }
     572           0 :             type = GL_FLOAT;
     573           0 :             break;
     574             :         }
     575           0 :         case GL_RGBA16F:
     576             :         case GL_RGBA32F:
     577             :         {
     578           0 :             if(!format)
     579             :             {
     580           0 :                 format = GL_RGBA;
     581             :             }
     582           0 :             type = GL_FLOAT;
     583           0 :             break;
     584             :         }
     585           0 :         case GL_DEPTH_COMPONENT16:
     586             :         case GL_DEPTH_COMPONENT24:
     587             :         case GL_DEPTH_COMPONENT32:
     588             :         {
     589           0 :             if(!format)
     590             :             {
     591           0 :                 format = GL_DEPTH_COMPONENT;
     592             :             }
     593           0 :             break;
     594             :         }
     595           0 :         case GL_DEPTH_STENCIL:
     596             :         case GL_DEPTH24_STENCIL8:
     597             :         {
     598           0 :             if(!format)
     599             :             {
     600           0 :                 format = GL_DEPTH_STENCIL;
     601             :             }
     602           0 :             type = GL_UNSIGNED_INT_24_8;
     603           0 :             break;
     604             :         }
     605           0 :         case GL_R8:
     606             :         case GL_R16:
     607             :         {
     608           0 :             if(!format)
     609             :             {
     610           0 :                 format = GL_RED;
     611             :             }
     612           0 :             break;
     613             :         }
     614           0 :         case GL_RG8:
     615             :         case GL_RG16:
     616             :         {
     617           0 :             if(!format)
     618             :             {
     619           0 :                 format = GL_RG;
     620             :             }
     621           0 :             break;
     622             :         }
     623           0 :         case GL_RGB5:
     624             :         case GL_RGB8:
     625             :         case GL_RGB16:
     626             :         case GL_RGB10:
     627             :         {
     628           0 :             if(!format)
     629             :             {
     630           0 :                 format = GL_RGB;
     631             :             }
     632           0 :             break;
     633             :         }
     634           0 :         case GL_RGB5_A1:
     635             :         case GL_RGBA8:
     636             :         case GL_RGBA16:
     637             :         case GL_RGB10_A2:
     638             :         {
     639           0 :             if(!format)
     640             :             {
     641           0 :                 format = GL_RGBA;
     642             :             }
     643           0 :             break;
     644             :         }
     645           0 :         case GL_RGB8UI:
     646             :         case GL_RGB16UI:
     647             :         case GL_RGB32UI:
     648             :         case GL_RGB8I:
     649             :         case GL_RGB16I:
     650             :         case GL_RGB32I:
     651             :         {
     652           0 :             if(!format)
     653             :             {
     654           0 :                 format = GL_RGB_INTEGER;
     655             :             }
     656           0 :             break;
     657             :         }
     658           0 :         case GL_RGBA8UI:
     659             :         case GL_RGBA16UI:
     660             :         case GL_RGBA32UI:
     661             :         case GL_RGBA8I:
     662             :         case GL_RGBA16I:
     663             :         case GL_RGBA32I:
     664             :         {
     665           0 :             if(!format)
     666             :             {
     667           0 :                 format = GL_RGBA_INTEGER;
     668             :             }
     669           0 :             break;
     670             :         }
     671           0 :         case GL_R8UI:
     672             :         case GL_R16UI:
     673             :         case GL_R32UI:
     674             :         case GL_R8I:
     675             :         case GL_R16I:
     676             :         case GL_R32I:
     677             :         {
     678           0 :             if(!format)
     679             :             {
     680           0 :                 format = GL_RED_INTEGER;
     681             :             }
     682           0 :             break;
     683             :         }
     684           0 :         case GL_RG8UI:
     685             :         case GL_RG16UI:
     686             :         case GL_RG32UI:
     687             :         case GL_RG8I:
     688             :         case GL_RG16I:
     689             :         case GL_RG32I:
     690             :         {
     691           0 :             if(!format)
     692             :             {
     693           0 :                 format = GL_RG_INTEGER;
     694             :             }
     695           0 :             break;
     696             :         }
     697             :     }
     698           1 :     if(!format)
     699             :     {
     700           0 :         format = component;
     701             :     }
     702           1 :     return type;
     703             : }
     704             : 
     705           1 : void createtexture(int tnum, int w, int h, const void *pixels, int clamp, int filter, GLenum component, GLenum subtarget, int pw, int ph, int pitch, bool resize, GLenum format, bool swizzle)
     706             : {
     707           1 :     GLenum target = textarget(subtarget),
     708           1 :            type = textype(component, format);
     709           1 :     if(tnum)
     710             :     {
     711           1 :         setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
     712             :     }
     713           1 :     if(!pw)
     714             :     {
     715           0 :         pw = w;
     716             :     }
     717           1 :     if(!ph)
     718             :     {
     719           0 :         ph = h;
     720             :     }
     721           1 :     int tw = w,
     722           1 :         th = h;
     723           1 :     bool mipmap = filter > 1;
     724           1 :     if(resize && pixels)
     725             :     {
     726           0 :         resizetexture(w, h, mipmap, false, target, 0, tw, th);
     727             :     }
     728           1 :     uploadtexture(subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap);
     729           1 : }
     730             : 
     731           0 : static void createcompressedtexture(int tnum, int w, int h, const uchar *data, int align, int blocksize, int levels, int clamp, int filter, GLenum format, GLenum subtarget, bool swizzle = false)
     732             : {
     733           0 :     GLenum target = textarget(subtarget);
     734           0 :     if(tnum)
     735             :     {
     736           0 :         setuptexparameters(tnum, data, clamp, filter, format, target, swizzle);
     737             :     }
     738           0 :     uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1);
     739           0 : }
     740             : 
     741           0 : void create3dtexture(int tnum, int w, int h, int d, const void *pixels, int clamp, int filter, GLenum component, GLenum target, bool swizzle)
     742             : {
     743           0 :     GLenum format = GL_FALSE, type = textype(component, format);
     744           0 :     if(tnum)
     745             :     {
     746           0 :         setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
     747             :     }
     748           0 :     glTexImage3D(target, 0, component, w, h, d, 0, format, type, pixels);
     749           0 : }
     750             : 
     751             : std::unordered_map<std::string, Texture> textures;
     752             : 
     753             : Texture *notexture = nullptr; // used as default, ensured to be loaded
     754             : 
     755           2 : GLenum texformat(int bpp, bool swizzle)
     756             : {
     757           2 :     switch(bpp)
     758             :     {
     759           2 :         case 1:
     760             :         {
     761           2 :             return GL_RED;
     762             :         }
     763           0 :         case 2:
     764             :         {
     765           0 :             return GL_RG;
     766             :         }
     767           0 :         case 3:
     768             :         {
     769           0 :             return GL_RGB;
     770             :         }
     771           0 :         case 4:
     772             :         {
     773           0 :             return GL_RGBA;
     774             :         }
     775           0 :         default:
     776             :         {
     777           0 :             return 0;
     778             :         }
     779             :     }
     780             : }
     781             : 
     782           1 : static bool alphaformat(GLenum format)
     783             : {
     784           1 :     switch(format)
     785             :     {
     786           0 :         case GL_RG:
     787             :         case GL_RGBA:
     788             :         {
     789           0 :             return true;
     790             :         }
     791           1 :         default:
     792             :         {
     793           1 :             return false;
     794             :         }
     795             :     }
     796             : }
     797             : 
     798           0 : bool floatformat(GLenum format)
     799             : {
     800           0 :     switch(format)
     801             :     {
     802           0 :         case GL_R16F:
     803             :         case GL_R32F:
     804             :         case GL_RG16F:
     805             :         case GL_RG32F:
     806             :         case GL_RGB16F:
     807             :         case GL_RGB32F:
     808             :         case GL_R11F_G11F_B10F:
     809             :         case GL_RGBA16F:
     810             :         case GL_RGBA32F:
     811             :         {
     812           0 :             return true;
     813             :         }
     814           0 :         default:
     815             :         {
     816           0 :             return false;
     817             :         }
     818             :     }
     819             : }
     820             : 
     821           1 : static Texture *newtexture(Texture *t, const char *rname, ImageData &s, int clamp = 0, bool mipit = true, bool canreduce = false, bool transient = false, int compress = 0)
     822             : {
     823           1 :     if(!t)
     824             :     {
     825           1 :         char *key = newstring(rname);
     826           1 :         auto itr = textures.insert_or_assign(key, Texture()).first;
     827           1 :         t = &(*itr).second;
     828           1 :         t->name = key;
     829             :     }
     830             : 
     831           1 :     t->clamp = clamp;
     832           1 :     t->mipmap = mipit;
     833           1 :     t->type = Texture::IMAGE;
     834           1 :     if(transient)
     835             :     {
     836           0 :         t->type |= Texture::TRANSIENT;
     837             :     }
     838           1 :     if(clamp&0x300)
     839             :     {
     840           0 :         t->type |= Texture::MIRROR;
     841             :     }
     842           1 :     if(!s.data)
     843             :     {
     844           0 :         t->type |= Texture::STUB;
     845           0 :         t->w = t->h = t->xs = t->ys = t->bpp = 0;
     846           0 :         return t;
     847             :     }
     848             : 
     849           1 :     bool swizzle = !(clamp&0x10000);
     850             :     GLenum format;
     851           1 :     format = texformat(s.bpp, swizzle);
     852           1 :     t->bpp = s.bpp;
     853           1 :     if(alphaformat(format))
     854             :     {
     855           0 :         t->type |= Texture::ALPHA;
     856             :     }
     857           1 :     t->w = t->xs = s.w;
     858           1 :     t->h = t->ys = s.h;
     859           1 :     int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0;
     860           1 :     glGenTextures(1, &t->id);
     861           1 :     if(s.compressed)
     862             :     {
     863           0 :         uchar *data = s.data;
     864           0 :         int levels = s.levels, level = 0;
     865           0 :         int sizelimit = mipit && maxtexsize ? std::min(maxtexsize, hwtexsize) : hwtexsize;
     866           0 :         while(t->w > sizelimit || t->h > sizelimit)
     867             :         {
     868           0 :             data += s.calclevelsize(level++);
     869           0 :             levels--;
     870           0 :             if(t->w > 1)
     871             :             {
     872           0 :                 t->w /= 2;
     873             :             }
     874           0 :             if(t->h > 1)
     875             :             {
     876           0 :                 t->h /= 2;
     877             :             }
     878             :         }
     879           0 :         createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle);
     880             :     }
     881             :     else
     882             :     {
     883           1 :         resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h);
     884           1 :         createtexture(t->id, t->w, t->h, s.data, clamp, filter, format, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle);
     885             :     }
     886           1 :     return t;
     887             : }
     888             : 
     889           0 : static SDL_Surface *creatergbsurface(SDL_Surface *os)
     890             : {
     891           0 :     SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, 0x0000ff, 0x00ff00, 0xff0000, 0);
     892           0 :     if(ns)
     893             :     {
     894           0 :         SDL_BlitSurface(os, nullptr, ns, nullptr);
     895             :     }
     896           0 :     SDL_FreeSurface(os);
     897           0 :     return ns;
     898             : }
     899             : 
     900           0 : static SDL_Surface *creatergbasurface(SDL_Surface *os)
     901             : {
     902           0 :     SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
     903           0 :     if(ns)
     904             :     {
     905           0 :         SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE);
     906           0 :         SDL_BlitSurface(os, nullptr, ns, nullptr);
     907             :     }
     908           0 :     SDL_FreeSurface(os);
     909           0 :     return ns;
     910             : }
     911             : 
     912           1 : static bool checkgrayscale(SDL_Surface *s)
     913             : {
     914             :     // gray scale images have 256 levels, no colorkey, and the palette is a ramp
     915           1 :     if(s->format->palette)
     916             :     {
     917           1 :         if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, nullptr) >= 0)
     918             :         {
     919           0 :             return false;
     920             :         }
     921           1 :         const SDL_Color *colors = s->format->palette->colors;
     922         257 :         for(int i = 0; i < 256; ++i)
     923             :         {
     924         256 :             if(colors[i].r != i || colors[i].g != i || colors[i].b != i)
     925             :             {
     926           0 :                 return false;
     927             :             }
     928             :         }
     929             :     }
     930           1 :     return true;
     931             : }
     932             : 
     933          10 : static SDL_Surface *fixsurfaceformat(SDL_Surface *s)
     934             : {
     935          10 :     if(!s)
     936             :     {
     937           9 :         return nullptr;
     938             :     }
     939           1 :     if(!s->pixels || std::min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0)
     940             :     {
     941           0 :         SDL_FreeSurface(s);
     942           0 :         return nullptr;
     943             :     }
     944             :     static const uint rgbmasks[]  = { 0x0000ff, 0x00ff00, 0xff0000, 0 },
     945             :                       rgbamasks[] = { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
     946           1 :     switch(s->format->BytesPerPixel)
     947             :     {
     948           1 :         case 1:
     949           1 :             if(!checkgrayscale(s))
     950             :             {
     951           0 :                 return SDL_GetColorKey(s, nullptr) >= 0 ? creatergbasurface(s) : creatergbsurface(s);
     952             :             }
     953           1 :             break;
     954           0 :         case 3:
     955           0 :             if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2])
     956             :             {
     957           0 :                 return creatergbsurface(s);
     958             :             }
     959           0 :             break;
     960           0 :         case 4:
     961           0 :             if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3])
     962             :             {
     963           0 :                 return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s);
     964             :             }
     965           0 :             break;
     966             :     }
     967           1 :     return s;
     968             : }
     969             : 
     970          10 : SDL_Surface *loadsurface(const char *name)
     971             : {
     972          10 :     SDL_Surface *s = nullptr;
     973          10 :     stream *z = openzipfile(name, "rb");
     974          10 :     if(z)
     975             :     {
     976           0 :         SDL_RWops *rw = z->rwops();
     977           0 :         if(rw)
     978             :         {
     979           0 :             const char *ext = std::strrchr(name, '.');
     980           0 :             if(ext)
     981             :             {
     982           0 :                 ++ext;
     983             :             }
     984           0 :             s = IMG_LoadTyped_RW(rw, 0, ext);
     985           0 :             SDL_FreeRW(rw);
     986             :         }
     987           0 :         delete z;
     988             :     }
     989          10 :     if(!s)
     990             :     {
     991          10 :         s = IMG_Load(findfile(name, "rb"));
     992             :     }
     993          10 :     return fixsurfaceformat(s);
     994             : }
     995             : 
     996           2 : const uchar * Texture::loadalphamask()
     997             : {
     998           2 :     if(alphamask)
     999             :     {
    1000           0 :         return alphamask;
    1001             :     }
    1002           2 :     if(!(type&Texture::ALPHA))
    1003             :     {
    1004           2 :         return nullptr;
    1005             :     }
    1006           0 :     ImageData s;
    1007           0 :     if(!s.texturedata(name, false) || !s.data || s.compressed)
    1008             :     {
    1009           0 :         return nullptr;
    1010             :     }
    1011           0 :     alphamask = new uchar[s.h * ((s.w+7)/8)];
    1012           0 :     uchar *srcrow = s.data,
    1013           0 :           *dst = alphamask-1;
    1014           0 :     for(int y = 0; y < s.h; ++y)
    1015             :     {
    1016           0 :         uchar *src = srcrow+s.bpp-1;
    1017           0 :         for(int x = 0; x < s.w; ++x)
    1018             :         {
    1019           0 :             int offset = x%8;
    1020           0 :             if(!offset)
    1021             :             {
    1022           0 :                 *++dst = 0;
    1023             :             }
    1024           0 :             if(*src)
    1025             :             {
    1026           0 :                 *dst |= 1<<offset;
    1027             :             }
    1028           0 :             src += s.bpp;
    1029             :         }
    1030           0 :         srcrow += s.pitch;
    1031             :     }
    1032           0 :     return alphamask;
    1033           0 : }
    1034             : 
    1035           0 : float Texture::ratio() const
    1036             : {
    1037           0 :     return (w / static_cast<float>(h));
    1038             : }
    1039             : 
    1040          17 : Texture *textureload(const char *name, int clamp, bool mipit, bool msg)
    1041             : {
    1042          17 :     std::string tname(name);
    1043          17 :     auto itr = textures.find(path(tname));
    1044          17 :     if(itr != textures.end())
    1045             :     {
    1046           7 :         return &(*itr).second;
    1047             :     }
    1048          10 :     int compress = 0;
    1049          10 :     ImageData s;
    1050          10 :     if(s.texturedata(tname.c_str(), msg, &compress, &clamp))
    1051             :     {
    1052           1 :         return newtexture(nullptr, tname.c_str(), s, clamp, mipit, false, false, compress);
    1053             :     }
    1054           9 :     return notexture;
    1055          17 : }
    1056             : 
    1057           0 : bool settexture(const char *name, int clamp)
    1058             : {
    1059           0 :     Texture *t = textureload(name, clamp, true, false);
    1060           0 :     glBindTexture(GL_TEXTURE_2D, t->id);
    1061           0 :     return t != notexture;
    1062             : }
    1063             : 
    1064             : std::vector<VSlot *> vslots;
    1065             : std::vector<Slot *> slots;
    1066             : MatSlot materialslots[(MatFlag_Volume|MatFlag_Index)+1];
    1067             : Slot dummyslot;
    1068             : VSlot dummyvslot(&dummyslot);
    1069             : static std::vector<DecalSlot *> decalslots;
    1070             : DecalSlot dummydecalslot;
    1071             : Slot *defslot = nullptr;
    1072             : 
    1073           2 : const char *Slot::name() const
    1074             : {
    1075           2 :     return tempformatstring("slot %d", index);
    1076             : }
    1077             : 
    1078          32 : MatSlot::MatSlot() : Slot(static_cast<int>(this - materialslots)), VSlot(this) {}
    1079           0 : const char *MatSlot::name() const
    1080             : {
    1081           0 :     return tempformatstring("material slot %s", findmaterialname(Slot::index));
    1082             : }
    1083             : 
    1084           2 : const char *DecalSlot::name() const
    1085             : {
    1086           2 :     return tempformatstring("decal slot %d", Slot::index);
    1087             : }
    1088             : 
    1089           1 : void texturereset(int *n)
    1090             : {
    1091           1 :     if(!(identflags&Idf_Overridden) && !allowediting)
    1092             :     {
    1093           1 :         return;
    1094             :     }
    1095           0 :     defslot = nullptr;
    1096           0 :     resetslotshader();
    1097           0 :     int limit = std::clamp(*n, 0, static_cast<int>(slots.size()));
    1098           0 :     for(size_t i = limit; i < slots.size(); i++)
    1099             :     {
    1100           0 :         Slot *s = slots[i];
    1101           0 :         for(VSlot *vs = s->variants; vs; vs = vs->next)
    1102             :         {
    1103           0 :             vs->slot = &dummyslot;
    1104             :         }
    1105           0 :         delete s;
    1106             :     }
    1107           0 :     slots.resize(limit);
    1108           0 :     while(vslots.size())
    1109             :     {
    1110           0 :         VSlot *vs = vslots.back();
    1111           0 :         if(vs->slot != &dummyslot || vs->changed)
    1112             :         {
    1113             :             break;
    1114             :         }
    1115           0 :         delete vslots.back();
    1116           0 :         vslots.pop_back();
    1117             :     }
    1118             : }
    1119             : 
    1120           1 : void materialreset()
    1121             : {
    1122           1 :     if(!(identflags&Idf_Overridden) && !allowediting)
    1123             :     {
    1124           1 :         return;
    1125             :     }
    1126           0 :     defslot = nullptr;
    1127           0 :     for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
    1128             :     {
    1129           0 :         materialslots[i].reset();
    1130             :     }
    1131             : }
    1132             : 
    1133           1 : void decalreset(const int *n)
    1134             : {
    1135           1 :     if(!(identflags&Idf_Overridden) && !allowediting)
    1136             :     {
    1137           1 :         return;
    1138             :     }
    1139           0 :     defslot = nullptr;
    1140           0 :     resetslotshader();
    1141           0 :     for(uint i = *n; i < decalslots.size(); ++i)
    1142             :     {
    1143           0 :         delete decalslots.at(i);
    1144             :     }
    1145           0 :     decalslots.resize(*n);
    1146             : }
    1147             : 
    1148             : static int compactedvslots = 0,
    1149             :            compactvslotsprogress = 0,
    1150             :            clonedvslots = 0;
    1151             : static bool markingvslots = false;
    1152             : 
    1153           0 : void clearslots()
    1154             : {
    1155           0 :     defslot = nullptr;
    1156           0 :     resetslotshader();
    1157           0 :     for(Slot * i : slots)
    1158             :     {
    1159           0 :         delete i;
    1160             :     }
    1161           0 :     slots.clear();
    1162           0 :     for(VSlot * i : vslots)
    1163             :     {
    1164           0 :         delete i;
    1165             :     }
    1166           0 :     vslots.clear();
    1167           0 :     for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
    1168             :     {
    1169           0 :         materialslots[i].reset();
    1170             :     }
    1171           0 :     decalslots.clear();
    1172           0 :     clonedvslots = 0;
    1173           0 : }
    1174             : 
    1175           0 : static void assignvslot(VSlot &vs)
    1176             : {
    1177           0 :     vs.index = compactedvslots++;
    1178           0 : }
    1179             : 
    1180           0 : void compactvslot(int &index)
    1181             : {
    1182           0 :     if(static_cast<long>(vslots.size()) > index)
    1183             :     {
    1184           0 :         VSlot &vs = *vslots[index];
    1185           0 :         if(vs.index < 0)
    1186             :         {
    1187           0 :             assignvslot(vs);
    1188             :         }
    1189           0 :         if(!markingvslots)
    1190             :         {
    1191           0 :             index = vs.index;
    1192             :         }
    1193             :     }
    1194           0 : }
    1195             : 
    1196           0 : void compactvslot(VSlot &vs)
    1197             : {
    1198           0 :     if(vs.index < 0)
    1199             :     {
    1200           0 :         assignvslot(vs);
    1201             :     }
    1202           0 : }
    1203             : 
    1204             : //n will be capped at 8
    1205           0 : void compactvslots(cube * const c, int n)
    1206             : {
    1207           0 :     if((compactvslotsprogress++&0xFFF)==0)
    1208             :     {
    1209           0 :         renderprogress(std::min(static_cast<float>(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots...");
    1210             :     }
    1211           0 :     for(int i = 0; i < std::min(n, 8); ++i)
    1212             :     {
    1213           0 :         if(c[i].children)
    1214             :         {
    1215           0 :             compactvslots(c[i].children->data());
    1216             :         }
    1217             :         else
    1218             :         {
    1219           0 :             for(int j = 0; j < 6; ++j)
    1220             :             {
    1221           0 :                 if(vslots.size() > c[i].texture[j])
    1222             :                 {
    1223           0 :                     VSlot &vs = *vslots[c[i].texture[j]];
    1224           0 :                     if(vs.index < 0)
    1225             :                     {
    1226           0 :                         assignvslot(vs);
    1227             :                     }
    1228           0 :                     if(!markingvslots)
    1229             :                     {
    1230           0 :                         c[i].texture[j] = vs.index;
    1231             :                     }
    1232             :                 }
    1233             :             }
    1234             :         }
    1235             :     }
    1236           0 : }
    1237             : 
    1238           1 : int cubeworld::compactvslots(bool cull)
    1239             : {
    1240           1 :     if(!worldroot)
    1241             :     {
    1242           1 :         conoutf(Console_Error, "no cube to compact");
    1243           1 :         return 0;
    1244             :     }
    1245           0 :     defslot = nullptr;
    1246           0 :     clonedvslots = 0;
    1247           0 :     markingvslots = cull;
    1248           0 :     compactedvslots = 0;
    1249           0 :     compactvslotsprogress = 0;
    1250           0 :     for(uint i = 0; i < vslots.size(); i++)
    1251             :     {
    1252           0 :         vslots[i]->index = -1;
    1253             :     }
    1254           0 :     if(cull)
    1255             :     {
    1256           0 :         uint numdefaults = std::min(static_cast<uint>(Default_NumDefaults), static_cast<uint>(slots.size()));
    1257           0 :         for(uint i = 0; i < numdefaults; ++i)
    1258             :         {
    1259           0 :             slots[i]->variants->index = compactedvslots++;
    1260             :         }
    1261             :     }
    1262             :     else
    1263             :     {
    1264           0 :         for(const Slot *i : slots)
    1265             :         {
    1266           0 :             i->variants->index = compactedvslots++;
    1267             :         }
    1268           0 :         for(const VSlot *i : vslots)
    1269             :         {
    1270           0 :             if(!i->changed && i->index < 0)
    1271             :             {
    1272           0 :                 markingvslots = true;
    1273           0 :                 break;
    1274             :             }
    1275             :         }
    1276             :     }
    1277           0 :     ::compactvslots(worldroot->data());
    1278           0 :     int total = compactedvslots;
    1279           0 :     compacteditvslots();
    1280           0 :     for(VSlot *i : vslots)
    1281             :     {
    1282           0 :         if(i->changed)
    1283             :         {
    1284           0 :             continue;
    1285             :         }
    1286           0 :         while(i->next)
    1287             :         {
    1288           0 :             if(i->next->index < 0)
    1289             :             {
    1290           0 :                 i->next = i->next->next;
    1291             :             }
    1292             :             else
    1293             :             {
    1294           0 :                 i = i->next;
    1295             :             }
    1296             :         }
    1297             :     }
    1298           0 :     if(markingvslots)
    1299             :     {
    1300           0 :         markingvslots = false;
    1301           0 :         compactedvslots = 0;
    1302           0 :         compactvslotsprogress = 0;
    1303           0 :         int lastdiscard = 0;
    1304           0 :         for(uint i = 0; i < vslots.size(); i++)
    1305             :         {
    1306           0 :             VSlot &vs = *vslots[i];
    1307           0 :             if(vs.changed || (vs.index < 0 && !vs.next))
    1308             :             {
    1309           0 :                 vs.index = -1;
    1310             :             }
    1311             :             else
    1312             :             {
    1313           0 :                 if(!cull)
    1314             :                 {
    1315           0 :                     while(lastdiscard < static_cast<int>(i))
    1316             :                     {
    1317           0 :                         VSlot &ds = *vslots[lastdiscard++];
    1318           0 :                         if(!ds.changed && ds.index < 0)
    1319             :                         {
    1320           0 :                             ds.index = compactedvslots++;
    1321             :                         }
    1322             :                     }
    1323             :                 }
    1324           0 :                 vs.index = compactedvslots++;
    1325             :             }
    1326             :         }
    1327           0 :         ::compactvslots(worldroot->data());
    1328           0 :         total = compactedvslots;
    1329           0 :         compacteditvslots();
    1330             :     }
    1331           0 :     compactmruvslots();
    1332           0 :     if(cull)
    1333             :     {
    1334           0 :         for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
    1335             :         {
    1336           0 :             if(slots[i]->variants->index < 0)
    1337             :             {
    1338           0 :                 delete slots.at(i);
    1339           0 :                 slots.erase(slots.begin() + i);
    1340             :             }
    1341             :         }
    1342           0 :         for(uint i = 0; i < slots.size(); i++)
    1343             :         {
    1344           0 :             slots[i]->index = i;
    1345             :         }
    1346             :     }
    1347           0 :     for(uint i = 0; i < vslots.size(); i++)
    1348             :     {
    1349           0 :         while(vslots[i]->index >= 0 && vslots[i]->index != static_cast<int>(i))
    1350             :         {
    1351           0 :             std::swap(vslots[i], vslots[vslots[i]->index]);
    1352             :         }
    1353             :     }
    1354           0 :     for(uint i = compactedvslots; i < vslots.size(); i++)
    1355             :     {
    1356           0 :         delete vslots[i];
    1357             :     }
    1358           0 :     vslots.resize(compactedvslots);
    1359           0 :     return total;
    1360             : }
    1361             : 
    1362           0 : static void clampvslotoffset(VSlot &dst, Slot *slot = nullptr)
    1363             : {
    1364           0 :     if(!slot)
    1365             :     {
    1366           0 :         slot = dst.slot;
    1367             :     }
    1368           0 :     if(slot && slot->sts.size())
    1369             :     {
    1370           0 :         if(!slot->loaded)
    1371             :         {
    1372           0 :             slot->load();
    1373             :         }
    1374           0 :         Texture *t = slot->sts[0].t;
    1375           0 :         int xs = t->xs,
    1376           0 :             ys = t->ys;
    1377           0 :         if(t->type & Texture::MIRROR)
    1378             :         {
    1379           0 :             xs *= 2;
    1380           0 :             ys *= 2;
    1381             :         }
    1382           0 :         if(texrotations[dst.rotation].swapxy)
    1383             :         {
    1384           0 :             std::swap(xs, ys);
    1385             :         }
    1386           0 :         dst.offset.x %= xs;
    1387           0 :         if(dst.offset.x < 0)
    1388             :         {
    1389           0 :             dst.offset.x += xs;
    1390             :         }
    1391           0 :         dst.offset.y %= ys;
    1392           0 :         if(dst.offset.y < 0)
    1393             :         {
    1394           0 :             dst.offset.y += ys;
    1395             :         }
    1396             :     }
    1397             :     else
    1398             :     {
    1399           0 :         dst.offset.max(0);
    1400             :     }
    1401           0 : }
    1402             : 
    1403           0 : static void propagatevslot(VSlot &dst, const VSlot &src, int diff, bool edit = false)
    1404             : {
    1405           0 :     if(diff & (1 << VSlot_ShParam))
    1406             :     {
    1407           0 :         for(uint i = 0; i < src.params.size(); i++)
    1408             :         {
    1409           0 :             dst.params.push_back(src.params[i]);
    1410             :         }
    1411             :     }
    1412           0 :     if(diff & (1 << VSlot_Scale))
    1413             :     {
    1414           0 :         dst.scale = src.scale;
    1415             :     }
    1416           0 :     if(diff & (1 << VSlot_Rotation))
    1417             :     {
    1418           0 :         dst.rotation = src.rotation;
    1419           0 :         if(edit && !dst.offset.iszero())
    1420             :         {
    1421           0 :             clampvslotoffset(dst);
    1422             :         }
    1423             :     }
    1424           0 :     if(diff & (1 << VSlot_Angle))
    1425             :     {
    1426           0 :         dst.angle = src.angle;
    1427             :     }
    1428           0 :     if(diff & (1 << VSlot_Offset))
    1429             :     {
    1430           0 :         dst.offset = src.offset;
    1431           0 :         if(edit)
    1432             :         {
    1433           0 :             clampvslotoffset(dst);
    1434             :         }
    1435             :     }
    1436           0 :     if(diff & (1 << VSlot_Scroll))
    1437             :     {
    1438           0 :         dst.scroll = src.scroll;
    1439             :     }
    1440           0 :     if(diff & (1 << VSlot_Alpha))
    1441             :     {
    1442           0 :         dst.alphafront = src.alphafront;
    1443           0 :         dst.alphaback = src.alphaback;
    1444             :     }
    1445           0 :     if(diff & (1 << VSlot_Color))
    1446             :     {
    1447           0 :         dst.colorscale = src.colorscale;
    1448             :     }
    1449           0 :     if(diff & (1 << VSlot_Refract))
    1450             :     {
    1451           0 :         dst.refractscale = src.refractscale;
    1452           0 :         dst.refractcolor = src.refractcolor;
    1453             :     }
    1454           0 : }
    1455             : 
    1456           0 : static void propagatevslot(const VSlot *root, int changed)
    1457             : {
    1458           0 :     for(VSlot *vs = root->next; vs; vs = vs->next)
    1459             :     {
    1460           0 :         int diff = changed & ~vs->changed;
    1461           0 :         if(diff)
    1462             :         {
    1463           0 :             propagatevslot(*vs, *root, diff);
    1464             :         }
    1465             :     }
    1466           0 : }
    1467             : 
    1468           0 : static void mergevslot(VSlot &dst, const VSlot &src, int diff, Slot *slot = nullptr)
    1469             : {
    1470           0 :     if(diff & (1 << VSlot_ShParam))
    1471             :     {
    1472           0 :         for(uint i = 0; i < src.params.size(); i++)
    1473             :         {
    1474           0 :             const SlotShaderParam &sp = src.params[i];
    1475           0 :             for(uint j = 0; j < dst.params.size(); j++)
    1476             :             {
    1477           0 :                 SlotShaderParam &dp = dst.params[j];
    1478           0 :                 if(sp.name == dp.name)
    1479             :                 {
    1480           0 :                     std::memcpy(dp.val, sp.val, sizeof(dp.val));
    1481           0 :                     goto nextparam; //bail out of loop
    1482             :                 }
    1483             :             }
    1484           0 :             dst.params.push_back(sp);
    1485           0 :         nextparam:;
    1486             :         }
    1487             :     }
    1488           0 :     if(diff & (1 << VSlot_Scale))
    1489             :     {
    1490           0 :         dst.scale = std::clamp(dst.scale*src.scale, 1/8.0f, 8.0f);
    1491             :     }
    1492           0 :     if(diff & (1 << VSlot_Rotation))
    1493             :     {
    1494           0 :         dst.rotation = std::clamp(dst.rotation + src.rotation, 0, 7);
    1495           0 :         if(!dst.offset.iszero())
    1496             :         {
    1497           0 :             clampvslotoffset(dst, slot);
    1498             :         }
    1499             :     }
    1500           0 :     if(diff & (1 << VSlot_Angle))
    1501             :     {
    1502           0 :         dst.angle.add(src.angle);
    1503             :     }
    1504           0 :     if(diff & (1 << VSlot_Offset))
    1505             :     {
    1506           0 :         dst.offset.add(src.offset);
    1507           0 :         clampvslotoffset(dst, slot);
    1508             :     }
    1509           0 :     if(diff & (1 << VSlot_Scroll))
    1510             :     {
    1511           0 :         dst.scroll.add(src.scroll);
    1512             :     }
    1513           0 :     if(diff & (1 << VSlot_Alpha))
    1514             :     {
    1515           0 :         dst.alphafront = src.alphafront;
    1516           0 :         dst.alphaback = src.alphaback;
    1517             :     }
    1518           0 :     if(diff & (1 << VSlot_Color))
    1519             :     {
    1520           0 :         dst.colorscale.mul(src.colorscale);
    1521             :     }
    1522           0 :     if(diff & (1 << VSlot_Refract))
    1523             :     {
    1524           0 :         dst.refractscale *= src.refractscale;
    1525           0 :         dst.refractcolor.mul(src.refractcolor);
    1526             :     }
    1527           0 : }
    1528             : 
    1529           0 : void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta)
    1530             : {
    1531           0 :     dst.changed = src.changed | delta.changed;
    1532           0 :     propagatevslot(dst, src, (1 << VSlot_Num) - 1);
    1533           0 :     mergevslot(dst, delta, delta.changed, src.slot);
    1534           0 : }
    1535             : 
    1536           0 : static VSlot *reassignvslot(Slot &owner, VSlot *vs)
    1537             : {
    1538           0 :     vs->reset();
    1539           0 :     owner.variants = vs;
    1540           0 :     while(vs)
    1541             :     {
    1542           0 :         vs->slot = &owner;
    1543           0 :         vs->linked = false;
    1544           0 :         vs = vs->next;
    1545             :     }
    1546           0 :     return owner.variants;
    1547             : }
    1548             : 
    1549           0 : static VSlot *emptyvslot(Slot &owner)
    1550             : {
    1551           0 :     int offset = 0;
    1552           0 :     for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
    1553             :     {
    1554           0 :         if(slots[i]->variants)
    1555             :         {
    1556           0 :             offset = slots[i]->variants->index + 1;
    1557           0 :             break;
    1558             :         }
    1559             :     }
    1560           0 :     for(uint i = offset; i < vslots.size(); i++)
    1561             :     {
    1562           0 :         if(!vslots[i]->changed)
    1563             :         {
    1564           0 :             return reassignvslot(owner, vslots[i]);
    1565             :         }
    1566             :     }
    1567           0 :     vslots.push_back(new VSlot(&owner, vslots.size()));
    1568           0 :     return vslots.back();
    1569             : }
    1570             : 
    1571           0 : static bool comparevslot(const VSlot &dst, const VSlot &src, int diff)
    1572             : {
    1573           0 :     if(diff & (1 << VSlot_ShParam))
    1574             :     {
    1575           0 :         if(src.params.size() != dst.params.size())
    1576             :         {
    1577           0 :             return false;
    1578             :         }
    1579           0 :         for(uint i = 0; i < src.params.size(); i++)
    1580             :         {
    1581           0 :             const SlotShaderParam &sp = src.params[i], &dp = dst.params[i];
    1582           0 :             if(sp.name != dp.name || std::memcmp(sp.val, dp.val, sizeof(sp.val)))
    1583             :             {
    1584           0 :                 return false;
    1585             :             }
    1586             :         }
    1587             :     }
    1588           0 :     if(diff & (1 << VSlot_Scale)   && dst.scale != src.scale)       return false;
    1589           0 :     if(diff & (1 << VSlot_Rotation)&& dst.rotation != src.rotation) return false;
    1590           0 :     if(diff & (1 << VSlot_Angle)   && dst.angle != src.angle)       return false;
    1591           0 :     if(diff & (1 << VSlot_Offset)  && dst.offset != src.offset)     return false;
    1592           0 :     if(diff & (1 << VSlot_Scroll)  && dst.scroll != src.scroll)     return false;
    1593           0 :     if(diff & (1 << VSlot_Alpha)   && (dst.alphafront != src.alphafront || dst.alphaback != src.alphaback)) return false;
    1594           0 :     if(diff & (1 << VSlot_Color)   && dst.colorscale != src.colorscale) return false;
    1595           0 :     if(diff & (1 << VSlot_Refract) && (dst.refractscale != src.refractscale || dst.refractcolor != src.refractcolor)) return false;
    1596           0 :     return true;
    1597             : }
    1598             : 
    1599           0 : void packvslot(std::vector<uchar> &buf, const VSlot &src)
    1600             : {
    1601           0 :     if(src.changed & (1 << VSlot_ShParam))
    1602             :     {
    1603           0 :         for(uint i = 0; i < src.params.size(); i++)
    1604             :         {
    1605           0 :             const SlotShaderParam &p = src.params[i];
    1606           0 :             buf.push_back(VSlot_ShParam);
    1607           0 :             sendstring(p.name, buf);
    1608           0 :             for(int j = 0; j < 4; ++j)
    1609             :             {
    1610           0 :                 putfloat(buf, p.val[j]);
    1611             :             }
    1612             :         }
    1613             :     }
    1614           0 :     if(src.changed & (1 << VSlot_Scale))
    1615             :     {
    1616           0 :         buf.push_back(VSlot_Scale);
    1617           0 :         putfloat(buf, src.scale);
    1618             :     }
    1619           0 :     if(src.changed & (1 << VSlot_Rotation))
    1620             :     {
    1621           0 :         buf.push_back(VSlot_Rotation);
    1622           0 :         putint(buf, src.rotation);
    1623             :     }
    1624           0 :     if(src.changed & (1 << VSlot_Angle))
    1625             :     {
    1626           0 :         buf.push_back(VSlot_Angle);
    1627           0 :         putfloat(buf, src.angle.x);
    1628           0 :         putfloat(buf, src.angle.y);
    1629           0 :         putfloat(buf, src.angle.z);
    1630             :     }
    1631           0 :     if(src.changed & (1 << VSlot_Offset))
    1632             :     {
    1633           0 :         buf.push_back(VSlot_Offset);
    1634           0 :         putint(buf, src.offset.x);
    1635           0 :         putint(buf, src.offset.y);
    1636             :     }
    1637           0 :     if(src.changed & (1 << VSlot_Scroll))
    1638             :     {
    1639           0 :         buf.push_back(VSlot_Scroll);
    1640           0 :         putfloat(buf, src.scroll.x);
    1641           0 :         putfloat(buf, src.scroll.y);
    1642             :     }
    1643           0 :     if(src.changed & (1 << VSlot_Alpha))
    1644             :     {
    1645           0 :         buf.push_back(VSlot_Alpha);
    1646           0 :         putfloat(buf, src.alphafront);
    1647           0 :         putfloat(buf, src.alphaback);
    1648             :     }
    1649           0 :     if(src.changed & (1 << VSlot_Color))
    1650             :     {
    1651           0 :         buf.push_back(VSlot_Color);
    1652           0 :         putfloat(buf, src.colorscale.r);
    1653           0 :         putfloat(buf, src.colorscale.g);
    1654           0 :         putfloat(buf, src.colorscale.b);
    1655             :     }
    1656           0 :     if(src.changed & (1 << VSlot_Refract))
    1657             :     {
    1658           0 :         buf.push_back(VSlot_Refract);
    1659           0 :         putfloat(buf, src.refractscale);
    1660           0 :         putfloat(buf, src.refractcolor.r);
    1661           0 :         putfloat(buf, src.refractcolor.g);
    1662           0 :         putfloat(buf, src.refractcolor.b);
    1663             :     }
    1664           0 :     buf.push_back(0xFF);
    1665           0 : }
    1666             : 
    1667             : //used in iengine.h
    1668           0 : void packvslot(std::vector<uchar> &buf, int index)
    1669             : {
    1670           0 :     if(static_cast<long>(vslots.size()) > index)
    1671             :     {
    1672           0 :         packvslot(buf, *vslots[index]);
    1673             :     }
    1674             :     else
    1675             :     {
    1676           0 :         buf.push_back(0xFF);
    1677             :     }
    1678           0 : }
    1679             : 
    1680             : //used in iengine.h
    1681           0 : void packvslot(std::vector<uchar> &buf, const VSlot *vs)
    1682             : {
    1683           0 :     if(vs)
    1684             :     {
    1685           0 :         packvslot(buf, *vs);
    1686             :     }
    1687             :     else
    1688             :     {
    1689           0 :         buf.push_back(0xFF);
    1690             :     }
    1691           0 : }
    1692             : 
    1693           0 : bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta)
    1694             : {
    1695           0 :     while(buf.remaining())
    1696             :     {
    1697           0 :         int changed = buf.get();
    1698           0 :         if(changed >= 0x80)
    1699             :         {
    1700           0 :             break;
    1701             :         }
    1702           0 :         switch(changed)
    1703             :         {
    1704           0 :             case VSlot_ShParam:
    1705             :             {
    1706             :                 string name;
    1707           0 :                 getstring(name, buf);
    1708           0 :                 SlotShaderParam p = { name[0] ? getshaderparamname(name) : nullptr, SIZE_MAX, 0, { 0, 0, 0, 0 } };
    1709           0 :                 for(int i = 0; i < 4; ++i)
    1710             :                 {
    1711           0 :                     p.val[i] = getfloat(buf);
    1712             :                 }
    1713           0 :                 if(p.name)
    1714             :                 {
    1715           0 :                     dst.params.push_back(p);
    1716             :                 }
    1717           0 :                 break;
    1718             :             }
    1719           0 :             case VSlot_Scale:
    1720             :             {
    1721           0 :                 dst.scale = getfloat(buf);
    1722           0 :                 if(dst.scale <= 0)
    1723             :                 {
    1724           0 :                     dst.scale = 1;
    1725             :                 }
    1726           0 :                 else if(!delta)
    1727             :                 {
    1728           0 :                     dst.scale = std::clamp(dst.scale, 1/8.0f, 8.0f);
    1729             :                 }
    1730           0 :                 break;
    1731             :             }
    1732           0 :             case VSlot_Rotation:
    1733           0 :                 dst.rotation = getint(buf);
    1734           0 :                 if(!delta)
    1735             :                 {
    1736           0 :                     dst.rotation = std::clamp(dst.rotation, 0, 7);
    1737             :                 }
    1738           0 :                 break;
    1739           0 :             case VSlot_Angle:
    1740             :             {
    1741           0 :                 dst.angle.x = getfloat(buf);
    1742           0 :                 dst.angle.y = getfloat(buf);
    1743           0 :                 dst.angle.z = getfloat(buf);
    1744           0 :                 break;
    1745             :             }
    1746           0 :             case VSlot_Offset:
    1747             :             {
    1748           0 :                 dst.offset.x = getint(buf);
    1749           0 :                 dst.offset.y = getint(buf);
    1750           0 :                 if(!delta)
    1751             :                 {
    1752           0 :                     dst.offset.max(0);
    1753             :                 }
    1754           0 :                 break;
    1755             :             }
    1756           0 :             case VSlot_Scroll:
    1757             :             {
    1758           0 :                 dst.scroll.x = getfloat(buf);
    1759           0 :                 dst.scroll.y = getfloat(buf);
    1760           0 :                 break;
    1761             :             }
    1762           0 :             case VSlot_Alpha:
    1763             :             {
    1764           0 :                 dst.alphafront = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1765           0 :                 dst.alphaback = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1766           0 :                 break;
    1767             :             }
    1768           0 :             case VSlot_Color:
    1769             :             {
    1770           0 :                 dst.colorscale.r = std::clamp(getfloat(buf), 0.0f, 2.0f);
    1771           0 :                 dst.colorscale.g = std::clamp(getfloat(buf), 0.0f, 2.0f);
    1772           0 :                 dst.colorscale.b = std::clamp(getfloat(buf), 0.0f, 2.0f);
    1773           0 :                 break;
    1774             :             }
    1775           0 :             case VSlot_Refract:
    1776             :             {
    1777           0 :                 dst.refractscale = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1778           0 :                 dst.refractcolor.r = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1779           0 :                 dst.refractcolor.g = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1780           0 :                 dst.refractcolor.b = std::clamp(getfloat(buf), 0.0f, 1.0f);
    1781           0 :                 break;
    1782             :             }
    1783           0 :             default:
    1784             :             {
    1785           0 :                 return false;
    1786             :             }
    1787             :         }
    1788           0 :         dst.changed |= 1<<changed;
    1789             :     }
    1790           0 :     if(buf.overread())
    1791             :     {
    1792           0 :         return false;
    1793             :     }
    1794           0 :     return true;
    1795             : }
    1796             : 
    1797           0 : VSlot *findvslot(const Slot &slot, const VSlot &src, const VSlot &delta)
    1798             : {
    1799           0 :     for(VSlot *dst = slot.variants; dst; dst = dst->next)
    1800             :     {
    1801           0 :         if((!dst->changed || dst->changed == (src.changed | delta.changed)) &&
    1802           0 :            comparevslot(*dst, src, src.changed & ~delta.changed) &&
    1803           0 :            comparevslot(*dst, delta, delta.changed))
    1804             :         {
    1805           0 :             return dst;
    1806             :         }
    1807             :     }
    1808           0 :     return nullptr;
    1809             : }
    1810             : 
    1811           0 : static VSlot *clonevslot(const VSlot &src, const VSlot &delta)
    1812             : {
    1813           0 :     vslots.push_back(new VSlot(src.slot, vslots.size()));
    1814           0 :     VSlot *dst = vslots.back();
    1815           0 :     dst->changed = src.changed | delta.changed;
    1816           0 :     propagatevslot(*dst, src, ((1 << VSlot_Num) - 1) & ~delta.changed);
    1817           0 :     propagatevslot(*dst, delta, delta.changed, true);
    1818           0 :     return dst;
    1819             : }
    1820             : 
    1821             : VARP(autocompactvslots, 0, 256, 0x10000);
    1822             : 
    1823           0 : VSlot *editvslot(const VSlot &src, const VSlot &delta)
    1824             : {
    1825           0 :     VSlot *exists = findvslot(*src.slot, src, delta);
    1826           0 :     if(exists)
    1827             :     {
    1828           0 :         return exists;
    1829             :     }
    1830           0 :     if(vslots.size()>=0x10000)
    1831             :     {
    1832           0 :         ::rootworld.compactvslots();
    1833           0 :         rootworld.allchanged();
    1834           0 :         if(vslots.size()>=0x10000)
    1835             :         {
    1836           0 :             return nullptr;
    1837             :         }
    1838             :     }
    1839           0 :     if(autocompactvslots && ++clonedvslots >= autocompactvslots)
    1840             :     {
    1841           0 :         ::rootworld.compactvslots();
    1842           0 :         rootworld.allchanged();
    1843             :     }
    1844           0 :     return clonevslot(src, delta);
    1845             : }
    1846             : 
    1847           0 : static void fixinsidefaces(std::array<cube, 8> &c, const ivec &o, int size, int tex)
    1848             : {
    1849           0 :     for(int i = 0; i < 8; ++i)
    1850             :     {
    1851           0 :         ivec co(i, o, size);
    1852           0 :         if(c[i].children)
    1853             :         {
    1854           0 :             fixinsidefaces(*(c[i].children), co, size>>1, tex);
    1855             :         }
    1856             :         else
    1857             :         {
    1858           0 :             for(int j = 0; j < 6; ++j)
    1859             :             {
    1860           0 :                 if(!visibletris(c[i], j, co, size))
    1861             :                 {
    1862           0 :                     c[i].texture[j] = tex;
    1863             :                 }
    1864             :             }
    1865             :         }
    1866             :     }
    1867           0 : }
    1868             : 
    1869           1 : int findslottex(const char *name)
    1870             : {
    1871             : 
    1872             :     const struct slottex
    1873             :     {
    1874             :         const char *name;
    1875             :         int id;
    1876           1 :     } slottexs[] =
    1877             :     {
    1878             :         {"0", Tex_Diffuse},
    1879             :         {"1", Tex_Unknown},
    1880             : 
    1881             :         {"c", Tex_Diffuse},
    1882             :         {"u", Tex_Unknown},
    1883             :         {"n", Tex_Normal},
    1884             :         {"g", Tex_Glow},
    1885             :         {"s", Tex_Spec},
    1886             :         {"z", Tex_Depth},
    1887             :         {"a", Tex_Alpha}
    1888             :     };
    1889             : 
    1890          10 :     for(int i = 0; i < static_cast<int>(sizeof(slottexs)/sizeof(slottex)); ++i)
    1891             :     {
    1892           9 :         if(!std::strcmp(slottexs[i].name, name))
    1893             :         {
    1894           0 :             return slottexs[i].id;
    1895             :         }
    1896             :     }
    1897           1 :     return -1;
    1898             : }
    1899             : 
    1900           1 : static void texture(char *type, char *name, int *rot, int *xoffset, int *yoffset, float *scale)
    1901             : {
    1902           1 :     int tnum = findslottex(type), matslot;
    1903           1 :     if(tnum == Tex_Diffuse)
    1904             :     {
    1905           0 :         if(slots.size() >= 0x10000)
    1906             :         {
    1907           0 :             return;
    1908             :         }
    1909           0 :         slots.push_back(new Slot(slots.size()));
    1910           0 :         defslot = slots.back();
    1911             :     }
    1912           1 :     else if(!std::strcmp(type, "decal"))
    1913             :     {
    1914           0 :         if(decalslots.size() >= 0x10000)
    1915             :         {
    1916           0 :             return;
    1917             :         }
    1918           0 :         tnum = Tex_Diffuse;
    1919           0 :         decalslots.push_back(new DecalSlot(decalslots.size()));
    1920           0 :         defslot = decalslots.back();
    1921             :     }
    1922           1 :     else if((matslot = findmaterial(type)) >= 0)
    1923             :     {
    1924           0 :         tnum = Tex_Diffuse;
    1925           0 :         defslot = &materialslots[matslot];
    1926           0 :         defslot->reset();
    1927             :     }
    1928           1 :     else if(!defslot)
    1929             :     {
    1930           1 :         return;
    1931             :     }
    1932           0 :     else if(tnum < 0)
    1933             :     {
    1934           0 :         tnum = Tex_Unknown;
    1935             :     }
    1936           0 :     Slot &s = *defslot;
    1937           0 :     s.loaded = false;
    1938           0 :     s.texmask |= 1<<tnum;
    1939           0 :     if(s.sts.size()>=8)
    1940             :     {
    1941           0 :         conoutf(Console_Warn, "warning: too many textures in %s", s.name());
    1942             :     }
    1943           0 :     s.sts.emplace_back();
    1944           0 :     Slot::Tex &st = s.sts.back();
    1945           0 :     st.type = tnum;
    1946           0 :     copystring(st.name, name);
    1947           0 :     path(st.name);
    1948           0 :     if(tnum == Tex_Diffuse)
    1949             :     {
    1950           0 :         setslotshader(s);
    1951           0 :         VSlot &vs = s.emptyvslot();
    1952           0 :         vs.rotation = std::clamp(*rot, 0, 7);
    1953           0 :         vs.offset = ivec2(*xoffset, *yoffset).max(0);
    1954           0 :         vs.scale = *scale <= 0 ? 1 : *scale;
    1955           0 :         propagatevslot(&vs, (1 << VSlot_Num) - 1);
    1956             :     }
    1957             : }
    1958             : 
    1959           1 : void texgrass(char *name)
    1960             : {
    1961           1 :     if(!defslot)
    1962             :     {
    1963           1 :         return;
    1964             :     }
    1965           0 :     Slot &s = *defslot;
    1966           0 :     delete[] s.grass;
    1967           0 :     s.grass = name[0] ? newstring(makerelpath("media/texture", name)) : nullptr;
    1968             : }
    1969             : 
    1970           1 : void texscroll(float *scrollS, float *scrollT)
    1971             : {
    1972           1 :     if(!defslot)
    1973             :     {
    1974           1 :         return;
    1975             :     }
    1976           0 :     Slot &s = *defslot;
    1977           0 :     s.variants->scroll = vec2(*scrollS/1000.0f, *scrollT/1000.0f);
    1978           0 :     propagatevslot(s.variants, 1 << VSlot_Scroll);
    1979             : }
    1980             : 
    1981           1 : void texoffset_(int *xoffset, int *yoffset)
    1982             : {
    1983           1 :     if(!defslot)
    1984             :     {
    1985           1 :         return;
    1986             :     }
    1987           0 :     Slot &s = *defslot;
    1988           0 :     s.variants->offset = ivec2(*xoffset, *yoffset).max(0);
    1989           0 :     propagatevslot(s.variants, 1 << VSlot_Offset);
    1990             : }
    1991             : 
    1992           1 : void texrotate_(int *rot)
    1993             : {
    1994           1 :     if(!defslot)
    1995             :     {
    1996           1 :         return;
    1997             :     }
    1998           0 :     Slot &s = *defslot;
    1999           0 :     s.variants->rotation = std::clamp(*rot, 0, 7);
    2000           0 :     propagatevslot(s.variants, 1 << VSlot_Rotation);
    2001             : }
    2002             : 
    2003           1 : void texangle_(float *a)
    2004             : {
    2005           1 :     if(!defslot)
    2006             :     {
    2007           1 :         return;
    2008             :     }
    2009           0 :     Slot &s = *defslot;
    2010           0 :     s.variants->angle = vec(*a, std::sin(*a/RAD), std::cos(*a/RAD));
    2011           0 :     propagatevslot(s.variants, 1 << VSlot_Angle);
    2012             : }
    2013             : 
    2014           1 : void texscale(float *scale)
    2015             : {
    2016           1 :     if(!defslot)
    2017             :     {
    2018           1 :         return;
    2019             :     }
    2020           0 :     Slot &s = *defslot;
    2021           0 :     s.variants->scale = *scale <= 0 ? 1 : *scale;
    2022           0 :     propagatevslot(s.variants, 1 << VSlot_Scale);
    2023             : }
    2024             : 
    2025           1 : void texalpha(float *front, float *back)
    2026             : {
    2027           1 :     if(!defslot)
    2028             :     {
    2029           1 :         return;
    2030             :     }
    2031           0 :     Slot &s = *defslot;
    2032           0 :     s.variants->alphafront = std::clamp(*front, 0.0f, 1.0f);
    2033           0 :     s.variants->alphaback = std::clamp(*back, 0.0f, 1.0f);
    2034           0 :     propagatevslot(s.variants, 1 << VSlot_Alpha);
    2035             : }
    2036             : 
    2037           1 : void texcolor(float *r, float *g, float *b)
    2038             : {
    2039           1 :     if(!defslot)
    2040             :     {
    2041           1 :         return;
    2042             :     }
    2043           0 :     Slot &s = *defslot;
    2044           0 :     s.variants->colorscale = vec(std::clamp(*r, 0.0f, 2.0f), std::clamp(*g, 0.0f, 2.0f), std::clamp(*b, 0.0f, 2.0f));
    2045           0 :     propagatevslot(s.variants, 1 << VSlot_Color);
    2046             : }
    2047             : 
    2048           1 : void texrefract(float *k, float *r, float *g, float *b)
    2049             : {
    2050           1 :     if(!defslot)
    2051             :     {
    2052           1 :         return;
    2053             :     }
    2054           0 :     Slot &s = *defslot;
    2055           0 :     s.variants->refractscale = std::clamp(*k, 0.0f, 1.0f);
    2056           0 :     if(s.variants->refractscale > 0 && (*r > 0 || *g > 0 || *b > 0))
    2057             :     {
    2058           0 :         s.variants->refractcolor = vec(std::clamp(*r, 0.0f, 1.0f), std::clamp(*g, 0.0f, 1.0f), std::clamp(*b, 0.0f, 1.0f));
    2059             :     }
    2060             :     else
    2061             :     {
    2062           0 :         s.variants->refractcolor = vec(1, 1, 1);
    2063             :     }
    2064           0 :     propagatevslot(s.variants, 1 << VSlot_Refract);
    2065             : }
    2066             : 
    2067           1 : void texsmooth(int *id, int *angle)
    2068             : {
    2069           1 :     if(!defslot)
    2070             :     {
    2071           1 :         return;
    2072             :     }
    2073           0 :     Slot &s = *defslot;
    2074           0 :     s.smooth = smoothangle(*id, *angle);
    2075             : }
    2076             : 
    2077           1 : void decaldepth(float *depth, float *fade)
    2078             : {
    2079           1 :     if(!defslot || defslot->type() != Slot::SlotType_Decal)
    2080             :     {
    2081           1 :         return;
    2082             :     }
    2083           0 :     DecalSlot &s = *static_cast<DecalSlot *>(defslot);
    2084           0 :     s.depth = std::clamp(*depth, 1e-3f, 1e3f);
    2085           0 :     s.fade = std::clamp(*fade, 0.0f, s.depth);
    2086             : }
    2087             : 
    2088           0 : int DecalSlot::cancombine(int type) const
    2089             : {
    2090           0 :     switch(type)
    2091             :     {
    2092           0 :         case Tex_Glow:
    2093             :         {
    2094           0 :             return Tex_Spec;
    2095             :         }
    2096           0 :         case Tex_Normal:
    2097             :         {
    2098           0 :             return texmask&(1 << Tex_Depth) ? Tex_Depth : (texmask & (1 << Tex_Glow) ? -1 : Tex_Spec);
    2099             :         }
    2100           0 :         default:
    2101             :         {
    2102           0 :             return -1;
    2103             :         }
    2104             :     }
    2105             : }
    2106             : 
    2107           0 : bool DecalSlot::shouldpremul(int type) const
    2108             : {
    2109           0 :     switch(type)
    2110             :     {
    2111           0 :         case Tex_Diffuse:
    2112             :         {
    2113           0 :             return true;
    2114             :         }
    2115           0 :         default:
    2116             :         {
    2117           0 :             return false;
    2118             :         }
    2119             :     }
    2120             : }
    2121             : 
    2122           0 : static void addname(std::vector<char> &key, Slot &slot, Slot::Tex &t, bool combined = false, const char *prefix = nullptr)
    2123             : {
    2124           0 :     if(combined)
    2125             :     {
    2126           0 :         key.push_back('&');
    2127             :     }
    2128           0 :     if(prefix)
    2129             :     {
    2130           0 :         while(*prefix)
    2131             :         {
    2132           0 :             key.push_back(*prefix++);
    2133             :         }
    2134             :     }
    2135           0 :     DEF_FORMAT_STRING(tname, "%s/%s", slot.texturedir(), t.name);
    2136           0 :     for(const char *s = path(tname); *s; key.push_back(*s++))
    2137             :     {
    2138             :         //(empty body)
    2139             :     }
    2140           0 : }
    2141             : 
    2142             : // Slot object
    2143             : 
    2144           0 : VSlot &Slot::emptyvslot()
    2145             : {
    2146           0 :     return *::emptyvslot(*this);
    2147             : }
    2148             : 
    2149           0 : int Slot::findtextype(int type, int last) const
    2150             : {
    2151           0 :     for(uint i = last+1; i<sts.size(); i++)
    2152             :     {
    2153           0 :         if((type&(1<<sts[i].type)) && sts[i].combined<0)
    2154             :         {
    2155           0 :             return i;
    2156             :         }
    2157             :     }
    2158           0 :     return -1;
    2159             : }
    2160             : 
    2161           0 : int Slot::cancombine(int type) const
    2162             : {
    2163           0 :     switch(type)
    2164             :     {
    2165           0 :         case Tex_Diffuse:
    2166             :         {
    2167           0 :             return texmask&((1 << Tex_Spec) | (1 << Tex_Normal)) ? Tex_Spec : Tex_Alpha;
    2168             :         }
    2169           0 :         case Tex_Normal:
    2170             :         {
    2171           0 :             return texmask&(1 << Tex_Depth) ? Tex_Depth : Tex_Alpha;
    2172             :         }
    2173           0 :         default:
    2174             :         {
    2175           0 :             return -1;
    2176             :         }
    2177             :     }
    2178             : }
    2179             : 
    2180           0 : void Slot::load(int index, Slot::Tex &t)
    2181             : {
    2182           0 :     std::vector<char> key;
    2183           0 :     addname(key, *this, t, false, shouldpremul(t.type) ? "<premul>" : nullptr);
    2184           0 :     Slot::Tex *combine = nullptr;
    2185           0 :     for(uint i = 0; i < sts.size(); i++)
    2186             :     {
    2187           0 :         Slot::Tex &c = sts[i];
    2188           0 :         if(c.combined == index)
    2189             :         {
    2190           0 :             combine = &c;
    2191           0 :             addname(key, *this, c, true);
    2192           0 :             break;
    2193             :         }
    2194             :     }
    2195           0 :     key.push_back('\0');
    2196           0 :     auto itr = textures.find(key.data());
    2197           0 :     if(itr != textures.end())
    2198             :     {
    2199           0 :         t.t = &(*itr).second;
    2200           0 :         return;
    2201             :     }
    2202           0 :     t.t = nullptr;
    2203           0 :     int compress = 0,
    2204           0 :         wrap = 0;
    2205           0 :     ImageData ts;
    2206           0 :     if(!ts.texturedata(*this, t, true, &compress, &wrap))
    2207             :     {
    2208           0 :         t.t = notexture;
    2209           0 :         return;
    2210             :     }
    2211           0 :     if(!ts.compressed)
    2212             :     {
    2213           0 :         switch(t.type)
    2214             :         {
    2215           0 :             case Tex_Spec:
    2216             :             {
    2217           0 :                 if(ts.bpp > 1)
    2218             :                 {
    2219           0 :                     ts.collapsespec();
    2220             :                 }
    2221           0 :                 break;
    2222             :             }
    2223           0 :             case Tex_Glow:
    2224             :             case Tex_Diffuse:
    2225             :             case Tex_Normal:
    2226           0 :                 if(combine)
    2227             :                 {
    2228           0 :                     ImageData cs;
    2229           0 :                     if(cs.texturedata(*this, *combine))
    2230             :                     {
    2231           0 :                         if(cs.w!=ts.w || cs.h!=ts.h)
    2232             :                         {
    2233           0 :                             cs.scaleimage(ts.w, ts.h);
    2234             :                         }
    2235           0 :                         switch(combine->type)
    2236             :                         {
    2237           0 :                             case Tex_Spec:
    2238             :                             {
    2239           0 :                                 ts.mergespec(cs);
    2240           0 :                                 break;
    2241             :                             }
    2242           0 :                             case Tex_Depth:
    2243             :                             {
    2244           0 :                                 ts.mergedepth(cs);
    2245           0 :                                 break;
    2246             :                             }
    2247           0 :                             case Tex_Alpha:
    2248             :                             {
    2249           0 :                                 ts.mergealpha(cs);
    2250           0 :                                 break;
    2251             :                             }
    2252             :                         }
    2253             :                     }
    2254           0 :                 }
    2255           0 :                 if(ts.bpp < 3)
    2256             :                 {
    2257           0 :                     ts.swizzleimage();
    2258             :                 }
    2259           0 :                 break;
    2260             :         }
    2261             :     }
    2262           0 :     if(!ts.compressed && shouldpremul(t.type))
    2263             :     {
    2264           0 :         ts.texpremul();
    2265             :     }
    2266           0 :     t.t = newtexture(nullptr, key.data(), ts, wrap, true, true, true, compress);
    2267           0 : }
    2268             : 
    2269           0 : void Slot::load()
    2270             : {
    2271           0 :     linkslotshader(*this);
    2272           0 :     for(uint i = 0; i < sts.size(); i++)
    2273             :     {
    2274           0 :         Slot::Tex &t = sts[i];
    2275           0 :         if(t.combined >= 0)
    2276             :         {
    2277           0 :             continue;
    2278             :         }
    2279           0 :         int combine = cancombine(t.type);
    2280           0 :         if(combine >= 0 && (combine = findtextype(1<<combine)) >= 0)
    2281             :         {
    2282           0 :             Slot::Tex &c = sts[combine];
    2283           0 :             c.combined = i;
    2284             :         }
    2285             :     }
    2286           0 :     for(uint i = 0; i < sts.size(); i++)
    2287             :     {
    2288           0 :         Slot::Tex &t = sts[i];
    2289           0 :         if(t.combined >= 0)
    2290             :         {
    2291           0 :             continue;
    2292             :         }
    2293           0 :         switch(t.type)
    2294             :         {
    2295             :             default:
    2296             :             {
    2297           0 :                 load(i, t);
    2298           0 :                 break;
    2299             :             }
    2300             :         }
    2301             :     }
    2302           0 :     loaded = true;
    2303           0 : }
    2304             : 
    2305             : // VSlot
    2306             : 
    2307          40 : void VSlot::addvariant(Slot *slot)
    2308             : {
    2309          40 :     if(!slot->variants)
    2310             :     {
    2311          40 :         slot->variants = this;
    2312             :     }
    2313             :     else
    2314             :     {
    2315           0 :         VSlot *prev = slot->variants;
    2316           0 :         while(prev->next)
    2317             :         {
    2318           0 :             prev = prev->next;
    2319             :         }
    2320           0 :         prev->next = this;
    2321             :     }
    2322          40 : }
    2323             : 
    2324           0 : bool VSlot::isdynamic() const
    2325             : {
    2326           0 :     return !scroll.iszero() || slot->shader->isdynamic();
    2327             : }
    2328             : 
    2329             : // end of Slot/VSlot
    2330           0 : MatSlot &lookupmaterialslot(int index, bool load)
    2331             : {
    2332           0 :     MatSlot &s = materialslots[index];
    2333           0 :     if(load && !s.linked)
    2334             :     {
    2335           0 :         if(!s.loaded)
    2336             :         {
    2337           0 :             s.load();
    2338             :         }
    2339           0 :         linkvslotshader(s);
    2340           0 :         s.linked = true;
    2341             :     }
    2342           0 :     return s;
    2343             : }
    2344             : 
    2345           0 : Slot &lookupslot(int index, bool load)
    2346             : {
    2347           0 :     Slot &s = (static_cast<long>(slots.size()) > index) ? *slots[index] : ((slots.size() > Default_Geom) ? *slots[Default_Geom] : dummyslot);
    2348           0 :     if(!s.loaded && load)
    2349             :     {
    2350           0 :         s.load();
    2351             :     }
    2352           0 :     return s;
    2353             : }
    2354             : 
    2355           0 : VSlot &lookupvslot(int index, bool load)
    2356             : {
    2357           0 :     VSlot &s = (static_cast<long>(vslots.size()) > index) && vslots[index]->slot ? *vslots[index] : ((slots.size() > Default_Geom) && slots[Default_Geom]->variants ? *slots[Default_Geom]->variants : dummyvslot);
    2358           0 :     if(load && !s.linked)
    2359             :     {
    2360           0 :         if(!s.slot->loaded)
    2361             :         {
    2362           0 :             s.slot->load();
    2363             :         }
    2364           0 :         linkvslotshader(s);
    2365           0 :         s.linked = true;
    2366             :     }
    2367           0 :     return s;
    2368             : }
    2369             : 
    2370           0 : DecalSlot &lookupdecalslot(int index, bool load)
    2371             : {
    2372           0 :     DecalSlot &s = (static_cast<int>(decalslots.size()) > index) ? *decalslots[index] : dummydecalslot;
    2373           0 :     if(load && !s.linked)
    2374             :     {
    2375           0 :         if(!s.loaded)
    2376             :         {
    2377           0 :             s.load();
    2378             :         }
    2379           0 :         linkvslotshader(s);
    2380           0 :         s.linked = true;
    2381             :     }
    2382           0 :     return s;
    2383             : }
    2384             : 
    2385           0 : void linkslotshaders()
    2386             : {
    2387           0 :     for(Slot * const &i : slots)
    2388             :     {
    2389           0 :         if(i->loaded)
    2390             :         {
    2391           0 :             linkslotshader(*i);
    2392             :         }
    2393             :     }
    2394           0 :     for(VSlot * const &i : vslots)
    2395             :     {
    2396           0 :         if(i->linked)
    2397             :         {
    2398           0 :             linkvslotshader(*i);
    2399             :         }
    2400             :     }
    2401           0 :     for(uint i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
    2402             :     {
    2403           0 :         if(materialslots[i].loaded)
    2404             :         {
    2405           0 :             linkslotshader(materialslots[i]);
    2406           0 :             linkvslotshader(materialslots[i]);
    2407             :         }
    2408             :     }
    2409           0 :     for(DecalSlot * const &i : decalslots)
    2410             :     {
    2411           0 :         if(i->loaded)
    2412             :         {
    2413           0 :             linkslotshader(*i);
    2414           0 :             linkvslotshader(*i);
    2415             :         }
    2416             :     }
    2417           0 : }
    2418             : 
    2419           0 : static void blitthumbnail(ImageData &d, ImageData &s, int x, int y)
    2420             : {
    2421           0 :     d.forcergbimage();
    2422           0 :     s.forcergbimage();
    2423           0 :     uchar *dstrow = &d.data[d.pitch*y + d.bpp*x],
    2424           0 :           *srcrow = s.data;
    2425           0 :     for(int y = 0; y < s.h; ++y)
    2426             :     {
    2427           0 :         for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += d.bpp, src += s.bpp)
    2428             :         {
    2429           0 :             for(int k = 0; k < 3; ++k)
    2430             :             {
    2431           0 :                 dst[k] = src[k];
    2432             :             }
    2433             :         }
    2434           0 :         dstrow += d.pitch;
    2435           0 :         srcrow += s.pitch;
    2436             :     }
    2437           0 : }
    2438             : 
    2439           0 : Texture *Slot::loadthumbnail()
    2440             : {
    2441           0 :     if(thumbnail)
    2442             :     {
    2443           0 :         return thumbnail;
    2444             :     }
    2445           0 :     if(!variants)
    2446             :     {
    2447           0 :         thumbnail = notexture;
    2448           0 :         return thumbnail;
    2449             :     }
    2450           0 :     VSlot &vslot = *variants;
    2451           0 :     linkslotshader(*this, false);
    2452           0 :     linkvslotshader(vslot, false);
    2453           0 :     std::vector<char> name;
    2454           0 :     if(vslot.colorscale == vec(1, 1, 1))
    2455             :     {
    2456           0 :         addname(name, *this, sts[0], false, "<thumbnail>");
    2457             :     }
    2458             :     else
    2459             :     {
    2460           0 :         DEF_FORMAT_STRING(prefix, "<thumbnail:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
    2461           0 :         addname(name, *this, sts[0], false, prefix);
    2462             :     }
    2463           0 :     int glow = -1;
    2464           0 :     if(texmask&(1 << Tex_Glow))
    2465             :     {
    2466           0 :         for(uint j = 0; j < sts.size(); j++)
    2467             :         {
    2468           0 :             if(sts[j].type == Tex_Glow)
    2469             :             {
    2470           0 :                 glow = j;
    2471           0 :                 break;
    2472             :             }
    2473             :         }
    2474           0 :         if(glow >= 0)
    2475             :         {
    2476           0 :             DEF_FORMAT_STRING(prefix, "<glow:%.2f/%.2f/%.2f>", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z);
    2477           0 :             addname(name, *this, sts[glow], true, prefix);
    2478             :         }
    2479             :     }
    2480           0 :     name.push_back('\0');
    2481           0 :     auto itr = textures.find(path(name.data()));
    2482           0 :     if(itr != textures.end())
    2483             :     {
    2484           0 :         thumbnail = &(*itr).second;
    2485           0 :         return &(*itr).second;
    2486             :     }
    2487             :     else
    2488             :     {
    2489           0 :         auto insert = textures.insert( { std::string(name.data()), Texture() } ).first;
    2490           0 :         Texture *t = &(*insert).second;
    2491           0 :         ImageData s, g, l, d;
    2492           0 :         s.texturedata(*this, sts[0], false);
    2493           0 :         if(glow >= 0)
    2494             :         {
    2495           0 :             g.texturedata(*this, sts[glow], false);
    2496             :         }
    2497           0 :         if(!s.data)
    2498             :         {
    2499           0 :             t = thumbnail = notexture;
    2500             :         }
    2501             :         else
    2502             :         {
    2503           0 :             if(vslot.colorscale != vec(1, 1, 1))
    2504             :             {
    2505           0 :                 s.texmad(vslot.colorscale, vec(0, 0, 0));
    2506             :             }
    2507           0 :             int xs = s.w, ys = s.h;
    2508           0 :             if(s.w > 128 || s.h > 128)
    2509             :             {
    2510           0 :                 s.scaleimage(std::min(s.w, 128), std::min(s.h, 128));
    2511             :             }
    2512           0 :             if(g.data)
    2513             :             {
    2514           0 :                 if(g.w != s.w || g.h != s.h) g.scaleimage(s.w, s.h);
    2515           0 :                 s.addglow(g, vslot.glowcolor);
    2516             :             }
    2517           0 :             if(l.data)
    2518             :             {
    2519           0 :                 if(l.w != s.w/2 || l.h != s.h/2)
    2520             :                 {
    2521           0 :                     l.scaleimage(s.w/2, s.h/2);
    2522             :                 }
    2523           0 :                 blitthumbnail(s, l, s.w-l.w, s.h-l.h);
    2524             :             }
    2525           0 :             if(d.data)
    2526             :             {
    2527           0 :                 if(vslot.colorscale != vec(1, 1, 1))
    2528             :                 {
    2529           0 :                     d.texmad(vslot.colorscale, vec(0, 0, 0));
    2530             :                 }
    2531           0 :                 if(d.w != s.w/2 || d.h != s.h/2)
    2532             :                 {
    2533           0 :                     d.scaleimage(s.w/2, s.h/2);
    2534             :                 }
    2535           0 :                 blitthumbnail(s, d, 0, 0);
    2536             :             }
    2537           0 :             if(s.bpp < 3)
    2538             :             {
    2539           0 :                 s.forcergbimage();
    2540             :             }
    2541           0 :             t = newtexture(nullptr, name.data(), s, 0, false, false, true);
    2542           0 :             t->xs = xs;
    2543           0 :             t->ys = ys;
    2544           0 :             thumbnail = t;
    2545             :         }
    2546           0 :         return t;
    2547           0 :     }
    2548           0 : }
    2549             : 
    2550             : // environment mapped reflections
    2551             : 
    2552           0 : void cleanuptextures()
    2553             : {
    2554           0 :     for(Slot * const &i : slots)
    2555             :     {
    2556           0 :         i->cleanup();
    2557             :     }
    2558           0 :     for(VSlot * const &i : vslots)
    2559             :     {
    2560           0 :         i->cleanup();
    2561             :     }
    2562           0 :     for(uint i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
    2563             :     {
    2564           0 :         materialslots[i].cleanup();
    2565             :     }
    2566           0 :     for(DecalSlot * const &i : decalslots)
    2567             :     {
    2568           0 :         i->cleanup();
    2569             :     }
    2570           0 :     for(auto itr = textures.begin(); itr != textures.end(); ++itr)
    2571             :     {
    2572           0 :         Texture &t = (*itr).second;
    2573           0 :         delete[] t.alphamask;
    2574           0 :         t.alphamask = nullptr;
    2575           0 :         if(t.id)
    2576             :         {
    2577           0 :             glDeleteTextures(1, &t.id);
    2578           0 :             t.id = 0;
    2579             :         }
    2580           0 :         if(t.type&Texture::TRANSIENT)
    2581             :         {
    2582           0 :             itr = textures.erase(itr);
    2583             :         }
    2584             :     }
    2585           0 : }
    2586             : 
    2587           0 : bool reloadtexture(const char *name)
    2588             : {
    2589           0 :     auto itr = textures.find(path(std::string(name)));
    2590           0 :     if(itr != textures.end())
    2591             :     {
    2592           0 :         return (*itr).second.reload();
    2593             :     }
    2594           0 :     return true;
    2595             : }
    2596             : 
    2597           0 : bool Texture::reload()
    2598             : {
    2599           0 :     if(id)
    2600             :     {
    2601           0 :         return true;
    2602             :     }
    2603           0 :     switch(type&TYPE)
    2604             :     {
    2605           0 :         case IMAGE:
    2606             :         {
    2607           0 :             int compress = 0;
    2608           0 :             ImageData s;
    2609           0 :             if(!s.texturedata(name, true, &compress) || !newtexture(this, nullptr, s, clamp, mipmap, false, false, compress))
    2610             :             {
    2611           0 :                 return false;
    2612             :             }
    2613           0 :             break;
    2614           0 :         }
    2615             :     }
    2616           0 :     return true;
    2617             : }
    2618             : 
    2619           1 : void reloadtex(char *name)
    2620             : {
    2621           1 :     auto itr = textures.find(path(std::string(name)));
    2622           1 :     if(itr == textures.end())
    2623             :     {
    2624           1 :         conoutf(Console_Error, "texture %s is not loaded", name);
    2625           1 :         return;
    2626             :     }
    2627           0 :     Texture *t = &(*itr).second;
    2628           0 :     if(t->type&Texture::TRANSIENT)
    2629             :     {
    2630           0 :         conoutf(Console_Error, "can't reload transient texture %s", name);
    2631           0 :         return;
    2632             :     }
    2633           0 :     delete[] t->alphamask;
    2634           0 :     t->alphamask = nullptr;
    2635           0 :     Texture oldtex = *t;
    2636           0 :     t->id = 0;
    2637           0 :     if(!t->reload())
    2638             :     {
    2639           0 :         if(t->id)
    2640             :         {
    2641           0 :             glDeleteTextures(1, &t->id);
    2642             :         }
    2643           0 :         *t = oldtex;
    2644           0 :         conoutf(Console_Error, "failed to reload texture %s", name);
    2645             :     }
    2646             : }
    2647             : 
    2648           0 : void reloadtextures()
    2649             : {
    2650           0 :     int reloaded = 0;
    2651           0 :     for(auto &[k, t] : textures)
    2652             :     {
    2653           0 :         loadprogress = static_cast<float>(++reloaded)/textures.size();
    2654           0 :         t.reload();
    2655             :     }
    2656           0 :     loadprogress = 0;
    2657           0 : }
    2658             : 
    2659           0 : static void writepngchunk(stream *f, const char *type, const uchar *data = nullptr, uint len = 0)
    2660             : {
    2661           0 :     f->putbig<uint>(len);
    2662           0 :     f->write(type, 4);
    2663           0 :     f->write(data, len);
    2664             : 
    2665           0 :     uint crc = crc32(0, Z_NULL, 0);
    2666           0 :     crc = crc32(crc, reinterpret_cast<const Bytef *>(type), 4);
    2667           0 :     if(data)
    2668             :     {
    2669           0 :         crc = crc32(crc, data, len);
    2670             :     }
    2671           0 :     f->putbig<uint>(crc);
    2672           0 : }
    2673             : 
    2674             : VARP(compresspng, 0, 9, 9);
    2675             : 
    2676           0 : static void flushzip(z_stream &z, uchar *buf, const uint &buflen, uint &len, stream *f, uint &crc)
    2677             : {
    2678           0 :     int flush = buflen- z.avail_out;
    2679           0 :     crc = crc32(crc, buf, flush);
    2680           0 :     len += flush;
    2681           0 :     f->write(buf, flush);
    2682           0 :     z.next_out = static_cast<Bytef *>(buf);
    2683           0 :     z.avail_out = buflen;
    2684           0 : }
    2685             : 
    2686           1 : static void savepng(const char *filename, const ImageData &image, bool flip)
    2687             : {
    2688           1 :     if(!image.h || !image.w)
    2689             :     {
    2690           1 :         conoutf(Console_Error, "cannot save 0-size png");
    2691           1 :         return;
    2692             :     }
    2693           0 :     uchar ctype = 0;
    2694           0 :     switch(image.bpp)
    2695             :     {
    2696           0 :         case 1:
    2697             :         {
    2698           0 :             ctype = 0;
    2699           0 :             break;
    2700             :         }
    2701           0 :         case 2:
    2702             :         {
    2703           0 :             ctype = 4;
    2704           0 :             break;
    2705             :         }
    2706           0 :         case 3:
    2707             :         {
    2708           0 :             ctype = 2;
    2709           0 :             break;
    2710             :         }
    2711           0 :         case 4:
    2712             :         {
    2713           0 :             ctype = 6;
    2714           0 :             break;
    2715             :         }
    2716           0 :         default:
    2717             :         {
    2718           0 :             conoutf(Console_Error, "failed saving png to %s", filename);
    2719           0 :             return;
    2720             :         }
    2721             :     }
    2722           0 :     stream *f = openfile(filename, "wb");
    2723           0 :     if(!f)
    2724             :     {
    2725           0 :         conoutf(Console_Error, "could not write to %s", filename);
    2726           0 :         return;
    2727             :     }
    2728           0 :     uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
    2729           0 :     f->write(signature, sizeof(signature));
    2730             :     struct pngihdr
    2731             :     {
    2732             :         uint width,
    2733             :              height;
    2734             :         uchar bitdepth,
    2735             :               colortype,
    2736             :               compress,
    2737             :               filter,
    2738             :               interlace;
    2739             :     };
    2740           0 :     pngihdr ihdr =
    2741             :     {
    2742           0 :         static_cast<uint>(endianswap(image.w)),
    2743           0 :         static_cast<uint>(endianswap(image.h)),
    2744             :         8,
    2745             :         ctype,
    2746             :         0,
    2747             :         0,
    2748             :         0
    2749           0 :     };
    2750           0 :     writepngchunk(f, "IHDR", reinterpret_cast<uchar *>(&ihdr), 13);
    2751           0 :     stream::offset idat = f->tell();
    2752           0 :     uint len = 0;
    2753           0 :     f->write("\0\0\0\0IDAT", 8);
    2754           0 :     uint crc = crc32(0, Z_NULL, 0);
    2755           0 :     crc = crc32(crc, reinterpret_cast<const Bytef *>("IDAT"), 4);
    2756             :     z_stream z;
    2757           0 :     z.zalloc = nullptr;
    2758           0 :     z.zfree = nullptr;
    2759           0 :     z.opaque = nullptr;
    2760           0 :     if(deflateInit(&z, compresspng) != Z_OK)
    2761             :     {
    2762           0 :         goto error; //goto is beneath FLUSHZ macro
    2763             :     }
    2764             :     uchar buf[1<<12];
    2765           0 :     z.next_out = static_cast<Bytef *>(buf);
    2766           0 :     z.avail_out = sizeof(buf);
    2767           0 :     for(int i = 0; i < image.h; ++i)
    2768             :     {
    2769           0 :         uchar filter = 0;
    2770           0 :         for(int j = 0; j < 2; ++j)
    2771             :         {
    2772           0 :             z.next_in = j ? static_cast<Bytef *>(image.data) + (flip ? image.h-i-1 : i)*image.pitch : static_cast<Bytef *>(&filter);
    2773           0 :             z.avail_in = j ? image.w*image.bpp : 1;
    2774           0 :             while(z.avail_in > 0)
    2775             :             {
    2776           0 :                 if(deflate(&z, Z_NO_FLUSH) != Z_OK)
    2777             :                 {
    2778           0 :                     goto cleanuperror; //goto is beneath FLUSHZ macro
    2779             :                 }
    2780           0 :                 flushzip(z, buf, sizeof(buf), len, f, crc);
    2781             :             }
    2782             :         }
    2783             :     }
    2784             : 
    2785             :     for(;;)
    2786             :     {
    2787           0 :         int err = deflate(&z, Z_FINISH);
    2788           0 :         if(err != Z_OK && err != Z_STREAM_END)
    2789             :         {
    2790           0 :             goto cleanuperror;
    2791             :         }
    2792           0 :         flushzip(z, buf, sizeof(buf), len, f, crc);
    2793           0 :         if(err == Z_STREAM_END)
    2794             :         {
    2795           0 :             break;
    2796             :         }
    2797           0 :     }
    2798           0 :     deflateEnd(&z);
    2799             : 
    2800           0 :     f->seek(idat, SEEK_SET);
    2801           0 :     f->putbig<uint>(len);
    2802           0 :     f->seek(0, SEEK_END);
    2803           0 :     f->putbig<uint>(crc);
    2804           0 :     writepngchunk(f, "IEND");
    2805           0 :     delete f;
    2806           0 :     return;
    2807             : 
    2808           0 : cleanuperror:
    2809           0 :     deflateEnd(&z);
    2810             : 
    2811           0 : error:
    2812           0 :     delete f;
    2813           0 :     conoutf(Console_Error, "failed saving png to %s", filename);
    2814             : }
    2815             : 
    2816             : SVARP(screenshotdir, "screenshot");
    2817             : 
    2818           1 : void screenshot(char *filename)
    2819             : {
    2820             :     static string buf;
    2821           1 :     int dirlen = 0;
    2822           1 :     copystring(buf, screenshotdir);
    2823           1 :     if(screenshotdir[0])
    2824             :     {
    2825           1 :         dirlen = std::strlen(buf);
    2826           1 :         if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < static_cast<int>(sizeof(buf)))
    2827             :         {
    2828           1 :             buf[dirlen++] = '/';
    2829           1 :             buf[dirlen] = '\0';
    2830             :         }
    2831           1 :         const char *dir = findfile(buf, "w");
    2832           1 :         if(!fileexists(dir, "w"))
    2833             :         {
    2834           1 :             createdir(dir);
    2835             :         }
    2836             :     }
    2837           1 :     if(filename[0])
    2838             :     {
    2839           0 :         concatstring(buf, filename);
    2840             :     }
    2841             :     else
    2842             :     {
    2843             :         string sstime;
    2844           1 :         time_t t = std::time(nullptr);
    2845           1 :         size_t len = std::strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S.png", std::localtime(&t));
    2846           1 :         sstime[std::min(len, sizeof(sstime)-1)] = '\0';
    2847           1 :         concatstring(buf, sstime);
    2848          24 :         for(char *s = &buf[dirlen]; *s; s++)
    2849             :         {
    2850          23 :             if(iscubespace(*s) || *s == '/' || *s == '\\')
    2851             :             {
    2852           0 :                 *s = '-';
    2853             :             }
    2854             :         }
    2855             :     }
    2856             : 
    2857           1 :     ImageData image(screenw, screenh, 3);
    2858           1 :     glPixelStorei(GL_PACK_ALIGNMENT, texalign(screenw, 3));
    2859           1 :     glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data);
    2860           1 :     savepng(path(buf), image, true);
    2861           1 : }
    2862             : 
    2863             : //used in libprimis api, avoids having to provide entire Shader iface
    2864           0 : void setldrnotexture()
    2865             : {
    2866           0 :     ldrnotextureshader->set();
    2867           0 : }
    2868             : 
    2869           1 : void inittexturecmds()
    2870             : {
    2871           1 :     addcommand("texturereset", reinterpret_cast<identfun>(texturereset), "i", Id_Command);
    2872           1 :     addcommand("materialreset", reinterpret_cast<identfun>(materialreset), "", Id_Command);
    2873           1 :     addcommand("decalreset", reinterpret_cast<identfun>(decalreset), "i", Id_Command);
    2874           2 :     addcommand("compactvslots", reinterpret_cast<identfun>(+[](int *cull){multiplayerwarn();rootworld.compactvslots(*cull!=0);rootworld.allchanged();}), "i", Id_Command);
    2875           1 :     addcommand("texture", reinterpret_cast<identfun>(texture), "ssiiif", Id_Command);
    2876           1 :     addcommand("texgrass", reinterpret_cast<identfun>(texgrass), "s", Id_Command);
    2877           1 :     addcommand("texscroll", reinterpret_cast<identfun>(texscroll), "ff", Id_Command);
    2878           1 :     addcommand("texoffset", reinterpret_cast<identfun>(texoffset_), "ii", Id_Command);
    2879           1 :     addcommand("texrotate", reinterpret_cast<identfun>(texrotate_), "i", Id_Command);
    2880           1 :     addcommand("texangle", reinterpret_cast<identfun>(texangle_), "f", Id_Command);
    2881           1 :     addcommand("texscale", reinterpret_cast<identfun>(texscale), "f", Id_Command);
    2882           1 :     addcommand("texalpha", reinterpret_cast<identfun>(texalpha), "ff", Id_Command);
    2883           1 :     addcommand("texcolor", reinterpret_cast<identfun>(texcolor), "fff", Id_Command);
    2884           1 :     addcommand("texrefract", reinterpret_cast<identfun>(texrefract), "ffff", Id_Command);
    2885           1 :     addcommand("texsmooth", reinterpret_cast<identfun>(texsmooth), "ib", Id_Command);
    2886           1 :     addcommand("decaldepth", reinterpret_cast<identfun>(decaldepth), "ff", Id_Command);
    2887           1 :     addcommand("reloadtex", reinterpret_cast<identfun>(reloadtex), "s", Id_Command);
    2888           1 :     addcommand("screenshot", reinterpret_cast<identfun>(screenshot), "s", Id_Command);
    2889           1 : }
    2890             : 

Generated by: LCOV version 1.14