From d309df4ce4d8ad0ed995a8e1c4267412a7782021 Mon Sep 17 00:00:00 2001 From: xolatile Date: Mon, 4 Aug 2025 22:53:42 +0200 Subject: Bunch of small changes... --- src/engine/texture.cpp | 4963 +++++++++++++++++++++++------------------------- 1 file changed, 2382 insertions(+), 2581 deletions(-) (limited to 'src/engine/texture.cpp') diff --git a/src/engine/texture.cpp b/src/engine/texture.cpp index e76b59e..6a0084d 100644 --- a/src/engine/texture.cpp +++ b/src/engine/texture.cpp @@ -5,611 +5,611 @@ #ifndef SDL_IMAGE_VERSION_ATLEAST #define SDL_IMAGE_VERSION_ATLEAST(X, Y, Z) \ - (SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL) >= SDL_VERSIONNUM(X, Y, Z)) + (SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL) >= SDL_VERSIONNUM(X, Y, Z)) #endif template static void halvetexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst) { - for(uchar *yend = &src[sh*stride]; src < yend;) - { - for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP) - { - loopi(BPP) dst[i] = (uint(xsrc[i]) + uint(xsrc[i+BPP]) + uint(xsrc[stride+i]) + uint(xsrc[stride+i+BPP]))>>2; - } - src += 2*stride; - } + for(uchar *yend = &src[sh*stride]; src < yend;) + { + for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP) + { + loopi(BPP) dst[i] = (uint(xsrc[i]) + uint(xsrc[i+BPP]) + uint(xsrc[stride+i]) + uint(xsrc[stride+i+BPP]))>>2; + } + src += 2*stride; + } } template static void shifttexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh) { - uint wfrac = sw/dw, hfrac = sh/dh, wshift = 0, hshift = 0; - while(dw<> tshift; - } - src += hfrac*stride; - } + uint wfrac = sw/dw, hfrac = sh/dh, wshift = 0, hshift = 0; + while(dw<> tshift; + } + src += hfrac*stride; + } } template static void scaletexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh) { - uint wfrac = (sw<<12)/dw, hfrac = (sh<<12)/dh, darea = dw*dh, sarea = sw*sh; - int over, under; - for(over = 0; (darea>>over) > sarea; over++); - for(under = 0; (darea<>12, h = (yn>>12) - yi, ylow = ((yn|(-int(h)>>24))&0xFFFU) + 1 - (y&0xFFFU), yhigh = (yn&0xFFFU) + 1; - const uchar *ysrc = &src[yi*stride]; - for(uint x = 0; x < dw; x += wfrac, dst += BPP) - { - const uint xn = x + wfrac - 1, xi = x>>12, w = (xn>>12) - xi, xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU), xhigh = (xn&0xFFFU) + 1; - const uchar *xsrc = &ysrc[xi*BPP], *xend = &xsrc[w*BPP]; - uint t[BPP] = {0}; - for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) - loopi(BPP) t[i] += xcur[i]; - loopi(BPP) t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale; - if(h) - { - xsrc += stride; - xend += stride; - for(uint hcur = h; --hcur; xsrc += stride, xend += stride) - { - uint c[BPP] = {0}; - for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) - loopi(BPP) c[i] += xcur[i]; - loopi(BPP) t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale; - } - uint c[BPP] = {0}; - for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) - loopi(BPP) c[i] += xcur[i]; - loopi(BPP) t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale; - } - loopi(BPP) dst[i] = (t[i] * area)>>dscale; - } - } + uint wfrac = (sw<<12)/dw, hfrac = (sh<<12)/dh, darea = dw*dh, sarea = sw*sh; + int over, under; + for(over = 0; (darea>>over) > sarea; over++); + for(under = 0; (darea<>12, h = (yn>>12) - yi, ylow = ((yn|(-int(h)>>24))&0xFFFU) + 1 - (y&0xFFFU), yhigh = (yn&0xFFFU) + 1; + const uchar *ysrc = &src[yi*stride]; + for(uint x = 0; x < dw; x += wfrac, dst += BPP) + { + const uint xn = x + wfrac - 1, xi = x>>12, w = (xn>>12) - xi, xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU), xhigh = (xn&0xFFFU) + 1; + const uchar *xsrc = &ysrc[xi*BPP], *xend = &xsrc[w*BPP]; + uint t[BPP] = {0}; + for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) + loopi(BPP) t[i] += xcur[i]; + loopi(BPP) t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale; + if(h) + { + xsrc += stride; + xend += stride; + for(uint hcur = h; --hcur; xsrc += stride, xend += stride) + { + uint c[BPP] = {0}; + for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) + loopi(BPP) c[i] += xcur[i]; + loopi(BPP) t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale; + } + uint c[BPP] = {0}; + for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP) + loopi(BPP) c[i] += xcur[i]; + loopi(BPP) t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale; + } + loopi(BPP) dst[i] = (t[i] * area)>>dscale; + } + } } static void scaletexture(uchar * RESTRICT src, uint sw, uint sh, uint bpp, uint pitch, uchar * RESTRICT dst, uint dw, uint dh) { - if(sw == dw*2 && sh == dh*2) - { - switch(bpp) - { - case 1: return halvetexture<1>(src, sw, sh, pitch, dst); - case 2: return halvetexture<2>(src, sw, sh, pitch, dst); - case 3: return halvetexture<3>(src, sw, sh, pitch, dst); - case 4: return halvetexture<4>(src, sw, sh, pitch, dst); - } - } - else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1)) - { - switch(bpp) - { - case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh); - case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh); - case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh); - case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh); - } - } - else - { - switch(bpp) - { - case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh); - case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh); - case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh); - case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh); - } - } + if(sw == dw*2 && sh == dh*2) + { + switch(bpp) + { + case 1: return halvetexture<1>(src, sw, sh, pitch, dst); + case 2: return halvetexture<2>(src, sw, sh, pitch, dst); + case 3: return halvetexture<3>(src, sw, sh, pitch, dst); + case 4: return halvetexture<4>(src, sw, sh, pitch, dst); + } + } + else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1)) + { + switch(bpp) + { + case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh); + case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh); + case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh); + case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh); + } + } + else + { + switch(bpp) + { + case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh); + case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh); + case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh); + case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh); + } + } } static void reorientnormals(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy) { - int stridex = bpp, stridey = bpp; - if(swapxy) stridex *= sh; else stridey *= sw; - if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; } - if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; } - uchar *srcrow = src; - loopi(sh) - { - for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;) - { - uchar nx = *src++, ny = *src++; - if(flipx) nx = 255-nx; - if(flipy) ny = 255-ny; - if(swapxy) swap(nx, ny); - curdst[0] = nx; - curdst[1] = ny; - curdst[2] = *src++; - if(bpp > 3) curdst[3] = *src++; - curdst += stridex; - } - srcrow += stride; - dst += stridey; - } + int stridex = bpp, stridey = bpp; + if(swapxy) stridex *= sh; else stridey *= sw; + if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; } + if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; } + uchar *srcrow = src; + loopi(sh) + { + for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;) + { + uchar nx = *src++, ny = *src++; + if(flipx) nx = 255-nx; + if(flipy) ny = 255-ny; + if(swapxy) swap(nx, ny); + curdst[0] = nx; + curdst[1] = ny; + curdst[2] = *src++; + if(bpp > 3) curdst[3] = *src++; + curdst += stridex; + } + srcrow += stride; + dst += stridey; + } } template static inline void reorienttexture(uchar * RESTRICT src, int sw, int sh, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy) { - int stridex = BPP, stridey = BPP; - if(swapxy) stridex *= sh; else stridey *= sw; - if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; } - if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; } - uchar *srcrow = src; - loopi(sh) - { - for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*BPP]; src < end;) - { - loopk(BPP) curdst[k] = *src++; - curdst += stridex; - } - srcrow += stride; - dst += stridey; - } + int stridex = BPP, stridey = BPP; + if(swapxy) stridex *= sh; else stridey *= sw; + if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; } + if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; } + uchar *srcrow = src; + loopi(sh) + { + for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*BPP]; src < end;) + { + loopk(BPP) curdst[k] = *src++; + curdst += stridex; + } + srcrow += stride; + dst += stridey; + } } static void reorienttexture(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy) { - switch(bpp) - { - case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy); - case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy); - case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy); - case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy); - } + switch(bpp) + { + case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy); + case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy); + case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy); + case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy); + } } static void reorients3tc(GLenum format, int blocksize, int w, int h, uchar *src, uchar *dst, bool flipx, bool flipy, bool swapxy, bool normals = false) { - int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize; - if(swapxy) stridex *= bw; else stridey *= bh; - if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; } - if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; } - loopi(bh) - { - for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; src += blocksize, curdst += stridex) - { - if(format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) - { - ullong salpha = lilswap(*(const ullong *)src), dalpha = 0; - uint xmask = flipx ? 15 : 0, ymask = flipy ? 15 : 0, xshift = 2, yshift = 4; - if(swapxy) swap(xshift, yshift); - for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) - { - dalpha |= ((salpha&15) << (((xmask^x)<>= 4; - } - *(ullong *)curdst = lilswap(dalpha); - src += 8; - curdst += 8; - } - else if(format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) - { - uchar alpha1 = src[0], alpha2 = src[1]; - ullong salpha = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4]) << 16), dalpha = 0; - uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2; - if(swapxy) swap(xshift, yshift); - for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) - { - dalpha |= ((salpha&7) << (3*((xmask^x)<>= 3; - } - curdst[0] = alpha1; - curdst[1] = alpha2; - *(ushort *)&curdst[2] = lilswap(ushort(dalpha)); - *(ushort *)&curdst[4] = lilswap(ushort(dalpha>>16)); - *(ushort *)&curdst[6] = lilswap(ushort(dalpha>>32)); - src += 8; - curdst += 8; - } - - ushort color1 = lilswap(*(const ushort *)src), color2 = lilswap(*(const ushort *)&src[2]); - uint sbits = lilswap(*(const uint *)&src[4]); - if(normals) - { - ushort ncolor1 = color1, ncolor2 = color2; - if(flipx) - { - ncolor1 = (ncolor1 & ~0xF800) | (0xF800 - (ncolor1 & 0xF800)); - ncolor2 = (ncolor2 & ~0xF800) | (0xF800 - (ncolor2 & 0xF800)); - } - if(flipy) - { - ncolor1 = (ncolor1 & ~0x7E0) | (0x7E0 - (ncolor1 & 0x7E0)); - ncolor2 = (ncolor2 & ~0x7E0) | (0x7E0 - (ncolor2 & 0x7E0)); - } - if(swapxy) - { - ncolor1 = (ncolor1 & 0x1F) | (((((ncolor1 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor1 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11); - ncolor2 = (ncolor2 & 0x1F) | (((((ncolor2 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor2 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11); - } - if(color1 <= color2 && ncolor1 > ncolor2) { color1 = ncolor2; color2 = ncolor1; } - else { color1 = ncolor1; color2 = ncolor2; } - } - uint dbits = 0, xmask = flipx ? 3 : 0, ymask = flipy ? 3 : 0, xshift = 1, yshift = 3; - if(swapxy) swap(xshift, yshift); - for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) - { - dbits |= ((sbits&3) << (((xmask^x)<>= 2; - } - *(ushort *)curdst = lilswap(color1); - *(ushort *)&curdst[2] = lilswap(color2); - *(uint *)&curdst[4] = lilswap(dbits); - - if(blocksize > 8) { src -= 8; curdst -= 8; } - } - dst += stridey; - } + int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize; + if(swapxy) stridex *= bw; else stridey *= bh; + if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; } + if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; } + loopi(bh) + { + for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; src += blocksize, curdst += stridex) + { + if(format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) + { + ullong salpha = lilswap(*(const ullong *)src), dalpha = 0; + uint xmask = flipx ? 15 : 0, ymask = flipy ? 15 : 0, xshift = 2, yshift = 4; + if(swapxy) swap(xshift, yshift); + for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) + { + dalpha |= ((salpha&15) << (((xmask^x)<>= 4; + } + *(ullong *)curdst = lilswap(dalpha); + src += 8; + curdst += 8; + } + else if(format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + { + uchar alpha1 = src[0], alpha2 = src[1]; + ullong salpha = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4]) << 16), dalpha = 0; + uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2; + if(swapxy) swap(xshift, yshift); + for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) + { + dalpha |= ((salpha&7) << (3*((xmask^x)<>= 3; + } + curdst[0] = alpha1; + curdst[1] = alpha2; + *(ushort *)&curdst[2] = lilswap(ushort(dalpha)); + *(ushort *)&curdst[4] = lilswap(ushort(dalpha>>16)); + *(ushort *)&curdst[6] = lilswap(ushort(dalpha>>32)); + src += 8; + curdst += 8; + } + + ushort color1 = lilswap(*(const ushort *)src), color2 = lilswap(*(const ushort *)&src[2]); + uint sbits = lilswap(*(const uint *)&src[4]); + if(normals) + { + ushort ncolor1 = color1, ncolor2 = color2; + if(flipx) + { + ncolor1 = (ncolor1 & ~0xF800) | (0xF800 - (ncolor1 & 0xF800)); + ncolor2 = (ncolor2 & ~0xF800) | (0xF800 - (ncolor2 & 0xF800)); + } + if(flipy) + { + ncolor1 = (ncolor1 & ~0x7E0) | (0x7E0 - (ncolor1 & 0x7E0)); + ncolor2 = (ncolor2 & ~0x7E0) | (0x7E0 - (ncolor2 & 0x7E0)); + } + if(swapxy) + { + ncolor1 = (ncolor1 & 0x1F) | (((((ncolor1 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor1 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11); + ncolor2 = (ncolor2 & 0x1F) | (((((ncolor2 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor2 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11); + } + if(color1 <= color2 && ncolor1 > ncolor2) { color1 = ncolor2; color2 = ncolor1; } + else { color1 = ncolor1; color2 = ncolor2; } + } + uint dbits = 0, xmask = flipx ? 3 : 0, ymask = flipy ? 3 : 0, xshift = 1, yshift = 3; + if(swapxy) swap(xshift, yshift); + for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) + { + dbits |= ((sbits&3) << (((xmask^x)<>= 2; + } + *(ushort *)curdst = lilswap(color1); + *(ushort *)&curdst[2] = lilswap(color2); + *(uint *)&curdst[4] = lilswap(dbits); + + if(blocksize > 8) { src -= 8; curdst -= 8; } + } + dst += stridey; + } } static void reorientrgtc(GLenum format, int blocksize, int w, int h, uchar *src, uchar *dst, bool flipx, bool flipy, bool swapxy) { - int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize; - if(swapxy) stridex *= bw; else stridey *= bh; - if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; } - if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; } - stridex -= blocksize; - loopi(bh) - { - for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; curdst += stridex) - { - loopj(blocksize/8) - { - uchar val1 = src[0], val2 = src[1]; - ullong sval = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4] )<< 16), dval = 0; - uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2; - if(swapxy) swap(xshift, yshift); - for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) - { - dval |= ((sval&7) << (3*((xmask^x)<>= 3; - } - curdst[0] = val1; - curdst[1] = val2; - *(ushort *)&curdst[2] = lilswap(ushort(dval)); - *(ushort *)&curdst[4] = lilswap(ushort(dval>>16)); - *(ushort *)&curdst[6] = lilswap(ushort(dval>>32)); - src += 8; - curdst += 8; - } - } - dst += stridey; - } + int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize; + if(swapxy) stridex *= bw; else stridey *= bh; + if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; } + if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; } + stridex -= blocksize; + loopi(bh) + { + for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; curdst += stridex) + { + loopj(blocksize/8) + { + uchar val1 = src[0], val2 = src[1]; + ullong sval = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4] )<< 16), dval = 0; + uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2; + if(swapxy) swap(xshift, yshift); + for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++) + { + dval |= ((sval&7) << (3*((xmask^x)<>= 3; + } + curdst[0] = val1; + curdst[1] = val2; + *(ushort *)&curdst[2] = lilswap(ushort(dval)); + *(ushort *)&curdst[4] = lilswap(ushort(dval>>16)); + *(ushort *)&curdst[6] = lilswap(ushort(dval>>32)); + src += 8; + curdst += 8; + } + } + dst += stridey; + } } #define writetex(t, body) do \ - { \ - uchar *dstrow = t.data; \ - loop(y, t.h) \ - { \ - for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp]; dst < end; dst += t.bpp) \ - { \ - body; \ - } \ - dstrow += t.pitch; \ - } \ - } while(0) + { \ + uchar *dstrow = t.data; \ + loop(y, t.h) \ + { \ + for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp]; dst < end; dst += t.bpp) \ + { \ + body; \ + } \ + dstrow += t.pitch; \ + } \ + } while(0) #define readwritetex(t, s, body) do \ - { \ - uchar *dstrow = t.data, *srcrow = s.data; \ - loop(y, t.h) \ - { \ - for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += t.bpp, src += s.bpp) \ - { \ - body; \ - } \ - dstrow += t.pitch; \ - srcrow += s.pitch; \ - } \ - } while(0) + { \ + uchar *dstrow = t.data, *srcrow = s.data; \ + loop(y, t.h) \ + { \ + for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += t.bpp, src += s.bpp) \ + { \ + body; \ + } \ + dstrow += t.pitch; \ + srcrow += s.pitch; \ + } \ + } while(0) #define read2writetex(t, s1, src1, s2, src2, body) do \ - { \ - uchar *dstrow = t.data, *src1row = s1.data, *src2row = s2.data; \ - loop(y, t.h) \ - { \ - for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp], *src1 = src1row, *src2 = src2row; dst < end; dst += t.bpp, src1 += s1.bpp, src2 += s2.bpp) \ - { \ - body; \ - } \ - dstrow += t.pitch; \ - src1row += s1.pitch; \ - src2row += s2.pitch; \ - } \ - } while(0) + { \ + uchar *dstrow = t.data, *src1row = s1.data, *src2row = s2.data; \ + loop(y, t.h) \ + { \ + for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp], *src1 = src1row, *src2 = src2row; dst < end; dst += t.bpp, src1 += s1.bpp, src2 += s2.bpp) \ + { \ + body; \ + } \ + dstrow += t.pitch; \ + src1row += s1.pitch; \ + src2row += s2.pitch; \ + } \ + } while(0) #define readwritergbtex(t, s, body) \ - { \ - if(t.bpp >= 3) readwritetex(t, s, body); \ - else \ - { \ - ImageData rgb(t.w, t.h, 3); \ - read2writetex(rgb, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \ - t.replace(rgb); \ - } \ - } + { \ + if(t.bpp >= 3) readwritetex(t, s, body); \ + else \ + { \ + ImageData rgb(t.w, t.h, 3); \ + read2writetex(rgb, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \ + t.replace(rgb); \ + } \ + } void forcergbimage(ImageData &s) { - if(s.bpp >= 3) return; - ImageData d(s.w, s.h, 3); - readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); - s.replace(d); + if(s.bpp >= 3) return; + ImageData d(s.w, s.h, 3); + readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); + s.replace(d); } #define readwritergbatex(t, s, body) \ - { \ - if(t.bpp >= 4) { readwritetex(t, s, body); } \ - else \ - { \ - ImageData rgba(t.w, t.h, 4); \ - if(t.bpp==3) read2writetex(rgba, t, orig, s, src, { dst[0] = orig[0]; dst[1] = orig[1]; dst[2] = orig[2]; body; }); \ - else read2writetex(rgba, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \ - t.replace(rgba); \ - } \ - } + { \ + if(t.bpp >= 4) { readwritetex(t, s, body); } \ + else \ + { \ + ImageData rgba(t.w, t.h, 4); \ + if(t.bpp==3) read2writetex(rgba, t, orig, s, src, { dst[0] = orig[0]; dst[1] = orig[1]; dst[2] = orig[2]; body; }); \ + else read2writetex(rgba, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \ + t.replace(rgba); \ + } \ + } void forcergbaimage(ImageData &s) { - if(s.bpp >= 4) return; - ImageData d(s.w, s.h, 4); - if(s.bpp==3) readwritetex(d, s, { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; }); - else readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); - s.replace(d); + if(s.bpp >= 4) return; + ImageData d(s.w, s.h, 4); + if(s.bpp==3) readwritetex(d, s, { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; }); + else readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); + s.replace(d); } void swizzleimage(ImageData &s) { - if(s.bpp==2) - { - ImageData d(s.w, s.h, 4); - readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; dst[3] = src[1]; }); - s.replace(d); - } - else if(s.bpp==1) - { - ImageData d(s.w, s.h, 3); - readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); - s.replace(d); - } + if(s.bpp==2) + { + ImageData d(s.w, s.h, 4); + readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; dst[3] = src[1]; }); + s.replace(d); + } + else if(s.bpp==1) + { + ImageData d(s.w, s.h, 3); + readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; }); + s.replace(d); + } } void texreorient(ImageData &s, bool flipx, bool flipy, bool swapxy, int type = TEX_DIFFUSE) { - ImageData d(swapxy ? s.h : s.w, swapxy ? s.w : s.h, s.bpp, s.levels, s.align, s.compressed); - switch(s.compressed) - { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - [[fallthrough]]; - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - [[fallthrough]]; - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - [[fallthrough]]; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - { - uchar *dst = d.data, *src = s.data; - loopi(s.levels) - { - reorients3tc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy, type==TEX_NORMAL); - src += s.calclevelsize(i); - dst += d.calclevelsize(i); - } - break; - } - case GL_COMPRESSED_RED_RGTC1: - [[fallthrough]]; - case GL_COMPRESSED_RG_RGTC2: - [[fallthrough]]; - case GL_COMPRESSED_LUMINANCE_LATC1_EXT: - [[fallthrough]]; - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - { - uchar *dst = d.data, *src = s.data; - loopi(s.levels) - { - reorientrgtc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy); - src += s.calclevelsize(i); - dst += d.calclevelsize(i); - } - break; - } - default: - if(type==TEX_NORMAL && s.bpp >= 3) reorientnormals(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy); - else reorienttexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy); - break; - } - s.replace(d); + ImageData d(swapxy ? s.h : s.w, swapxy ? s.w : s.h, s.bpp, s.levels, s.align, s.compressed); + switch(s.compressed) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + [[fallthrough]]; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + [[fallthrough]]; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + [[fallthrough]]; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + { + uchar *dst = d.data, *src = s.data; + loopi(s.levels) + { + reorients3tc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy, type==TEX_NORMAL); + src += s.calclevelsize(i); + dst += d.calclevelsize(i); + } + break; + } + case GL_COMPRESSED_RED_RGTC1: + [[fallthrough]]; + case GL_COMPRESSED_RG_RGTC2: + [[fallthrough]]; + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + [[fallthrough]]; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + { + uchar *dst = d.data, *src = s.data; + loopi(s.levels) + { + reorientrgtc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy); + src += s.calclevelsize(i); + dst += d.calclevelsize(i); + } + break; + } + default: + if(type==TEX_NORMAL && s.bpp >= 3) reorientnormals(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy); + else reorienttexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy); + break; + } + s.replace(d); } extern const texrotation texrotations[8] = { - { false, false, false }, // 0: default - { false, true, true }, // 1: 90 degrees - { true, true, false }, // 2: 180 degrees - { true, false, true }, // 3: 270 degrees - { true, false, false }, // 4: flip X - { false, true, false }, // 5: flip Y - { false, false, true }, // 6: transpose - { true, true, true }, // 7: flipped transpose + { false, false, false }, // 0: default + { false, true, true }, // 1: 90 degrees + { true, true, false }, // 2: 180 degrees + { true, false, true }, // 3: 270 degrees + { true, false, false }, // 4: flip X + { false, true, false }, // 5: flip Y + { false, false, true }, // 6: transpose + { true, true, true }, // 7: flipped transpose }; void texrotate(ImageData &s, int numrots, int type = TEX_DIFFUSE) { - if(numrots>=1 && numrots<=7) - { - const texrotation &r = texrotations[numrots]; - texreorient(s, r.flipx, r.flipy, r.swapxy, type); - } + if(numrots>=1 && numrots<=7) + { + const texrotation &r = texrotations[numrots]; + texreorient(s, r.flipx, r.flipy, r.swapxy, type); + } } void texoffset(ImageData &s, int xoffset, int yoffset) { - xoffset = max(xoffset, 0); - xoffset %= s.w; - yoffset = max(yoffset, 0); - yoffset %= s.h; - if(!xoffset && !yoffset) return; - ImageData d(s.w, s.h, s.bpp); - uchar *src = s.data; - loop(y, s.h) - { - uchar *dst = (uchar *)d.data+((y+yoffset)%d.h)*d.pitch; - memcpy(dst+xoffset*s.bpp, src, (s.w-xoffset)*s.bpp); - memcpy(dst, src+(s.w-xoffset)*s.bpp, xoffset*s.bpp); - src += s.pitch; - } - s.replace(d); + xoffset = max(xoffset, 0); + xoffset %= s.w; + yoffset = max(yoffset, 0); + yoffset %= s.h; + if(!xoffset && !yoffset) return; + ImageData d(s.w, s.h, s.bpp); + uchar *src = s.data; + loop(y, s.h) + { + uchar *dst = (uchar *)d.data+((y+yoffset)%d.h)*d.pitch; + memcpy(dst+xoffset*s.bpp, src, (s.w-xoffset)*s.bpp); + memcpy(dst, src+(s.w-xoffset)*s.bpp, xoffset*s.bpp); + src += s.pitch; + } + s.replace(d); } void texmad(ImageData &s, const vec &mul, const vec &add) { - if(s.bpp < 3 && (mul.x != mul.y || mul.y != mul.z || add.x != add.y || add.y != add.z)) - swizzleimage(s); - int maxk = min(int(s.bpp), 3); - writetex(s, - loopk(maxk) dst[k] = uchar(clamp(dst[k]*mul[k] + 255*add[k], 0.0f, 255.0f)); - ); + if(s.bpp < 3 && (mul.x != mul.y || mul.y != mul.z || add.x != add.y || add.y != add.z)) + swizzleimage(s); + int maxk = min(int(s.bpp), 3); + writetex(s, + loopk(maxk) dst[k] = uchar(clamp(dst[k]*mul[k] + 255*add[k], 0.0f, 255.0f)); + ); } void texcolorify(ImageData &s, const vec &color, vec weights) { - if(s.bpp < 3) return; - if(weights.iszero()) weights = vec(0.21f, 0.72f, 0.07f); - writetex(s, - float lum = dst[0]*weights.x + dst[1]*weights.y + dst[2]*weights.z; - loopk(3) dst[k] = uchar(clamp(lum*color[k], 0.0f, 255.0f)); - ); + if(s.bpp < 3) return; + if(weights.iszero()) weights = vec(0.21f, 0.72f, 0.07f); + writetex(s, + float lum = dst[0]*weights.x + dst[1]*weights.y + dst[2]*weights.z; + loopk(3) dst[k] = uchar(clamp(lum*color[k], 0.0f, 255.0f)); + ); } void texcolormask(ImageData &s, const vec &color1, const vec &color2) { - if(s.bpp < 4) return; - ImageData d(s.w, s.h, 3); - readwritetex(d, s, - vec color; - color.lerp(color2, color1, src[3]/255.0f); - loopk(3) dst[k] = uchar(clamp(color[k]*src[k], 0.0f, 255.0f)); - ); - s.replace(d); + if(s.bpp < 4) return; + ImageData d(s.w, s.h, 3); + readwritetex(d, s, + vec color; + color.lerp(color2, color1, src[3]/255.0f); + loopk(3) dst[k] = uchar(clamp(color[k]*src[k], 0.0f, 255.0f)); + ); + s.replace(d); } void texdup(ImageData &s, int srcchan, int dstchan) { - if(srcchan==dstchan || max(srcchan, dstchan) >= s.bpp) return; - writetex(s, dst[dstchan] = dst[srcchan]); + if(srcchan==dstchan || max(srcchan, dstchan) >= s.bpp) return; + writetex(s, dst[dstchan] = dst[srcchan]); } void texmix(ImageData &s, int c1, int c2, int c3, int c4) { - int numchans = c1 < 0 ? 0 : (c2 < 0 ? 1 : (c3 < 0 ? 2 : (c4 < 0 ? 3 : 4))); - if(numchans <= 0) return; - ImageData d(s.w, s.h, numchans); - readwritetex(d, s, - switch(numchans) - { - case 4: dst[3] = src[c4]; - [[fallthrough]]; - case 3: dst[2] = src[c3]; - [[fallthrough]]; - case 2: dst[1] = src[c2]; - [[fallthrough]]; - case 1: dst[0] = src[c1]; break; - default: break; - } - ); - s.replace(d); + int numchans = c1 < 0 ? 0 : (c2 < 0 ? 1 : (c3 < 0 ? 2 : (c4 < 0 ? 3 : 4))); + if(numchans <= 0) return; + ImageData d(s.w, s.h, numchans); + readwritetex(d, s, + switch(numchans) + { + case 4: dst[3] = src[c4]; + [[fallthrough]]; + case 3: dst[2] = src[c3]; + [[fallthrough]]; + case 2: dst[1] = src[c2]; + [[fallthrough]]; + case 1: dst[0] = src[c1]; break; + default: break; + } + ); + s.replace(d); } void texgrey(ImageData &s) { - if(s.bpp <= 2) return; - ImageData d(s.w, s.h, s.bpp >= 4 ? 2 : 1); - if(s.bpp >= 4) - { - readwritetex(d, s, - dst[0] = src[0]; - dst[1] = src[3]; - ); - } - else - { - readwritetex(d, s, dst[0] = src[0]); - } - s.replace(d); + if(s.bpp <= 2) return; + ImageData d(s.w, s.h, s.bpp >= 4 ? 2 : 1); + if(s.bpp >= 4) + { + readwritetex(d, s, + dst[0] = src[0]; + dst[1] = src[3]; + ); + } + else + { + readwritetex(d, s, dst[0] = src[0]); + } + s.replace(d); } void texpremul(ImageData &s) { - switch(s.bpp) - { - case 2: - writetex(s, - dst[0] = uchar((uint(dst[0])*uint(dst[1]))/255); - ); - break; - case 4: - writetex(s, - uint alpha = dst[3]; - dst[0] = uchar((uint(dst[0])*alpha)/255); - dst[1] = uchar((uint(dst[1])*alpha)/255); - dst[2] = uchar((uint(dst[2])*alpha)/255); - ); - break; - } + switch(s.bpp) + { + case 2: + writetex(s, + dst[0] = uchar((uint(dst[0])*uint(dst[1]))/255); + ); + break; + case 4: + writetex(s, + uint alpha = dst[3]; + dst[0] = uchar((uint(dst[0])*alpha)/255); + dst[1] = uchar((uint(dst[1])*alpha)/255); + dst[2] = uchar((uint(dst[2])*alpha)/255); + ); + break; + } } void texagrad(ImageData &s, float x2, float y2, float x1, float y1) { - if(s.bpp != 2 && s.bpp != 4) return; - y1 = 1 - y1; - y2 = 1 - y2; - float minx = 1, miny = 1, maxx = 1, maxy = 1; - if(x1 != x2) - { - minx = (0 - x1) / (x2 - x1); - maxx = (1 - x1) / (x2 - x1); - } - if(y1 != y2) - { - miny = (0 - y1) / (y2 - y1); - maxy = (1 - y1) / (y2 - y1); - } - float dx = (maxx - minx)/max(s.w-1, 1), - dy = (maxy - miny)/max(s.h-1, 1), - cury = miny; - for(uchar *dstrow = s.data + s.bpp - 1, *endrow = dstrow + s.h*s.pitch; dstrow < endrow; dstrow += s.pitch) - { - float curx = minx; - for(uchar *dst = dstrow, *end = &dstrow[s.w*s.bpp]; dst < end; dst += s.bpp) - { - dst[0] = uchar(dst[0]*clamp(curx, 0.0f, 1.0f)*clamp(cury, 0.0f, 1.0f)); - curx += dx; - } - cury += dy; - } + if(s.bpp != 2 && s.bpp != 4) return; + y1 = 1 - y1; + y2 = 1 - y2; + float minx = 1, miny = 1, maxx = 1, maxy = 1; + if(x1 != x2) + { + minx = (0 - x1) / (x2 - x1); + maxx = (1 - x1) / (x2 - x1); + } + if(y1 != y2) + { + miny = (0 - y1) / (y2 - y1); + maxy = (1 - y1) / (y2 - y1); + } + float dx = (maxx - minx)/max(s.w-1, 1), + dy = (maxy - miny)/max(s.h-1, 1), + cury = miny; + for(uchar *dstrow = s.data + s.bpp - 1, *endrow = dstrow + s.h*s.pitch; dstrow < endrow; dstrow += s.pitch) + { + float curx = minx; + for(uchar *dst = dstrow, *end = &dstrow[s.w*s.bpp]; dst < end; dst += s.bpp) + { + dst[0] = uchar(dst[0]*clamp(curx, 0.0f, 1.0f)*clamp(cury, 0.0f, 1.0f)); + curx += dx; + } + cury += dy; + } } VAR(hwtexsize, 1, 0, 0); @@ -628,384 +628,348 @@ extern int usetexcompress; void setuptexcompress() { - if(!usetexcompress) return; + if(!usetexcompress) return; - GLenum hint = GL_DONT_CARE; - switch(texcompressquality) - { - case 1: hint = GL_NICEST; break; - case 0: hint = GL_FASTEST; break; - } - glHint(GL_TEXTURE_COMPRESSION_HINT, hint); + GLenum hint = GL_DONT_CARE; + switch(texcompressquality) + { + case 1: hint = GL_NICEST; break; + case 0: hint = GL_FASTEST; break; + } + glHint(GL_TEXTURE_COMPRESSION_HINT, hint); } GLenum compressedformat(GLenum format, int w, int h, int force = 0) { - if(usetexcompress && texcompress && force >= 0 && (force || max(w, h) >= texcompress)) switch(format) - { - case GL_RGB5: - case GL_RGB8: - case GL_RGB: return usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB; - case GL_RGB5_A1: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA; - case GL_RGBA: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA; - case GL_RED: - case GL_R8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RED_RGTC1 : GL_COMPRESSED_RED) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB); - case GL_RG: - case GL_RG8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RG_RGTC2 : GL_COMPRESSED_RG) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA); - case GL_LUMINANCE: - case GL_LUMINANCE8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_LATC1_EXT : GL_COMPRESSED_LUMINANCE) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB); - case GL_LUMINANCE_ALPHA: - case GL_LUMINANCE8_ALPHA8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT : GL_COMPRESSED_LUMINANCE_ALPHA) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA); - } - return format; + if(usetexcompress && texcompress && force >= 0 && (force || max(w, h) >= texcompress)) switch(format) + { + case GL_RGB5: + case GL_RGB8: + case GL_RGB: return usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB; + case GL_RGB5_A1: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA; + case GL_RGBA: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA; + case GL_RED: + case GL_R8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RED_RGTC1 : GL_COMPRESSED_RED) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB); + case GL_RG: + case GL_RG8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RG_RGTC2 : GL_COMPRESSED_RG) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA); + case GL_LUMINANCE: + case GL_LUMINANCE8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_LATC1_EXT : GL_COMPRESSED_LUMINANCE) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB); + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE8_ALPHA8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT : GL_COMPRESSED_LUMINANCE_ALPHA) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA); + } + return format; } GLenum uncompressedformat(GLenum format) { - switch(format) - { - case GL_COMPRESSED_ALPHA: - return GL_ALPHA; - case GL_COMPRESSED_LUMINANCE: - case GL_COMPRESSED_LUMINANCE_LATC1_EXT: - return GL_LUMINANCE; - case GL_COMPRESSED_LUMINANCE_ALPHA: - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - return GL_LUMINANCE_ALPHA; - case GL_COMPRESSED_RED: - case GL_COMPRESSED_RED_RGTC1: - return GL_RED; - case GL_COMPRESSED_RG: - case GL_COMPRESSED_RG_RGTC2: - return GL_RG; - case GL_COMPRESSED_RGB: - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - return GL_RGB; - case GL_COMPRESSED_RGBA: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - return GL_RGBA; - } - return GL_FALSE; + switch(format) + { + case GL_COMPRESSED_ALPHA: + return GL_ALPHA; + case GL_COMPRESSED_LUMINANCE: + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + return GL_LUMINANCE; + case GL_COMPRESSED_LUMINANCE_ALPHA: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_LUMINANCE_ALPHA; + case GL_COMPRESSED_RED: + case GL_COMPRESSED_RED_RGTC1: + return GL_RED; + case GL_COMPRESSED_RG: + case GL_COMPRESSED_RG_RGTC2: + return GL_RG; + case GL_COMPRESSED_RGB: + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + return GL_RGB; + case GL_COMPRESSED_RGBA: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return GL_RGBA; + } + return GL_FALSE; } int formatsize(GLenum format) { - switch(format) - { - case GL_RED: - case GL_LUMINANCE: - case GL_ALPHA: return 1; - case GL_RG: - case GL_LUMINANCE_ALPHA: return 2; - case GL_RGB: return 3; - case GL_RGBA: return 4; - default: return 4; - } + switch(format) + { + case GL_RED: + case GL_LUMINANCE: + case GL_ALPHA: return 1; + case GL_RG: + case GL_LUMINANCE_ALPHA: return 2; + case GL_RGB: return 3; + case GL_RGBA: return 4; + default: return 4; + } } VARFP(usenp2, 0, 0, 1, initwarning("texture quality", INIT_LOAD)); void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int compress, int &tw, int &th) { - int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize, - sizelimit = mipmap && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit; - if(compress > 0 && !usetexcompress) - { - w = max(w/compress, 1); - h = max(h/compress, 1); - } - if(canreduce && texreduce) - { - w = max(w>>texreduce, 1); - h = max(h>>texreduce, 1); - } - w = min(w, sizelimit); - h = min(h, sizelimit); - if(!usenp2 && (w&(w-1) || h&(h-1))) - { - tw = th = 1; - while(tw < w) tw *= 2; - while(th < h) th *= 2; - if(w < tw - tw/4) tw /= 2; - if(h < th - th/4) th /= 2; - } - else - { - tw = w; - th = h; - } -} - -static GLuint mipmapfbo[2] = { 0, 0 }; - -void cleanupmipmaps() -{ - if(mipmapfbo[0]) { glDeleteFramebuffers_(2, mipmapfbo); memset(mipmapfbo, 0, sizeof(mipmapfbo)); } -} - -VARFP(gpumipmap, 0, 0, 1, cleanupmipmaps()); + int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize, + sizelimit = mipmap && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit; + if(compress > 0 && !usetexcompress) + { + w = max(w/compress, 1); + h = max(h/compress, 1); + } + if(canreduce && texreduce) + { + w = max(w>>texreduce, 1); + h = max(h>>texreduce, 1); + } + w = min(w, sizelimit); + h = min(h, sizelimit); + if(!usenp2 && (w&(w-1) || h&(h-1))) + { + tw = th = 1; + while(tw < w) tw *= 2; + while(th < h) th *= 2; + if(w < tw - tw/4) tw /= 2; + if(h < th - th/4) th /= 2; + } + else + { + tw = w; + th = h; + } +} void uploadtexture(int tnum, GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, void *pixels, int pw, int ph, int pitch, bool mipmap) { - int bpp = formatsize(format), row = 0, rowalign = 0; - if(!pitch) pitch = pw*bpp; - uchar *buf = NULL; - if(pw!=tw || ph!=th) - { - buf = new uchar[tw*th*bpp]; - scaletexture((uchar *)pixels, pw, ph, bpp, pitch, buf, tw, th); - } - else if(tw*bpp != pitch) - { - row = pitch/bpp; - rowalign = texalign(pixels, pitch, 1); - while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch) rowalign >>= 1; - if(!rowalign) - { - row = 0; - buf = new uchar[tw*th*bpp]; - loopi(th) memcpy(&buf[i*tw*bpp], &((uchar *)pixels)[i*pitch], tw*bpp); - } - } - bool shouldgpumipmap = pixels && mipmap && max(tw, th) > 1 && gpumipmap && hasFBB && !uncompressedformat(internal); - for(int level = 0, align = 0, mw = tw, mh = th;; level++) - { - uchar *src = buf ? buf : (uchar *)pixels; - if(buf) pitch = mw*bpp; - int srcalign = row > 0 ? rowalign : texalign(src, pitch, 1); - if(align != srcalign) glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign); - if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row); - glTexImage2D(target, level, internal, mw, mh, 0, format, type, src); - if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0); - if(!mipmap || shouldgpumipmap || max(mw, mh) <= 1) break; - int srcw = mw, srch = mh; - if(mw > 1) mw /= 2; - if(mh > 1) mh /= 2; - if(src) - { - if(!buf) buf = new uchar[mw*mh*bpp]; - scaletexture(src, srcw, srch, bpp, pitch, buf, mw, mh); - } - } - if(buf) delete[] buf; - if(shouldgpumipmap) - { - GLint fbo = 0; - if(!inbetweenframes || drawtex) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); - for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++) - { - if(mw > 1) mw /= 2; - if(mh > 1) mh /= 2; - glTexImage2D(target, level, internal, mw, mh, 0, format, type, NULL); - } - if(!mipmapfbo[0]) glGenFramebuffers_(2, mipmapfbo); - glBindFramebuffer_(GL_READ_FRAMEBUFFER, mipmapfbo[0]); - glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, mipmapfbo[1]); - for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++) - { - int srcw = mw, srch = mh; - if(mw > 1) mw /= 2; - if(mh > 1) mh /= 2; - glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level - 1); - glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level); - glBlitFramebuffer_(0, 0, srcw, srch, 0, 0, mw, mh, GL_COLOR_BUFFER_BIT, GL_LINEAR); - } - glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0); - glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0); - glBindFramebuffer_(GL_FRAMEBUFFER, fbo); - } + int bpp = formatsize(format), row = 0, rowalign = 0; + if(!pitch) pitch = pw*bpp; + uchar *buf = NULL; + if(pw!=tw || ph!=th) + { + buf = new uchar[tw*th*bpp]; + scaletexture((uchar *)pixels, pw, ph, bpp, pitch, buf, tw, th); + } + else if(tw*bpp != pitch) + { + row = pitch/bpp; + rowalign = texalign(pixels, pitch, 1); + while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch) rowalign >>= 1; + if(!rowalign) + { + row = 0; + buf = new uchar[tw*th*bpp]; + loopi(th) memcpy(&buf[i*tw*bpp], &((uchar *)pixels)[i*pitch], tw*bpp); + } + } + for(int level = 0, align = 0, mw = tw, mh = th;; level++) + { + uchar *src = buf ? buf : (uchar *)pixels; + if(buf) pitch = mw*bpp; + int srcalign = row > 0 ? rowalign : texalign(src, pitch, 1); + if(align != srcalign) glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign); + if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row); + glTexImage2D(target, level, internal, mw, mh, 0, format, type, src); + if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0); + if(!mipmap || max(mw, mh) <= 1) break; + int srcw = mw, srch = mh; + if(mw > 1) mw /= 2; + if(mh > 1) mh /= 2; + if(src) + { + if(!buf) buf = new uchar[mw*mh*bpp]; + scaletexture(src, srcw, srch, bpp, pitch, buf, mw, mh); + } + } + if(buf) delete[] buf; } void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, uchar *data, int align, int blocksize, int levels, bool mipmap) { - int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize, - sizelimit = levels > 1 && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit; - int level = 0; - loopi(levels) - { - int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize; - if(w <= sizelimit && h <= sizelimit) - { - glCompressedTexImage2D_(subtarget, level, format, w, h, 0, size, data); - level++; - if(!mipmap) break; - } - if(max(w, h) <= 1) break; - if(w > 1) w /= 2; - if(h > 1) h /= 2; - data += size; - } + int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize, + sizelimit = levels > 1 && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit; + int level = 0; + loopi(levels) + { + int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize; + if(w <= sizelimit && h <= sizelimit) + { + glCompressedTexImage2D_(subtarget, level, format, w, h, 0, size, data); + level++; + if(!mipmap) break; + } + if(max(w, h) <= 1) break; + if(w > 1) w /= 2; + if(h > 1) h /= 2; + data += size; + } } GLenum textarget(GLenum subtarget) { - switch(subtarget) - { - case GL_TEXTURE_CUBE_MAP_POSITIVE_X: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: - return GL_TEXTURE_CUBE_MAP; - } - return subtarget; + switch(subtarget) + { + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + return GL_TEXTURE_CUBE_MAP; + } + return subtarget; } const GLint *swizzlemask(GLenum format) { - static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - static const GLint luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; - switch(format) - { - case GL_RED: return luminance; - case GL_RG: return luminancealpha; - } - return NULL; + static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + static const GLint luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; + switch(format) + { + case GL_RED: return luminance; + case GL_RG: return luminancealpha; + } + return NULL; } void setuptexparameters(int tnum, void *pixels, int clamp, int filter, GLenum format, GLenum target, bool swizzle) { - glBindTexture(target, tnum); - glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT)); - glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT)); - if(target==GL_TEXTURE_2D && hasAF && min(aniso, hwmaxaniso) > 0 && filter > 1) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso)); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, - filter > 1 ? - (trilinear ? - (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) : - (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) : - (filter && bilinear ? GL_LINEAR : GL_NEAREST)); - if(swizzle && hasTRG && hasTSW) - { - const GLint *mask = swizzlemask(format); - if(mask) glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask); - } + glBindTexture(target, tnum); + glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT)); + glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT)); + if(target==GL_TEXTURE_2D && hasAF && min(aniso, hwmaxaniso) > 0 && filter > 1) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso)); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, + filter > 1 ? + (trilinear ? + (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) : + (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) : + (filter && bilinear ? GL_LINEAR : GL_NEAREST)); + if(swizzle && hasTRG && hasTSW) + { + const GLint *mask = swizzlemask(format); + if(mask) glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask); + } } static GLenum textype(GLenum &component, GLenum &format) { - GLenum type = GL_UNSIGNED_BYTE; - switch(component) - { - case GL_R16F: - case GL_R32F: - if(!format) format = GL_RED; - type = GL_FLOAT; - break; - - case GL_RG16F: - case GL_RG32F: - if(!format) format = GL_RG; - type = GL_FLOAT; - break; - - case GL_RGB16F: - case GL_RGB32F: - if(!format) format = GL_RGB; - type = GL_FLOAT; - break; - - case GL_RGBA16F: - case GL_RGBA32F: - if(!format) format = GL_RGBA; - type = GL_FLOAT; - break; - - case GL_DEPTH_COMPONENT16: - case GL_DEPTH_COMPONENT24: - case GL_DEPTH_COMPONENT32: - if(!format) format = GL_DEPTH_COMPONENT; - break; - - case GL_RGB5: - case GL_RGB8: - case GL_RGB10: - case GL_RGB16: - case GL_COMPRESSED_RGB: - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - if(!format) format = GL_RGB; - break; - - case GL_RGB5_A1: - case GL_RGBA8: - case GL_RGB10_A2: - case GL_RGBA16: - case GL_COMPRESSED_RGBA: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - if(!format) format = GL_RGBA; - break; - - case GL_DEPTH_STENCIL: - case GL_DEPTH24_STENCIL8: - if(!format) format = GL_DEPTH_STENCIL; - type = GL_UNSIGNED_INT_24_8; - break; - - case GL_R8: - case GL_R16: - case GL_COMPRESSED_RED: - case GL_COMPRESSED_RED_RGTC1: - if(!format) format = GL_RED; - break; - - case GL_RG8: - case GL_RG16: - case GL_COMPRESSED_RG: - case GL_COMPRESSED_RG_RGTC2: - if(!format) format = GL_RG; - break; - - case GL_LUMINANCE8: - case GL_LUMINANCE16: - case GL_COMPRESSED_LUMINANCE: - case GL_COMPRESSED_LUMINANCE_LATC1_EXT: - if(!format) format = GL_LUMINANCE; - break; - - case GL_LUMINANCE8_ALPHA8: - case GL_LUMINANCE16_ALPHA16: - case GL_COMPRESSED_LUMINANCE_ALPHA: - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - if(!format) format = GL_LUMINANCE_ALPHA; - break; - - case GL_ALPHA8: - case GL_ALPHA16: - case GL_COMPRESSED_ALPHA: - if(!format) format = GL_ALPHA; - break; - } - if(!format) format = component; - return type; + GLenum type = GL_UNSIGNED_BYTE; + switch(component) + { + case GL_R16F: + case GL_R32F: + if(!format) format = GL_RED; + type = GL_FLOAT; + break; + + case GL_RG16F: + case GL_RG32F: + if(!format) format = GL_RG; + type = GL_FLOAT; + break; + + case GL_RGB16F: + case GL_RGB32F: + if(!format) format = GL_RGB; + type = GL_FLOAT; + break; + + case GL_RGBA16F: + case GL_RGBA32F: + if(!format) format = GL_RGBA; + type = GL_FLOAT; + break; + + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + if(!format) format = GL_DEPTH_COMPONENT; + break; + + case GL_RGB5: + case GL_RGB8: + case GL_RGB10: + case GL_RGB16: + case GL_COMPRESSED_RGB: + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + if(!format) format = GL_RGB; + break; + + case GL_RGB5_A1: + case GL_RGBA8: + case GL_RGB10_A2: + case GL_RGBA16: + case GL_COMPRESSED_RGBA: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + if(!format) format = GL_RGBA; + break; + + case GL_DEPTH_STENCIL: + case GL_DEPTH24_STENCIL8: + if(!format) format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + break; + + case GL_R8: + case GL_R16: + case GL_COMPRESSED_RED: + case GL_COMPRESSED_RED_RGTC1: + if(!format) format = GL_RED; + break; + + case GL_RG8: + case GL_RG16: + case GL_COMPRESSED_RG: + case GL_COMPRESSED_RG_RGTC2: + if(!format) format = GL_RG; + break; + + case GL_LUMINANCE8: + case GL_LUMINANCE16: + case GL_COMPRESSED_LUMINANCE: + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + if(!format) format = GL_LUMINANCE; + break; + + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE16_ALPHA16: + case GL_COMPRESSED_LUMINANCE_ALPHA: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + if(!format) format = GL_LUMINANCE_ALPHA; + break; + + case GL_ALPHA8: + case GL_ALPHA16: + case GL_COMPRESSED_ALPHA: + if(!format) format = GL_ALPHA; + break; + } + if(!format) format = component; + return type; } void createtexture(int tnum, int w, int h, void *pixels, int clamp, int filter, GLenum component, GLenum subtarget, int pw, int ph, int pitch, bool resize, GLenum format, bool swizzle) { - GLenum target = textarget(subtarget), type = textype(component, format); - if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); - if(!pw) pw = w; - if(!ph) ph = h; - int tw = w, th = h; - bool mipmap = filter > 1 && pixels; - if(resize && pixels) - { - resizetexture(w, h, mipmap, false, target, 0, tw, th); - if(mipmap) component = compressedformat(component, tw, th); - } - uploadtexture(tnum, subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap); + GLenum target = textarget(subtarget), type = textype(component, format); + if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); + if(!pw) pw = w; + if(!ph) ph = h; + int tw = w, th = h; + bool mipmap = filter > 1 && pixels; + if(resize && pixels) + { + resizetexture(w, h, mipmap, false, target, 0, tw, th); + if(mipmap) component = compressedformat(component, tw, th); + } + uploadtexture(tnum, subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap); } void createcompressedtexture(int tnum, int w, int h, uchar *data, int align, int blocksize, int levels, int clamp, int filter, GLenum format, GLenum subtarget, bool swizzle = false) { - GLenum target = textarget(subtarget); - if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, data, clamp, filter, format, target); - uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1); + GLenum target = textarget(subtarget); + if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, data, clamp, filter, format, target); + uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1); } hashnameset textures; @@ -1014,112 +978,112 @@ Texture *notexture = NULL; // used as default, ensured to be loaded static GLenum texformat(int bpp, bool swizzle = false) { - switch(bpp) - { - case 1: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RED : GL_LUMINANCE; - case 2: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RG : GL_LUMINANCE_ALPHA; - case 3: return GL_RGB; - case 4: return GL_RGBA; - default: return 0; - } + switch(bpp) + { + case 1: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RED : GL_LUMINANCE; + case 2: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RG : GL_LUMINANCE_ALPHA; + case 3: return GL_RGB; + case 4: return GL_RGBA; + default: return 0; + } } static bool alphaformat(GLenum format) { - switch(format) - { - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_RG: - case GL_RGBA: - return true; - default: - return false; - } + switch(format) + { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_RG: + case GL_RGBA: + return true; + default: + return false; + } } int texalign(const void *data, int w, int bpp) { - int stride = w*bpp; - if(stride&1) return 1; - if(stride&2) return 2; - return 4; + int stride = w*bpp; + if(stride&1) return 1; + if(stride&2) return 2; + return 4; } 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) { - if(!t) - { - char *key = newstring(rname); - t = &textures[key]; - t->name = key; - } - - t->clamp = clamp; - t->mipmap = mipit; - t->type = Texture::IMAGE; - if(transient) t->type |= Texture::TRANSIENT; - if(clamp&0x300) t->type |= Texture::MIRROR; - if(!s.data) - { - t->type |= Texture::STUB; - t->w = t->h = t->xs = t->ys = t->bpp = 0; - return t; - } - - bool swizzle = !(clamp&0x10000); - GLenum format; - if(s.compressed) - { - format = uncompressedformat(s.compressed); - t->bpp = formatsize(format); - t->type |= Texture::COMPRESSED; - } - else - { - format = texformat(s.bpp, swizzle); - t->bpp = s.bpp; - if(swizzle && hasTRG && !hasTSW && swizzlemask(format)) - { - swizzleimage(s); - format = texformat(s.bpp, swizzle); - t->bpp = s.bpp; - } - } - if(alphaformat(format)) t->type |= Texture::ALPHA; - t->w = t->xs = s.w; - t->h = t->ys = s.h; - - int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0; - glGenTextures(1, &t->id); - if(s.compressed) - { - uchar *data = s.data; - int levels = s.levels, level = 0; - if(canreduce && texreduce) loopi(min(texreduce, s.levels-1)) - { - data += s.calclevelsize(level++); - levels--; - if(t->w > 1) t->w /= 2; - if(t->h > 1) t->h /= 2; - } - int sizelimit = mipit && maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize; - while(t->w > sizelimit || t->h > sizelimit) - { - data += s.calclevelsize(level++); - levels--; - if(t->w > 1) t->w /= 2; - if(t->h > 1) t->h /= 2; - } - createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle); - } - else - { - resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h); - GLenum component = compressedformat(format, t->w, t->h, compress); - createtexture(t->id, t->w, t->h, s.data, clamp, filter, component, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle); - } - return t; + if(!t) + { + char *key = newstring(rname); + t = &textures[key]; + t->name = key; + } + + t->clamp = clamp; + t->mipmap = mipit; + t->type = Texture::IMAGE; + if(transient) t->type |= Texture::TRANSIENT; + if(clamp&0x300) t->type |= Texture::MIRROR; + if(!s.data) + { + t->type |= Texture::STUB; + t->w = t->h = t->xs = t->ys = t->bpp = 0; + return t; + } + + bool swizzle = !(clamp&0x10000); + GLenum format; + if(s.compressed) + { + format = uncompressedformat(s.compressed); + t->bpp = formatsize(format); + t->type |= Texture::COMPRESSED; + } + else + { + format = texformat(s.bpp, swizzle); + t->bpp = s.bpp; + if(swizzle && hasTRG && !hasTSW && swizzlemask(format)) + { + swizzleimage(s); + format = texformat(s.bpp, swizzle); + t->bpp = s.bpp; + } + } + if(alphaformat(format)) t->type |= Texture::ALPHA; + t->w = t->xs = s.w; + t->h = t->ys = s.h; + + int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0; + glGenTextures(1, &t->id); + if(s.compressed) + { + uchar *data = s.data; + int levels = s.levels, level = 0; + if(canreduce && texreduce) loopi(min(texreduce, s.levels-1)) + { + data += s.calclevelsize(level++); + levels--; + if(t->w > 1) t->w /= 2; + if(t->h > 1) t->h /= 2; + } + int sizelimit = mipit && maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize; + while(t->w > sizelimit || t->h > sizelimit) + { + data += s.calclevelsize(level++); + levels--; + if(t->w > 1) t->w /= 2; + if(t->h > 1) t->h /= 2; + } + createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle); + } + else + { + resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h); + GLenum component = compressedformat(format, t->w, t->h, compress); + createtexture(t->id, t->w, t->h, s.data, clamp, filter, component, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle); + } + return t; } #if SDL_BYTEORDER == SDL_BIG_ENDIAN @@ -1132,435 +1096,435 @@ static Texture *newtexture(Texture *t, const char *rname, ImageData &s, int clam SDL_Surface *wrapsurface(void *data, int width, int height, int bpp) { - switch(bpp) - { - case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS); - case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS); - } - return NULL; + switch(bpp) + { + case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS); + case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS); + } + return NULL; } SDL_Surface *creatergbsurface(SDL_Surface *os) { - SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS); - if(ns) SDL_BlitSurface(os, NULL, ns, NULL); - SDL_FreeSurface(os); - return ns; + SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS); + if(ns) SDL_BlitSurface(os, NULL, ns, NULL); + SDL_FreeSurface(os); + return ns; } SDL_Surface *creatergbasurface(SDL_Surface *os) { - SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS); - if(ns) - { - SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE); - SDL_BlitSurface(os, NULL, ns, NULL); - } - SDL_FreeSurface(os); - return ns; + SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS); + if(ns) + { + SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE); + SDL_BlitSurface(os, NULL, ns, NULL); + } + SDL_FreeSurface(os); + return ns; } bool checkgrayscale(SDL_Surface *s) { - // gray scale images have 256 levels, no colorkey, and the palette is a ramp - if(s->format->palette) - { - if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, NULL) >= 0) return false; - const SDL_Color *colors = s->format->palette->colors; - loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false; - } - return true; + // gray scale images have 256 levels, no colorkey, and the palette is a ramp + if(s->format->palette) + { + if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, NULL) >= 0) return false; + const SDL_Color *colors = s->format->palette->colors; + loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false; + } + return true; } SDL_Surface *fixsurfaceformat(SDL_Surface *s) { - if(!s) return NULL; - if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0) - { - SDL_FreeSurface(s); - return NULL; - } - static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS }; - switch(s->format->BytesPerPixel) - { - case 1: - if(!checkgrayscale(s)) return SDL_GetColorKey(s, NULL) >= 0 ? creatergbasurface(s) : creatergbsurface(s); - break; - case 3: - if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2]) - return creatergbsurface(s); - break; - case 4: - if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3]) - return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s); - break; - } - return s; + if(!s) return NULL; + if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0) + { + SDL_FreeSurface(s); + return NULL; + } + static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS }; + switch(s->format->BytesPerPixel) + { + case 1: + if(!checkgrayscale(s)) return SDL_GetColorKey(s, NULL) >= 0 ? creatergbasurface(s) : creatergbsurface(s); + break; + case 3: + if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2]) + return creatergbsurface(s); + break; + case 4: + if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3]) + return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s); + break; + } + return s; } void texflip(ImageData &s) { - ImageData d(s.w, s.h, s.bpp); - uchar *dst = d.data, *src = &s.data[s.pitch*s.h]; - loopi(s.h) - { - src -= s.pitch; - memcpy(dst, src, s.bpp*s.w); - dst += d.pitch; - } - s.replace(d); + ImageData d(s.w, s.h, s.bpp); + uchar *dst = d.data, *src = &s.data[s.pitch*s.h]; + loopi(s.h) + { + src -= s.pitch; + memcpy(dst, src, s.bpp*s.w); + dst += d.pitch; + } + s.replace(d); } void texnormal(ImageData &s, int emphasis) { - ImageData d(s.w, s.h, 3); - uchar *src = s.data, *dst = d.data; - loop(y, s.h) loop(x, s.w) - { - vec normal(0.0f, 0.0f, 255.0f/emphasis); - normal.x += src[y*s.pitch + ((x+s.w-1)%s.w)*s.bpp]; - normal.x -= src[y*s.pitch + ((x+1)%s.w)*s.bpp]; - normal.y += src[((y+s.h-1)%s.h)*s.pitch + x*s.bpp]; - normal.y -= src[((y+1)%s.h)*s.pitch + x*s.bpp]; - normal.normalize(); - *dst++ = uchar(127.5f + normal.x*127.5f); - *dst++ = uchar(127.5f + normal.y*127.5f); - *dst++ = uchar(127.5f + normal.z*127.5f); - } - s.replace(d); + ImageData d(s.w, s.h, 3); + uchar *src = s.data, *dst = d.data; + loop(y, s.h) loop(x, s.w) + { + vec normal(0.0f, 0.0f, 255.0f/emphasis); + normal.x += src[y*s.pitch + ((x+s.w-1)%s.w)*s.bpp]; + normal.x -= src[y*s.pitch + ((x+1)%s.w)*s.bpp]; + normal.y += src[((y+s.h-1)%s.h)*s.pitch + x*s.bpp]; + normal.y -= src[((y+1)%s.h)*s.pitch + x*s.bpp]; + normal.normalize(); + *dst++ = uchar(127.5f + normal.x*127.5f); + *dst++ = uchar(127.5f + normal.y*127.5f); + *dst++ = uchar(127.5f + normal.z*127.5f); + } + s.replace(d); } template static void blurtexture(int w, int h, uchar *dst, const uchar *src, int margin) { - static const int weights3x3[9] = - { - 0x10, 0x20, 0x10, - 0x20, 0x40, 0x20, - 0x10, 0x20, 0x10 - }; - static const int weights5x5[25] = - { - 0x05, 0x05, 0x09, 0x05, 0x05, - 0x05, 0x0A, 0x14, 0x0A, 0x05, - 0x09, 0x14, 0x28, 0x14, 0x09, - 0x05, 0x0A, 0x14, 0x0A, 0x05, - 0x05, 0x05, 0x09, 0x05, 0x05 - }; - const int *mat = n > 1 ? weights5x5 : weights3x3; - int mstride = 2*n + 1, - mstartoffset = n*(mstride + 1), - stride = bpp*w, - startoffset = n*bpp, - nextoffset1 = stride + mstride*bpp, - nextoffset2 = stride - mstride*bpp; - src += margin*(stride + bpp); - for(int y = margin; y < h-margin; y++) - { - for(int x = margin; x < w-margin; x++) - { - int dr = 0, dg = 0, db = 0; - const uchar *p = src - startoffset; - const int *m = mat + mstartoffset; - for(int t = y; t >= y-n; t--, p -= nextoffset1, m -= mstride) - { - if(t < 0) p += stride; - int a = 0; - if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; } - a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; - int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp; - if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp; - if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; } - } - p = src - startoffset + stride; - m = mat + mstartoffset + mstride; - for(int t = y+1; t <= y+n; t++, p += nextoffset2, m += mstride) - { - if(t >= h) p -= stride; - int a = 0; - if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; } - a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; - int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp; - if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp; - if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; } - } - if(normals) - { - vec v(dr-0x7F80, dg-0x7F80, db-0x7F80); - float mag = 127.5f/v.magnitude(); - dst[0] = uchar(v.x*mag + 127.5f); - dst[1] = uchar(v.y*mag + 127.5f); - dst[2] = uchar(v.z*mag + 127.5f); - } - else - { - dst[0] = dr>>8; - dst[1] = dg>>8; - dst[2] = db>>8; - } - if(bpp > 3) dst[3] = src[3]; - dst += bpp; - src += bpp; - } - src += 2*margin*bpp; - } + static const int weights3x3[9] = + { + 0x10, 0x20, 0x10, + 0x20, 0x40, 0x20, + 0x10, 0x20, 0x10 + }; + static const int weights5x5[25] = + { + 0x05, 0x05, 0x09, 0x05, 0x05, + 0x05, 0x0A, 0x14, 0x0A, 0x05, + 0x09, 0x14, 0x28, 0x14, 0x09, + 0x05, 0x0A, 0x14, 0x0A, 0x05, + 0x05, 0x05, 0x09, 0x05, 0x05 + }; + const int *mat = n > 1 ? weights5x5 : weights3x3; + int mstride = 2*n + 1, + mstartoffset = n*(mstride + 1), + stride = bpp*w, + startoffset = n*bpp, + nextoffset1 = stride + mstride*bpp, + nextoffset2 = stride - mstride*bpp; + src += margin*(stride + bpp); + for(int y = margin; y < h-margin; y++) + { + for(int x = margin; x < w-margin; x++) + { + int dr = 0, dg = 0, db = 0; + const uchar *p = src - startoffset; + const int *m = mat + mstartoffset; + for(int t = y; t >= y-n; t--, p -= nextoffset1, m -= mstride) + { + if(t < 0) p += stride; + int a = 0; + if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; } + a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; + int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp; + if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp; + if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; } + } + p = src - startoffset + stride; + m = mat + mstartoffset + mstride; + for(int t = y+1; t <= y+n; t++, p += nextoffset2, m += mstride) + { + if(t >= h) p -= stride; + int a = 0; + if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; } + a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; + int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp; + if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp; + if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; } + } + if(normals) + { + vec v(dr-0x7F80, dg-0x7F80, db-0x7F80); + float mag = 127.5f/v.magnitude(); + dst[0] = uchar(v.x*mag + 127.5f); + dst[1] = uchar(v.y*mag + 127.5f); + dst[2] = uchar(v.z*mag + 127.5f); + } + else + { + dst[0] = dr>>8; + dst[1] = dg>>8; + dst[2] = db>>8; + } + if(bpp > 3) dst[3] = src[3]; + dst += bpp; + src += bpp; + } + src += 2*margin*bpp; + } } void blurtexture(int n, int bpp, int w, int h, uchar *dst, const uchar *src, int margin) { - switch((clamp(n, 1, 2)<<4) | bpp) - { - case 0x13: blurtexture<1, 3, false>(w, h, dst, src, margin); break; - case 0x23: blurtexture<2, 3, false>(w, h, dst, src, margin); break; - case 0x14: blurtexture<1, 4, false>(w, h, dst, src, margin); break; - case 0x24: blurtexture<2, 4, false>(w, h, dst, src, margin); break; - } + switch((clamp(n, 1, 2)<<4) | bpp) + { + case 0x13: blurtexture<1, 3, false>(w, h, dst, src, margin); break; + case 0x23: blurtexture<2, 3, false>(w, h, dst, src, margin); break; + case 0x14: blurtexture<1, 4, false>(w, h, dst, src, margin); break; + case 0x24: blurtexture<2, 4, false>(w, h, dst, src, margin); break; + } } void blurnormals(int n, int w, int h, bvec *dst, const bvec *src, int margin) { - switch(clamp(n, 1, 2)) - { - case 1: blurtexture<1, 3, true>(w, h, dst->v, src->v, margin); break; - case 2: blurtexture<2, 3, true>(w, h, dst->v, src->v, margin); break; - } + switch(clamp(n, 1, 2)) + { + case 1: blurtexture<1, 3, true>(w, h, dst->v, src->v, margin); break; + case 2: blurtexture<2, 3, true>(w, h, dst->v, src->v, margin); break; + } } void texblur(ImageData &s, int n, int r) { - if(s.bpp < 3) return; - loopi(r) - { - ImageData d(s.w, s.h, s.bpp); - blurtexture(n, s.bpp, s.w, s.h, d.data, s.data); - s.replace(d); - } + if(s.bpp < 3) return; + loopi(r) + { + ImageData d(s.w, s.h, s.bpp); + blurtexture(n, s.bpp, s.w, s.h, d.data, s.data); + s.replace(d); + } } void scaleimage(ImageData &s, int w, int h) { - ImageData d(w, h, s.bpp); - scaletexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, w, h); - s.replace(d); + ImageData d(w, h, s.bpp); + scaletexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, w, h); + s.replace(d); } bool canloadsurface(const char *name) { - stream *f = openfile(name, "rb"); - if(!f) return false; - delete f; - return true; + stream *f = openfile(name, "rb"); + if(!f) return false; + delete f; + return true; } SDL_Surface *loadsurface(const char *name) { - SDL_Surface *s = NULL; - stream *z = openzipfile(name, "rb"); - if(z) - { - SDL_RWops *rw = z->rwops(); - if(rw) - { - char *ext = (char *)strrchr(name, '.'); - if(ext) ++ext; - s = IMG_LoadTyped_RW(rw, 0, ext); - SDL_FreeRW(rw); - } - delete z; - } - if(!s) s = IMG_Load(findfile(name, "rb")); - return fixsurfaceformat(s); + SDL_Surface *s = NULL; + stream *z = openzipfile(name, "rb"); + if(z) + { + SDL_RWops *rw = z->rwops(); + if(rw) + { + char *ext = (char *)strrchr(name, '.'); + if(ext) ++ext; + s = IMG_LoadTyped_RW(rw, 0, ext); + SDL_FreeRW(rw); + } + delete z; + } + if(!s) s = IMG_Load(findfile(name, "rb")); + return fixsurfaceformat(s); } static vec parsevec(const char *arg) { - vec v(0, 0, 0); - int i = 0; - for(; arg[0] && (!i || arg[0]=='/') && i<3; arg += strcspn(arg, "/,><"), i++) - { - if(i) arg++; - v[i] = atof(arg); - } - if(i==1) v.y = v.z = v.x; - return v; + vec v(0, 0, 0); + int i = 0; + for(; arg[0] && (!i || arg[0]=='/') && i<3; arg += strcspn(arg, "/,><"), i++) + { + if(i) arg++; + v[i] = atof(arg); + } + if(i==1) v.y = v.z = v.x; + return v; } static bool texturedata(ImageData &d, const char *tname, Slot::Tex *tex = NULL, bool msg = true, int *compress = NULL, int *wrap = NULL) { - const char *cmds = NULL, *file = tname; - - if(!tname) - { - if(!tex) return false; - if(tex->name[0]=='<') - { - cmds = tex->name; - file = strrchr(tex->name, '>'); - if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture packages/%s", tex->name); return false; } - file++; - } - else file = tex->name; - - static string pname; - formatstring(pname, "packages/%s", file); - file = path(pname); - } - else if(tname[0]=='<') - { - cmds = tname; - file = strrchr(tname, '>'); - if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture %s", tname); return false; } - file++; - } - - int flen = strlen(file); - bool raw = !compress, guess = false; - for(const char *pcmds = cmds; pcmds;) - { - #define PARSETEXCOMMANDS(cmds) \ - const char *cmd = NULL, *end = NULL, *arg[4] = { NULL, NULL, NULL, NULL }; \ - cmd = &cmds[1]; \ - end = strchr(cmd, '>'); \ - if(!end) break; \ - cmds = strchr(cmd, '<'); \ - size_t len = strcspn(cmd, ":,><"); \ - loopi(4) \ - { \ - arg[i] = strchr(i ? arg[i-1] : cmd, i ? ',' : ':'); \ - if(!arg[i] || arg[i] >= end) arg[i] = ""; \ - else arg[i]++; \ - } - PARSETEXCOMMANDS(pcmds); - if(matchstring(cmd, len, "thumbnail")) - { - raw = true; - guess = flen >= 4 && !strchr(file+flen-4, '.'); - } - else if(matchstring(cmd, len, "stub")) return canloadsurface(file); - } - - if(msg) renderprogress(loadprogress, file); - - if(!d.data) - { - SDL_Surface *s = NULL; - if(guess) - { - static const char *exts[] = {".jpg", ".png"}; - string ext; - loopi(sizeof(exts)/sizeof(exts[0])) - { - copystring(ext, file); - concatstring(ext, exts[i]); - s = loadsurface(ext); - if(s) break; - } - } - else s = loadsurface(file); - if(!s) { if(msg) conoutf(CON_ERROR, "could not load texture %s", file); return false; } - int bpp = s->format->BitsPerPixel; - if(bpp%8 || !texformat(bpp/8)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture must be 8, 16, 24, or 32 bpp: %s", file); return false; } - if(max(s->w, s->h) > (1<<12)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file); return false; } - d.wrap(s); - } - - while(cmds) - { - PARSETEXCOMMANDS(cmds); - if(d.compressed) goto compressed; - if(matchstring(cmd, len, "mad")) texmad(d, parsevec(arg[0]), parsevec(arg[1])); - else if(matchstring(cmd, len, "colorify")) texcolorify(d, parsevec(arg[0]), parsevec(arg[1])); - else if(matchstring(cmd, len, "colormask")) texcolormask(d, parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1)); - else if(matchstring(cmd, len, "normal")) - { - int emphasis = atoi(arg[0]); - texnormal(d, emphasis > 0 ? emphasis : 3); - } - else if(matchstring(cmd, len, "dup")) texdup(d, atoi(arg[0]), atoi(arg[1])); - else if(matchstring(cmd, len, "offset")) texoffset(d, atoi(arg[0]), atoi(arg[1])); - else if(matchstring(cmd, len, "rotate")) texrotate(d, atoi(arg[0]), tex ? tex->type : 0); - else if(matchstring(cmd, len, "reorient")) texreorient(d, atoi(arg[0])>0, atoi(arg[1])>0, atoi(arg[2])>0, tex ? tex->type : TEX_DIFFUSE); - else if(matchstring(cmd, len, "mix")) texmix(d, *arg[0] ? atoi(arg[0]) : -1, *arg[1] ? atoi(arg[1]) : -1, *arg[2] ? atoi(arg[2]) : -1, *arg[3] ? atoi(arg[3]) : -1); - else if(matchstring(cmd, len, "grey")) texgrey(d); - else if(matchstring(cmd, len, "blur")) - { - int emphasis = atoi(arg[0]), repeat = atoi(arg[1]); - texblur(d, emphasis > 0 ? clamp(emphasis, 1, 2) : 1, repeat > 0 ? repeat : 1); - } - else if(matchstring(cmd, len, "premul")) texpremul(d); - else if(matchstring(cmd, len, "agrad")) texagrad(d, atof(arg[0]), atof(arg[1]), atof(arg[2]), atof(arg[3])); - else if(matchstring(cmd, len, "compress")) - { - int scale = atoi(arg[0]); - if(scale <= 0) scale = 2; - if(compress) *compress = scale; - } - else if(matchstring(cmd, len, "nocompress")) - { - if(compress) *compress = -1; - } - else if(matchstring(cmd, len, "thumbnail")) - { - int w = atoi(arg[0]), h = atoi(arg[1]); - if(w <= 0 || w > (1<<12)) w = 64; - if(h <= 0 || h > (1<<12)) h = w; - if(d.w > w || d.h > h) scaleimage(d, w, h); - } - else - compressed: - if(matchstring(cmd, len, "mirror")) - { - if(wrap) *wrap |= 0x300; - } - else if(matchstring(cmd, len, "noswizzle")) - { - if(wrap) *wrap |= 0x10000; - } - } - - return true; + const char *cmds = NULL, *file = tname; + + if(!tname) + { + if(!tex) return false; + if(tex->name[0]=='<') + { + cmds = tex->name; + file = strrchr(tex->name, '>'); + if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture packages/%s", tex->name); return false; } + file++; + } + else file = tex->name; + + static string pname; + formatstring(pname, "packages/%s", file); + file = path(pname); + } + else if(tname[0]=='<') + { + cmds = tname; + file = strrchr(tname, '>'); + if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture %s", tname); return false; } + file++; + } + + int flen = strlen(file); + bool raw = !compress, guess = false; + for(const char *pcmds = cmds; pcmds;) + { + #define PARSETEXCOMMANDS(cmds) \ + const char *cmd = NULL, *end = NULL, *arg[4] = { NULL, NULL, NULL, NULL }; \ + cmd = &cmds[1]; \ + end = strchr(cmd, '>'); \ + if(!end) break; \ + cmds = strchr(cmd, '<'); \ + size_t len = strcspn(cmd, ":,><"); \ + loopi(4) \ + { \ + arg[i] = strchr(i ? arg[i-1] : cmd, i ? ',' : ':'); \ + if(!arg[i] || arg[i] >= end) arg[i] = ""; \ + else arg[i]++; \ + } + PARSETEXCOMMANDS(pcmds); + if(matchstring(cmd, len, "thumbnail")) + { + raw = true; + guess = flen >= 4 && !strchr(file+flen-4, '.'); + } + else if(matchstring(cmd, len, "stub")) return canloadsurface(file); + } + + if(msg) renderprogress(loadprogress, file); + + if(!d.data) + { + SDL_Surface *s = NULL; + if(guess) + { + static const char *exts[] = {".jpg", ".png"}; + string ext; + loopi(sizeof(exts)/sizeof(exts[0])) + { + copystring(ext, file); + concatstring(ext, exts[i]); + s = loadsurface(ext); + if(s) break; + } + } + else s = loadsurface(file); + if(!s) { if(msg) conoutf(CON_ERROR, "could not load texture %s", file); return false; } + int bpp = s->format->BitsPerPixel; + if(bpp%8 || !texformat(bpp/8)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture must be 8, 16, 24, or 32 bpp: %s", file); return false; } + if(max(s->w, s->h) > (1<<12)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file); return false; } + d.wrap(s); + } + + while(cmds) + { + PARSETEXCOMMANDS(cmds); + if(d.compressed) goto compressed; + if(matchstring(cmd, len, "mad")) texmad(d, parsevec(arg[0]), parsevec(arg[1])); + else if(matchstring(cmd, len, "colorify")) texcolorify(d, parsevec(arg[0]), parsevec(arg[1])); + else if(matchstring(cmd, len, "colormask")) texcolormask(d, parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1)); + else if(matchstring(cmd, len, "normal")) + { + int emphasis = atoi(arg[0]); + texnormal(d, emphasis > 0 ? emphasis : 3); + } + else if(matchstring(cmd, len, "dup")) texdup(d, atoi(arg[0]), atoi(arg[1])); + else if(matchstring(cmd, len, "offset")) texoffset(d, atoi(arg[0]), atoi(arg[1])); + else if(matchstring(cmd, len, "rotate")) texrotate(d, atoi(arg[0]), tex ? tex->type : 0); + else if(matchstring(cmd, len, "reorient")) texreorient(d, atoi(arg[0])>0, atoi(arg[1])>0, atoi(arg[2])>0, tex ? tex->type : TEX_DIFFUSE); + else if(matchstring(cmd, len, "mix")) texmix(d, *arg[0] ? atoi(arg[0]) : -1, *arg[1] ? atoi(arg[1]) : -1, *arg[2] ? atoi(arg[2]) : -1, *arg[3] ? atoi(arg[3]) : -1); + else if(matchstring(cmd, len, "grey")) texgrey(d); + else if(matchstring(cmd, len, "blur")) + { + int emphasis = atoi(arg[0]), repeat = atoi(arg[1]); + texblur(d, emphasis > 0 ? clamp(emphasis, 1, 2) : 1, repeat > 0 ? repeat : 1); + } + else if(matchstring(cmd, len, "premul")) texpremul(d); + else if(matchstring(cmd, len, "agrad")) texagrad(d, atof(arg[0]), atof(arg[1]), atof(arg[2]), atof(arg[3])); + else if(matchstring(cmd, len, "compress")) + { + int scale = atoi(arg[0]); + if(scale <= 0) scale = 2; + if(compress) *compress = scale; + } + else if(matchstring(cmd, len, "nocompress")) + { + if(compress) *compress = -1; + } + else if(matchstring(cmd, len, "thumbnail")) + { + int w = atoi(arg[0]), h = atoi(arg[1]); + if(w <= 0 || w > (1<<12)) w = 64; + if(h <= 0 || h > (1<<12)) h = w; + if(d.w > w || d.h > h) scaleimage(d, w, h); + } + else + compressed: + if(matchstring(cmd, len, "mirror")) + { + if(wrap) *wrap |= 0x300; + } + else if(matchstring(cmd, len, "noswizzle")) + { + if(wrap) *wrap |= 0x10000; + } + } + + return true; } uchar *loadalphamask(Texture *t) { - if(t->alphamask) return t->alphamask; - if(!(t->type&Texture::ALPHA)) return NULL; - ImageData s; - if(!texturedata(s, t->name, NULL, false) || !s.data || s.compressed) return NULL; - t->alphamask = new uchar[s.h * ((s.w+7)/8)]; - uchar *srcrow = s.data, *dst = t->alphamask-1; - loop(y, s.h) - { - uchar *src = srcrow+s.bpp-1; - loop(x, s.w) - { - int offset = x%8; - if(!offset) *++dst = 0; - if(*src) *dst |= 1<alphamask; + if(t->alphamask) return t->alphamask; + if(!(t->type&Texture::ALPHA)) return NULL; + ImageData s; + if(!texturedata(s, t->name, NULL, false) || !s.data || s.compressed) return NULL; + t->alphamask = new uchar[s.h * ((s.w+7)/8)]; + uchar *srcrow = s.data, *dst = t->alphamask-1; + loop(y, s.h) + { + uchar *src = srcrow+s.bpp-1; + loop(x, s.w) + { + int offset = x%8; + if(!offset) *++dst = 0; + if(*src) *dst |= 1<alphamask; } Texture *textureload(const char *name, int clamp, bool mipit, bool msg) { - string tname; - copystring(tname, name); - Texture *t = textures.access(path(tname)); - if(t) return t; - int compress = 0; - ImageData s; - if(texturedata(s, tname, NULL, msg, &compress, &clamp)) return newtexture(NULL, tname, s, clamp, mipit, false, false, compress); - return notexture; + string tname; + copystring(tname, name); + Texture *t = textures.access(path(tname)); + if(t) return t; + int compress = 0; + ImageData s; + if(texturedata(s, tname, NULL, msg, &compress, &clamp)) return newtexture(NULL, tname, s, clamp, mipit, false, false, compress); + return notexture; } bool settexture(const char *name, int clamp) { - Texture *t = textureload(name, clamp, true, false); - glBindTexture(GL_TEXTURE_2D, t->id); - return t != notexture; + Texture *t = textureload(name, clamp, true, false); + glBindTexture(GL_TEXTURE_2D, t->id); + return t != notexture; } vector vslots; @@ -1571,30 +1535,30 @@ VSlot dummyvslot(&dummyslot); void texturereset(int *n) { - if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return; - resetslotshader(); - int limit = clamp(*n, 0, slots.length()); - for(int i = limit; i < slots.length(); i++) - { - Slot *s = slots[i]; - for(VSlot *vs = s->variants; vs; vs = vs->next) vs->slot = &dummyslot; - delete s; - } - slots.setsize(limit); - while(vslots.length()) - { - VSlot *vs = vslots.last(); - if(vs->slot != &dummyslot || vs->changed) break; - delete vslots.pop(); - } + if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return; + resetslotshader(); + int limit = clamp(*n, 0, slots.length()); + for(int i = limit; i < slots.length(); i++) + { + Slot *s = slots[i]; + for(VSlot *vs = s->variants; vs; vs = vs->next) vs->slot = &dummyslot; + delete s; + } + slots.setsize(limit); + while(vslots.length()) + { + VSlot *vs = vslots.last(); + if(vs->slot != &dummyslot || vs->changed) break; + delete vslots.pop(); + } } COMMAND(texturereset, "i"); void materialreset() { - if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return; - loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset(); + if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return; + loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset(); } COMMAND(materialreset, ""); @@ -1604,1042 +1568,1005 @@ static bool markingvslots = false; void clearslots() { - resetslotshader(); - slots.deletecontents(); - vslots.deletecontents(); - loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset(); - clonedvslots = 0; + resetslotshader(); + slots.deletecontents(); + vslots.deletecontents(); + loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset(); + clonedvslots = 0; } static void assignvslot(VSlot &vs); static inline void assignvslotlayer(VSlot &vs) { - if(vs.layer && vslots.inrange(vs.layer)) - { - VSlot &layer = *vslots[vs.layer]; - if(layer.index < 0) assignvslot(layer); - } + if(vs.layer && vslots.inrange(vs.layer)) + { + VSlot &layer = *vslots[vs.layer]; + if(layer.index < 0) assignvslot(layer); + } } static void assignvslot(VSlot &vs) { - vs.index = compactedvslots++; - assignvslotlayer(vs); + vs.index = compactedvslots++; + assignvslotlayer(vs); } void compactvslot(int &index) { - if(vslots.inrange(index)) - { - VSlot &vs = *vslots[index]; - if(vs.index < 0) assignvslot(vs); - if(!markingvslots) index = vs.index; - } + if(vslots.inrange(index)) + { + VSlot &vs = *vslots[index]; + if(vs.index < 0) assignvslot(vs); + if(!markingvslots) index = vs.index; + } } void compactvslot(VSlot &vs) { - if(vs.index < 0) assignvslot(vs); + if(vs.index < 0) assignvslot(vs); } void compactvslots(cube *c, int n) { - if((compactvslotsprogress++&0xFFF)==0) renderprogress(min(float(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots..."); - loopi(n) - { - if(c[i].children) compactvslots(c[i].children); - else loopj(6) if(vslots.inrange(c[i].texture[j])) - { - VSlot &vs = *vslots[c[i].texture[j]]; - if(vs.index < 0) assignvslot(vs); - if(!markingvslots) c[i].texture[j] = vs.index; - } - } + if((compactvslotsprogress++&0xFFF)==0) renderprogress(min(float(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots..."); + loopi(n) + { + if(c[i].children) compactvslots(c[i].children); + else loopj(6) if(vslots.inrange(c[i].texture[j])) + { + VSlot &vs = *vslots[c[i].texture[j]]; + if(vs.index < 0) assignvslot(vs); + if(!markingvslots) c[i].texture[j] = vs.index; + } + } } int compactvslots() { - clonedvslots = 0; - markingvslots = false; - compactedvslots = 0; - compactvslotsprogress = 0; - loopv(vslots) vslots[i]->index = -1; - loopv(slots) slots[i]->variants->index = compactedvslots++; - loopv(slots) assignvslotlayer(*slots[i]->variants); - loopv(vslots) - { - VSlot &vs = *vslots[i]; - if(!vs.changed && vs.index < 0) { markingvslots = true; break; } - } - compactvslots(worldroot); - int total = compactedvslots; - compacteditvslots(); - loopv(vslots) - { - VSlot *vs = vslots[i]; - if(vs->changed) continue; - while(vs->next) - { - if(vs->next->index < 0) vs->next = vs->next->next; - else vs = vs->next; - } - } - if(markingvslots) - { - markingvslots = false; - compactedvslots = 0; - compactvslotsprogress = 0; - int lastdiscard = 0; - loopv(vslots) - { - VSlot &vs = *vslots[i]; - if(vs.changed || (vs.index < 0 && !vs.next)) vs.index = -1; - else - { - while(lastdiscard < i) - { - VSlot &ds = *vslots[lastdiscard++]; - if(!ds.changed && ds.index < 0) ds.index = compactedvslots++; - } - vs.index = compactedvslots++; - } - } - compactvslots(worldroot); - total = compactedvslots; - compacteditvslots(); - } - compactmruvslots(); - loopv(vslots) - { - VSlot &vs = *vslots[i]; - if(vs.index >= 0 && vs.layer && vslots.inrange(vs.layer)) vs.layer = vslots[vs.layer]->index; - } - loopv(vslots) - { - while(vslots[i]->index >= 0 && vslots[i]->index != i) - swap(vslots[i], vslots[vslots[i]->index]); - } - for(int i = compactedvslots; i < vslots.length(); i++) delete vslots[i]; - vslots.setsize(compactedvslots); - return total; + clonedvslots = 0; + markingvslots = false; + compactedvslots = 0; + compactvslotsprogress = 0; + loopv(vslots) vslots[i]->index = -1; + loopv(slots) slots[i]->variants->index = compactedvslots++; + loopv(slots) assignvslotlayer(*slots[i]->variants); + loopv(vslots) + { + VSlot &vs = *vslots[i]; + if(!vs.changed && vs.index < 0) { markingvslots = true; break; } + } + compactvslots(worldroot); + int total = compactedvslots; + compacteditvslots(); + loopv(vslots) + { + VSlot *vs = vslots[i]; + if(vs->changed) continue; + while(vs->next) + { + if(vs->next->index < 0) vs->next = vs->next->next; + else vs = vs->next; + } + } + if(markingvslots) + { + markingvslots = false; + compactedvslots = 0; + compactvslotsprogress = 0; + int lastdiscard = 0; + loopv(vslots) + { + VSlot &vs = *vslots[i]; + if(vs.changed || (vs.index < 0 && !vs.next)) vs.index = -1; + else + { + while(lastdiscard < i) + { + VSlot &ds = *vslots[lastdiscard++]; + if(!ds.changed && ds.index < 0) ds.index = compactedvslots++; + } + vs.index = compactedvslots++; + } + } + compactvslots(worldroot); + total = compactedvslots; + compacteditvslots(); + } + compactmruvslots(); + loopv(vslots) + { + VSlot &vs = *vslots[i]; + if(vs.index >= 0 && vs.layer && vslots.inrange(vs.layer)) vs.layer = vslots[vs.layer]->index; + } + loopv(vslots) + { + while(vslots[i]->index >= 0 && vslots[i]->index != i) + swap(vslots[i], vslots[vslots[i]->index]); + } + for(int i = compactedvslots; i < vslots.length(); i++) delete vslots[i]; + vslots.setsize(compactedvslots); + return total; } ICOMMAND(compactvslots, "", (), { - extern int nompedit; - if(nompedit && multiplayer()) return; - compactvslots(); - allchanged(); + extern int nompedit; + if(nompedit && multiplayer()) return; + compactvslots(); + allchanged(); }); static Slot &loadslot(Slot &s, bool forceload); static void clampvslotoffset(VSlot &dst, Slot *slot = NULL) { - if(!slot) slot = dst.slot; - if(slot && slot->sts.inrange(0)) - { - if(!slot->loaded) loadslot(*slot, false); - Texture *t = slot->sts[0].t; - int xs = t->xs, ys = t->ys; - if(t->type & Texture::MIRROR) { xs *= 2; ys *= 2; } - if(texrotations[dst.rotation].swapxy) swap(xs, ys); - dst.offset.x %= xs; if(dst.offset.x < 0) dst.offset.x += xs; - dst.offset.y %= ys; if(dst.offset.y < 0) dst.offset.y += ys; - } - else dst.offset.max(0); + if(!slot) slot = dst.slot; + if(slot && slot->sts.inrange(0)) + { + if(!slot->loaded) loadslot(*slot, false); + Texture *t = slot->sts[0].t; + int xs = t->xs, ys = t->ys; + if(t->type & Texture::MIRROR) { xs *= 2; ys *= 2; } + if(texrotations[dst.rotation].swapxy) swap(xs, ys); + dst.offset.x %= xs; if(dst.offset.x < 0) dst.offset.x += xs; + dst.offset.y %= ys; if(dst.offset.y < 0) dst.offset.y += ys; + } + else dst.offset.max(0); } static void propagatevslot(VSlot &dst, const VSlot &src, int diff, bool edit = false) { - if(diff & (1<next; vs; vs = vs->next) - { - int diff = changed & ~vs->changed; - if(diff) propagatevslot(*vs, *root, diff); - } + for(VSlot *vs = root->next; vs; vs = vs->next) + { + int diff = changed & ~vs->changed; + if(diff) propagatevslot(*vs, *root, diff); + } } static void mergevslot(VSlot &dst, const VSlot &src, int diff, Slot *slot = NULL) { - if(diff & (1<slot = &owner; - vs->linked = false; - vs = vs->next; - } - return owner.variants; + owner.variants = vs; + while(vs) + { + vs->slot = &owner; + vs->linked = false; + vs = vs->next; + } + return owner.variants; } static VSlot *emptyvslot(Slot &owner) { - int offset = 0; - loopvrev(slots) if(slots[i]->variants) { offset = slots[i]->variants->index + 1; break; } - for(int i = offset; i < vslots.length(); i++) if(!vslots[i]->changed) return reassignvslot(owner, vslots[i]); - return vslots.add(new VSlot(&owner, vslots.length())); + int offset = 0; + loopvrev(slots) if(slots[i]->variants) { offset = slots[i]->variants->index + 1; break; } + for(int i = offset; i < vslots.length(); i++) if(!vslots[i]->changed) return reassignvslot(owner, vslots[i]); + return vslots.add(new VSlot(&owner, vslots.length())); } static bool comparevslot(const VSlot &dst, const VSlot &src, int diff) { - if(diff & (1< &buf, const VSlot &src) { - if(src.changed & (1<changed ? src.layer : 0); - } - if(src.changed & (1<changed ? src.layer : 0); + } + if(src.changed & (1< &buf, int index) { - if(vslots.inrange(index)) packvslot(buf, *vslots[index]); - else buf.put(0xFF); + if(vslots.inrange(index)) packvslot(buf, *vslots[index]); + else buf.put(0xFF); } void packvslot(vector &buf, const VSlot *vs) { - if(vs) packvslot(buf, *vs); - else buf.put(0xFF); + if(vs) packvslot(buf, *vs); + else buf.put(0xFF); } bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta) { - while(buf.remaining()) - { - int changed = buf.get(); - if(changed >= 0x80) break; - switch(changed) - { - case VSLOT_SHPARAM: - { - string name; - getstring(name, buf); - SlotShaderParam p = { name[0] ? getshaderparamname(name) : NULL, -1, { 0, 0, 0, 0 } }; - loopi(4) p.val[i] = getfloat(buf); - if(p.name) dst.params.add(p); - break; - } - case VSLOT_SCALE: - dst.scale = getfloat(buf); - if(dst.scale <= 0) dst.scale = 1; - else if(!delta) dst.scale = clamp(dst.scale, 1/8.0f, 8.0f); - break; - case VSLOT_ROTATION: - dst.rotation = getint(buf); - if(!delta) dst.rotation = clamp(dst.rotation, 0, 7); - break; - case VSLOT_OFFSET: - dst.offset.x = getint(buf); - dst.offset.y = getint(buf); - if(!delta) dst.offset.max(0); - break; - case VSLOT_SCROLL: - dst.scroll.x = getfloat(buf); - dst.scroll.y = getfloat(buf); - break; - case VSLOT_LAYER: - { - int tex = getuint(buf); - dst.layer = vslots.inrange(tex) ? tex : 0; - break; - } - case VSLOT_ALPHA: - dst.alphafront = clamp(getfloat(buf), 0.0f, 1.0f); - dst.alphaback = clamp(getfloat(buf), 0.0f, 1.0f); - break; - case VSLOT_COLOR: - dst.colorscale.r = clamp(getfloat(buf), 0.0f, 2.0f); - dst.colorscale.g = clamp(getfloat(buf), 0.0f, 2.0f); - dst.colorscale.b = clamp(getfloat(buf), 0.0f, 2.0f); - break; - default: - return false; - } - dst.changed |= 1<= 0x80) break; + switch(changed) + { + case VSLOT_SHPARAM: + { + string name; + getstring(name, buf); + SlotShaderParam p = { name[0] ? getshaderparamname(name) : NULL, -1, { 0, 0, 0, 0 } }; + loopi(4) p.val[i] = getfloat(buf); + if(p.name) dst.params.add(p); + break; + } + case VSLOT_SCALE: + dst.scale = getfloat(buf); + if(dst.scale <= 0) dst.scale = 1; + else if(!delta) dst.scale = clamp(dst.scale, 1/8.0f, 8.0f); + break; + case VSLOT_ROTATION: + dst.rotation = getint(buf); + if(!delta) dst.rotation = clamp(dst.rotation, 0, 7); + break; + case VSLOT_OFFSET: + dst.offset.x = getint(buf); + dst.offset.y = getint(buf); + if(!delta) dst.offset.max(0); + break; + case VSLOT_SCROLL: + dst.scroll.x = getfloat(buf); + dst.scroll.y = getfloat(buf); + break; + case VSLOT_LAYER: + { + int tex = getuint(buf); + dst.layer = vslots.inrange(tex) ? tex : 0; + break; + } + case VSLOT_ALPHA: + dst.alphafront = clamp(getfloat(buf), 0.0f, 1.0f); + dst.alphaback = clamp(getfloat(buf), 0.0f, 1.0f); + break; + case VSLOT_COLOR: + dst.colorscale.r = clamp(getfloat(buf), 0.0f, 2.0f); + dst.colorscale.g = clamp(getfloat(buf), 0.0f, 2.0f); + dst.colorscale.b = clamp(getfloat(buf), 0.0f, 2.0f); + break; + default: + return false; + } + dst.changed |= 1<next) - { - if((!dst->changed || dst->changed == (src.changed | delta.changed)) && - comparevslot(*dst, src, src.changed & ~delta.changed) && - comparevslot(*dst, delta, delta.changed)) - return dst; - } - return NULL; + for(VSlot *dst = slot.variants; dst; dst = dst->next) + { + if((!dst->changed || dst->changed == (src.changed | delta.changed)) && + comparevslot(*dst, src, src.changed & ~delta.changed) && + comparevslot(*dst, delta, delta.changed)) + return dst; + } + return NULL; } static VSlot *clonevslot(const VSlot &src, const VSlot &delta) { - VSlot *dst = vslots.add(new VSlot(src.slot, vslots.length())); - dst->changed = src.changed | delta.changed; - propagatevslot(*dst, src, ((1<changed = src.changed | delta.changed; + propagatevslot(*dst, src, ((1<=0x10000) - { - compactvslots(); - allchanged(); - if(vslots.length()>=0x10000) return NULL; - } - if(autocompactvslots && ++clonedvslots >= autocompactvslots) - { - compactvslots(); - allchanged(); - } - return clonevslot(src, delta); + VSlot *exists = findvslot(*src.slot, src, delta); + if(exists) return exists; + if(vslots.length()>=0x10000) + { + compactvslots(); + allchanged(); + if(vslots.length()>=0x10000) return NULL; + } + if(autocompactvslots && ++clonedvslots >= autocompactvslots) + { + compactvslots(); + allchanged(); + } + return clonevslot(src, delta); } static void fixinsidefaces(cube *c, const ivec &o, int size, int tex) { - loopi(8) - { - ivec co(i, o, size); - if(c[i].children) fixinsidefaces(c[i].children, co, size>>1, tex); - else loopj(6) if(!visibletris(c[i], j, co, size)) - c[i].texture[j] = tex; - } + loopi(8) + { + ivec co(i, o, size); + if(c[i].children) fixinsidefaces(c[i].children, co, size>>1, tex); + else loopj(6) if(!visibletris(c[i], j, co, size)) + c[i].texture[j] = tex; + } } ICOMMAND(fixinsidefaces, "i", (int *tex), { - extern int nompedit; - if(noedit(true) || (nompedit && multiplayer())) return; - fixinsidefaces(worldroot, ivec(0, 0, 0), worldsize>>1, *tex && vslots.inrange(*tex) ? *tex : DEFAULT_GEOM); - allchanged(); + extern int nompedit; + if(noedit(true) || (nompedit && multiplayer())) return; + fixinsidefaces(worldroot, ivec(0, 0, 0), worldsize>>1, *tex && vslots.inrange(*tex) ? *tex : DEFAULT_GEOM); + allchanged(); }); const struct slottex { - const char *name; - int id; + const char *name; + int id; } slottexs[] = { - {"c", TEX_DIFFUSE}, - {"u", TEX_UNKNOWN}, - {"d", TEX_DECAL}, - {"n", TEX_NORMAL}, - {"g", TEX_GLOW}, - {"s", TEX_SPEC}, - {"z", TEX_DEPTH}, - {"a", TEX_ALPHA}, - {"e", TEX_ENVMAP} + {"c", TEX_DIFFUSE}, + {"u", TEX_UNKNOWN}, + {"d", TEX_DECAL}, + {"n", TEX_NORMAL}, + {"g", TEX_GLOW}, + {"s", TEX_SPEC}, + {"z", TEX_DEPTH}, + {"a", TEX_ALPHA}, + {"e", TEX_ENVMAP} }; int findslottex(const char *name) { - loopi(sizeof(slottexs)/sizeof(slottex)) - { - if(!strcmp(slottexs[i].name, name)) return slottexs[i].id; - } - return -1; + loopi(sizeof(slottexs)/sizeof(slottex)) + { + if(!strcmp(slottexs[i].name, name)) return slottexs[i].id; + } + return -1; } void texture(char *type, char *name, int *rot, int *xoffset, int *yoffset, float *scale) { - if(slots.length()>=0x10000) return; - static int lastmatslot = -1; - int tnum = findslottex(type), matslot = findmaterial(type); - if(tnum<0) tnum = atoi(type); - if(tnum==TEX_DIFFUSE) lastmatslot = matslot; - else if(lastmatslot>=0) matslot = lastmatslot; - else if(slots.empty()) return; - Slot &s = matslot>=0 ? materialslots[matslot] : *(tnum!=TEX_DIFFUSE ? slots.last() : slots.add(new Slot(slots.length()))); - s.loaded = false; - s.texmask |= 1<=8) conoutf(CON_WARN, "warning: too many textures in slot %d", slots.length()-1); - Slot::Tex &st = s.sts.add(); - st.type = tnum; - st.combined = -1; - st.t = NULL; - copystring(st.name, name); - path(st.name); - if(tnum==TEX_DIFFUSE) - { - setslotshader(s); - VSlot &vs = matslot >= 0 ? materialslots[matslot] : *emptyvslot(s); - vs.reset(); - vs.rotation = clamp(*rot, 0, 7); - vs.offset = ivec2(*xoffset, *yoffset).max(0); - vs.scale = *scale <= 0 ? 1 : *scale; - propagatevslot(&vs, (1<=0x10000) return; + static int lastmatslot = -1; + int tnum = findslottex(type), matslot = findmaterial(type); + if(tnum<0) tnum = atoi(type); + if(tnum==TEX_DIFFUSE) lastmatslot = matslot; + else if(lastmatslot>=0) matslot = lastmatslot; + else if(slots.empty()) return; + Slot &s = matslot>=0 ? materialslots[matslot] : *(tnum!=TEX_DIFFUSE ? slots.last() : slots.add(new Slot(slots.length()))); + s.loaded = false; + s.texmask |= 1<=8) conoutf(CON_WARN, "warning: too many textures in slot %d", slots.length()-1); + Slot::Tex &st = s.sts.add(); + st.type = tnum; + st.combined = -1; + st.t = NULL; + copystring(st.name, name); + path(st.name); + if(tnum==TEX_DIFFUSE) + { + setslotshader(s); + VSlot &vs = matslot >= 0 ? materialslots[matslot] : *emptyvslot(s); + vs.reset(); + vs.rotation = clamp(*rot, 0, 7); + vs.offset = ivec2(*xoffset, *yoffset).max(0); + vs.scale = *scale <= 0 ? 1 : *scale; + propagatevslot(&vs, (1<scroll = vec2(*scrollS, *scrollT).div(1000.0f); - propagatevslot(s.variants, 1<scroll = vec2(*scrollS, *scrollT).div(1000.0f); + propagatevslot(s.variants, 1<offset = ivec2(*xoffset, *yoffset).max(0); - propagatevslot(s.variants, 1<offset = ivec2(*xoffset, *yoffset).max(0); + propagatevslot(s.variants, 1<rotation = clamp(*rot, 0, 7); - propagatevslot(s.variants, 1<rotation = clamp(*rot, 0, 7); + propagatevslot(s.variants, 1<scale = *scale <= 0 ? 1 : *scale; - propagatevslot(s.variants, 1<scale = *scale <= 0 ? 1 : *scale; + propagatevslot(s.variants, 1<layer = *layer < 0 ? max(slots.length()-1+*layer, 0) : *layer; - s.layermaskname = name[0] ? newstring(path(makerelpath("packages", name))) : NULL; - s.layermaskmode = *mode; - s.layermaskscale = *scale <= 0 ? 1 : *scale; - propagatevslot(s.variants, 1<layer = *layer < 0 ? max(slots.length()-1+*layer, 0) : *layer; + s.layermaskname = name[0] ? newstring(path(makerelpath("packages", name))) : NULL; + s.layermaskmode = *mode; + s.layermaskscale = *scale <= 0 ? 1 : *scale; + propagatevslot(s.variants, 1<alphafront = clamp(*front, 0.0f, 1.0f); - s.variants->alphaback = clamp(*back, 0.0f, 1.0f); - propagatevslot(s.variants, 1<alphafront = clamp(*front, 0.0f, 1.0f); + s.variants->alphaback = clamp(*back, 0.0f, 1.0f); + propagatevslot(s.variants, 1<colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f)); - propagatevslot(s.variants, 1<colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f)); + propagatevslot(s.variants, 1< &key, Slot &slot, Slot::Tex &t, bool combined = false, const char *prefix = NULL) { - if(combined) key.add('&'); - if(prefix) { while(*prefix) key.add(*prefix++); } - defformatstring(tname, "packages/%s", t.name); - for(const char *s = path(tname); *s; key.add(*s++)); + if(combined) key.add('&'); + if(prefix) { while(*prefix) key.add(*prefix++); } + defformatstring(tname, "packages/%s", t.name); + for(const char *s = path(tname); *s; key.add(*s++)); } static void texcombine(Slot &s, int index, Slot::Tex &t, bool forceload = false) { - vector key; - addname(key, s, t); - int texmask = 0; - if(!forceload) switch(t.type) - { - case TEX_DIFFUSE: - case TEX_NORMAL: - { - int i = findtextype(s, t.type==TEX_DIFFUSE ? (s.texmask&(1< key; + addname(key, s, t); + int texmask = 0; + if(!forceload) switch(t.type) + { + case TEX_DIFFUSE: + case TEX_NORMAL: + { + int i = findtextype(s, t.type==TEX_DIFFUSE ? (s.texmask&(1<= 0) continue; - switch(t.type) - { - case TEX_ENVMAP: - t.t = cubemapload(t.name); - break; - - default: - texcombine(s, i, t, forceload); - break; - } - } - s.loaded = true; - return s; + linkslotshader(s); + loopv(s.sts) + { + Slot::Tex &t = s.sts[i]; + if(t.combined >= 0) continue; + switch(t.type) + { + case TEX_ENVMAP: + t.t = cubemapload(t.name); + break; + + default: + texcombine(s, i, t, forceload); + break; + } + } + s.loaded = true; + return s; } MSlot &lookupmaterialslot(int index, bool load) { - if(materialslots[index].sts.empty() && index&MATF_INDEX) index &= ~MATF_INDEX; - MSlot &s = materialslots[index]; - if(load && !s.linked) - { - if(!s.loaded) loadslot(s, true); - linkvslotshader(s); - s.linked = true; - } - return s; + if(materialslots[index].sts.empty() && index&MATF_INDEX) index &= ~MATF_INDEX; + MSlot &s = materialslots[index]; + if(load && !s.linked) + { + if(!s.loaded) loadslot(s, true); + linkvslotshader(s); + s.linked = true; + } + return s; } Slot &lookupslot(int index, bool load) { - Slot &s = slots.inrange(index) ? *slots[index] : (slots.inrange(DEFAULT_GEOM) ? *slots[DEFAULT_GEOM] : dummyslot); - return s.loaded || !load ? s : loadslot(s, false); + Slot &s = slots.inrange(index) ? *slots[index] : (slots.inrange(DEFAULT_GEOM) ? *slots[DEFAULT_GEOM] : dummyslot); + return s.loaded || !load ? s : loadslot(s, false); } VSlot &lookupvslot(int index, bool load) { - VSlot &s = vslots.inrange(index) && vslots[index]->slot ? *vslots[index] : (slots.inrange(DEFAULT_GEOM) && slots[DEFAULT_GEOM]->variants ? *slots[DEFAULT_GEOM]->variants : dummyvslot); - if(load && !s.linked) - { - if(!s.slot->loaded) loadslot(*s.slot, false); - linkvslotshader(s); - s.linked = true; - } - return s; + VSlot &s = vslots.inrange(index) && vslots[index]->slot ? *vslots[index] : (slots.inrange(DEFAULT_GEOM) && slots[DEFAULT_GEOM]->variants ? *slots[DEFAULT_GEOM]->variants : dummyvslot); + if(load && !s.linked) + { + if(!s.slot->loaded) loadslot(*s.slot, false); + linkvslotshader(s); + s.linked = true; + } + return s; } void linkslotshaders() { - loopv(slots) if(slots[i]->loaded) linkslotshader(*slots[i]); - loopv(vslots) if(vslots[i]->linked) linkvslotshader(*vslots[i]); - loopi((MATF_VOLUME|MATF_INDEX)+1) if(materialslots[i].loaded) - { - linkslotshader(materialslots[i]); - linkvslotshader(materialslots[i]); - } + loopv(slots) if(slots[i]->loaded) linkslotshader(*slots[i]); + loopv(vslots) if(vslots[i]->linked) linkvslotshader(*vslots[i]); + loopi((MATF_VOLUME|MATF_INDEX)+1) if(materialslots[i].loaded) + { + linkslotshader(materialslots[i]); + linkvslotshader(materialslots[i]); + } } Texture *loadthumbnail(Slot &slot) { - if(slot.thumbnail) return slot.thumbnail; - if(!slot.variants) - { - slot.thumbnail = notexture; - return slot.thumbnail; - } - VSlot &vslot = *slot.variants; - linkslotshader(slot, false); - linkvslotshader(vslot, false); - vector name; - if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, ""); - else - { - defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); - addname(name, slot, slot.sts[0], false, prefix); - } - int glow = -1; - if(slot.texmask&(1<= 0) - { - defformatstring(prefix, "", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z); - addname(name, slot, slot.sts[glow], true, prefix); - } - } - VSlot *layer = vslot.layer ? &lookupvslot(vslot.layer, false) : NULL; - if(layer) - { - if(layer->colorscale == vec(1, 1, 1)) addname(name, *layer->slot, layer->slot->sts[0], true, ""); - else - { - defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); - addname(name, *layer->slot, layer->slot->sts[0], true, prefix); - } - } - name.add('\0'); - Texture *t = textures.access(path(name.getbuf())); - if(t) slot.thumbnail = t; - else - { - ImageData s, g, l; - texturedata(s, NULL, &slot.sts[0], false); - if(glow >= 0) texturedata(g, NULL, &slot.sts[glow], false); - if(layer) texturedata(l, NULL, &layer->slot->sts[0], false); - if(!s.data) t = slot.thumbnail = notexture; - else - { - if(vslot.colorscale != vec(1, 1, 1)) texmad(s, vslot.colorscale, vec(0, 0, 0)); - int xs = s.w, ys = s.h; - if(s.w > 64 || s.h > 64) scaleimage(s, min(s.w, 64), min(s.h, 64)); - if(g.data) - { - if(g.w != s.w || g.h != s.h) scaleimage(g, s.w, s.h); - addglow(s, g, vslot.glowcolor); - } - if(l.data) - { - if(layer->colorscale != vec(1, 1, 1)) texmad(l, layer->colorscale, vec(0, 0, 0)); - if(l.w != s.w/2 || l.h != s.h/2) scaleimage(l, s.w/2, s.h/2); - forcergbimage(s); - forcergbimage(l); - uchar *dstrow = &s.data[s.pitch*l.h + s.bpp*l.w], *srcrow = l.data; - loop(y, l.h) - { - for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[l.w*l.bpp]; src < end; dst += s.bpp, src += l.bpp) - loopk(3) dst[k] = src[k]; - dstrow += s.pitch; - srcrow += l.pitch; - } - } - if(s.bpp < 3) forcergbimage(s); - t = newtexture(NULL, name.getbuf(), s, 0, false, false, true); - t->xs = xs; - t->ys = ys; - slot.thumbnail = t; - } - } - return t; + if(slot.thumbnail) return slot.thumbnail; + if(!slot.variants) + { + slot.thumbnail = notexture; + return slot.thumbnail; + } + VSlot &vslot = *slot.variants; + linkslotshader(slot, false); + linkvslotshader(vslot, false); + vector name; + if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, ""); + else + { + defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); + addname(name, slot, slot.sts[0], false, prefix); + } + int glow = -1; + if(slot.texmask&(1<= 0) + { + defformatstring(prefix, "", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z); + addname(name, slot, slot.sts[glow], true, prefix); + } + } + VSlot *layer = vslot.layer ? &lookupvslot(vslot.layer, false) : NULL; + if(layer) + { + if(layer->colorscale == vec(1, 1, 1)) addname(name, *layer->slot, layer->slot->sts[0], true, ""); + else + { + defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); + addname(name, *layer->slot, layer->slot->sts[0], true, prefix); + } + } + name.add('\0'); + Texture *t = textures.access(path(name.getbuf())); + if(t) slot.thumbnail = t; + else + { + ImageData s, g, l; + texturedata(s, NULL, &slot.sts[0], false); + if(glow >= 0) texturedata(g, NULL, &slot.sts[glow], false); + if(layer) texturedata(l, NULL, &layer->slot->sts[0], false); + if(!s.data) t = slot.thumbnail = notexture; + else + { + if(vslot.colorscale != vec(1, 1, 1)) texmad(s, vslot.colorscale, vec(0, 0, 0)); + int xs = s.w, ys = s.h; + if(s.w > 64 || s.h > 64) scaleimage(s, min(s.w, 64), min(s.h, 64)); + if(g.data) + { + if(g.w != s.w || g.h != s.h) scaleimage(g, s.w, s.h); + addglow(s, g, vslot.glowcolor); + } + if(l.data) + { + if(layer->colorscale != vec(1, 1, 1)) texmad(l, layer->colorscale, vec(0, 0, 0)); + if(l.w != s.w/2 || l.h != s.h/2) scaleimage(l, s.w/2, s.h/2); + forcergbimage(s); + forcergbimage(l); + uchar *dstrow = &s.data[s.pitch*l.h + s.bpp*l.w], *srcrow = l.data; + loop(y, l.h) + { + for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[l.w*l.bpp]; src < end; dst += s.bpp, src += l.bpp) + loopk(3) dst[k] = src[k]; + dstrow += s.pitch; + srcrow += l.pitch; + } + } + if(s.bpp < 3) forcergbimage(s); + t = newtexture(NULL, name.getbuf(), s, 0, false, false, true); + t->xs = xs; + t->ys = ys; + slot.thumbnail = t; + } + } + return t; } void loadlayermasks() { - loopv(slots) - { - Slot &slot = *slots[i]; - if(slot.loaded && slot.layermaskname && !slot.layermask) - { - slot.layermask = new ImageData; - texturedata(*slot.layermask, slot.layermaskname); - if(!slot.layermask->data) DELETEP(slot.layermask); - } - } + loopv(slots) + { + Slot &slot = *slots[i]; + if(slot.loaded && slot.layermaskname && !slot.layermask) + { + slot.layermask = new ImageData; + texturedata(*slot.layermask, slot.layermaskname); + if(!slot.layermask->data) DELETEP(slot.layermask); + } + } } // environment mapped reflections void forcecubemapload(GLuint tex) { - extern int ati_cubemap_bug; - if(!ati_cubemap_bug || !tex) return; - - SETSHADER(cubemap); - GLenum depthtest = glIsEnabled(GL_DEPTH_TEST), blend = glIsEnabled(GL_BLEND); - if(depthtest) glDisable(GL_DEPTH_TEST); - glBindTexture(GL_TEXTURE_CUBE_MAP, tex); - if(!blend) glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gle::defvertex(2); - gle::deftexcoord0(3); - gle::defcolor(4); - gle::begin(GL_LINES); - loopi(2) - { - gle::attribf(i*1e-3f, 0); - gle::attribf(0, 0, 1); - gle::attribf(1, 1, 1, 0); - } - gle::end(); - if(!blend) glDisable(GL_BLEND); - if(depthtest) glEnable(GL_DEPTH_TEST); + extern int ati_cubemap_bug; + if(!ati_cubemap_bug || !tex) return; + + SETSHADER(cubemap); + GLenum depthtest = glIsEnabled(GL_DEPTH_TEST), blend = glIsEnabled(GL_BLEND); + if(depthtest) glDisable(GL_DEPTH_TEST); + glBindTexture(GL_TEXTURE_CUBE_MAP, tex); + if(!blend) glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gle::defvertex(2); + gle::deftexcoord0(3); + gle::defcolor(4); + gle::begin(GL_LINES); + loopi(2) + { + gle::attribf(i*1e-3f, 0); + gle::attribf(0, 0, 1); + gle::attribf(1, 1, 1, 0); + } + gle::end(); + if(!blend) glDisable(GL_BLEND); + if(depthtest) glEnable(GL_DEPTH_TEST); } extern const cubemapside cubemapsides[6] = { - { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", true, true, true }, - { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", false, false, true }, - { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "ft", true, false, false }, - { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "bk", false, true, false }, - { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", false, false, true }, - { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", false, false, true }, + { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", true, true, true }, + { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", false, false, true }, + { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "ft", true, false, false }, + { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "bk", false, true, false }, + { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", false, false, true }, + { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", false, false, true }, }; VARFP(envmapsize, 4, 7, 10, setupmaterials()); Texture *cubemaploadwildcard(Texture *t, const char *name, bool mipit, bool msg, bool transient = false) { - string tname; - if(!name) copystring(tname, t->name); - else - { - copystring(tname, name); - t = textures.access(path(tname)); - if(t) - { - if(!transient && t->type&Texture::TRANSIENT) t->type &= ~Texture::TRANSIENT; - return t; - } - } - char *wildcard = strchr(tname, '*'); - ImageData surface[6]; - string sname; - if(!wildcard) copystring(sname, tname); - int tsize = 0, compress = 0; - loopi(6) - { - if(wildcard) - { - copystring(sname, stringslice(tname, wildcard)); - concatstring(sname, cubemapsides[i].name); - concatstring(sname, wildcard+1); - } - ImageData &s = surface[i]; - texturedata(s, sname, NULL, msg, &compress); - if(!s.data) return NULL; - if(s.w != s.h) - { - if(msg) conoutf(CON_ERROR, "cubemap texture %s does not have square size", sname); - return NULL; - } - if(s.compressed ? s.compressed!=surface[0].compressed || s.w!=surface[0].w || s.h!=surface[0].h || s.levels!=surface[0].levels : surface[0].compressed || s.bpp!=surface[0].bpp) - { - if(msg) conoutf(CON_ERROR, "cubemap texture %s doesn't match other sides' format", sname); - return NULL; - } - tsize = max(tsize, max(s.w, s.h)); - } - if(name) - { - char *key = newstring(tname); - t = &textures[key]; - t->name = key; - } - t->type = Texture::CUBEMAP; - if(transient) t->type |= Texture::TRANSIENT; - GLenum format; - if(surface[0].compressed) - { - format = uncompressedformat(surface[0].compressed); - t->bpp = formatsize(format); - t->type |= Texture::COMPRESSED; - } - else - { - format = texformat(surface[0].bpp, true); - t->bpp = surface[0].bpp; - if(hasTRG && !hasTSW && swizzlemask(format)) - { - loopi(6) swizzleimage(surface[i]); - format = texformat(surface[0].bpp, true); - t->bpp = surface[0].bpp; - } - } - if(alphaformat(format)) t->type |= Texture::ALPHA; - t->mipmap = mipit; - t->clamp = 3; - t->xs = t->ys = tsize; - t->w = t->h = min(1<w, t->h, mipit, false, GL_TEXTURE_CUBE_MAP, compress, t->w, t->h); - GLenum component = format; - if(!surface[0].compressed) - { - component = compressedformat(format, t->w, t->h, compress); - switch(component) - { - case GL_RGB: component = GL_RGB5; break; - } - } - glGenTextures(1, &t->id); - loopi(6) - { - ImageData &s = surface[i]; - const cubemapside &side = cubemapsides[i]; - texreorient(s, side.flipx, side.flipy, side.swapxy); - if(s.compressed) - { - int w = s.w, h = s.h, levels = s.levels, level = 0; - uchar *data = s.data; - while(levels > 1 && (w > t->w || h > t->h)) - { - data += s.calclevelsize(level++); - levels--; - if(w > 1) w /= 2; - if(h > 1) h /= 2; - } - createcompressedtexture(t->id, w, h, data, s.align, s.bpp, levels, i ? -1 : 3, mipit ? 2 : 1, s.compressed, side.target, true); - } - else - { - createtexture(t->id, t->w, t->h, s.data, i ? -1 : 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true); - } - } - forcecubemapload(t->id); - return t; + string tname; + if(!name) copystring(tname, t->name); + else + { + copystring(tname, name); + t = textures.access(path(tname)); + if(t) + { + if(!transient && t->type&Texture::TRANSIENT) t->type &= ~Texture::TRANSIENT; + return t; + } + } + char *wildcard = strchr(tname, '*'); + ImageData surface[6]; + string sname; + if(!wildcard) copystring(sname, tname); + int tsize = 0, compress = 0; + loopi(6) + { + if(wildcard) + { + copystring(sname, stringslice(tname, wildcard)); + concatstring(sname, cubemapsides[i].name); + concatstring(sname, wildcard+1); + } + ImageData &s = surface[i]; + texturedata(s, sname, NULL, msg, &compress); + if(!s.data) return NULL; + if(s.w != s.h) + { + if(msg) conoutf(CON_ERROR, "cubemap texture %s does not have square size", sname); + return NULL; + } + if(s.compressed ? s.compressed!=surface[0].compressed || s.w!=surface[0].w || s.h!=surface[0].h || s.levels!=surface[0].levels : surface[0].compressed || s.bpp!=surface[0].bpp) + { + if(msg) conoutf(CON_ERROR, "cubemap texture %s doesn't match other sides' format", sname); + return NULL; + } + tsize = max(tsize, max(s.w, s.h)); + } + if(name) + { + char *key = newstring(tname); + t = &textures[key]; + t->name = key; + } + t->type = Texture::CUBEMAP; + if(transient) t->type |= Texture::TRANSIENT; + GLenum format; + if(surface[0].compressed) + { + format = uncompressedformat(surface[0].compressed); + t->bpp = formatsize(format); + t->type |= Texture::COMPRESSED; + } + else + { + format = texformat(surface[0].bpp, true); + t->bpp = surface[0].bpp; + if(hasTRG && !hasTSW && swizzlemask(format)) + { + loopi(6) swizzleimage(surface[i]); + format = texformat(surface[0].bpp, true); + t->bpp = surface[0].bpp; + } + } + if(alphaformat(format)) t->type |= Texture::ALPHA; + t->mipmap = mipit; + t->clamp = 3; + t->xs = t->ys = tsize; + t->w = t->h = min(1<w, t->h, mipit, false, GL_TEXTURE_CUBE_MAP, compress, t->w, t->h); + GLenum component = format; + if(!surface[0].compressed) + { + component = compressedformat(format, t->w, t->h, compress); + switch(component) + { + case GL_RGB: component = GL_RGB5; break; + } + } + glGenTextures(1, &t->id); + loopi(6) + { + ImageData &s = surface[i]; + const cubemapside &side = cubemapsides[i]; + texreorient(s, side.flipx, side.flipy, side.swapxy); + if(s.compressed) + { + int w = s.w, h = s.h, levels = s.levels, level = 0; + uchar *data = s.data; + while(levels > 1 && (w > t->w || h > t->h)) + { + data += s.calclevelsize(level++); + levels--; + if(w > 1) w /= 2; + if(h > 1) h /= 2; + } + createcompressedtexture(t->id, w, h, data, s.align, s.bpp, levels, i ? -1 : 3, mipit ? 2 : 1, s.compressed, side.target, true); + } + else + { + createtexture(t->id, t->w, t->h, s.data, i ? -1 : 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true); + } + } + forcecubemapload(t->id); + return t; } Texture *cubemapload(const char *name, bool mipit, bool msg, bool transient) { - string pname; - copystring(pname, makerelpath("packages", name)); - path(pname); - Texture *t = NULL; - if(!strchr(pname, '*')) - { - defformatstring(jpgname, "%s_*.jpg", pname); - t = cubemaploadwildcard(NULL, jpgname, mipit, false, transient); - if(!t) - { - defformatstring(pngname, "%s_*.png", pname); - t = cubemaploadwildcard(NULL, pngname, mipit, false, transient); - if(!t && msg) conoutf(CON_ERROR, "could not load envmap %s", name); - } - } - else t = cubemaploadwildcard(NULL, pname, mipit, msg, transient); - return t; + string pname; + copystring(pname, makerelpath("packages", name)); + path(pname); + Texture *t = NULL; + if(!strchr(pname, '*')) + { + defformatstring(jpgname, "%s_*.jpg", pname); + t = cubemaploadwildcard(NULL, jpgname, mipit, false, transient); + if(!t) + { + defformatstring(pngname, "%s_*.png", pname); + t = cubemaploadwildcard(NULL, pngname, mipit, false, transient); + if(!t && msg) conoutf(CON_ERROR, "could not load envmap %s", name); + } + } + else t = cubemaploadwildcard(NULL, pname, mipit, msg, transient); + return t; } VARR(envmapradius, 0, 128, 10000); @@ -2647,16 +2574,16 @@ VARR(envmapbb, 0, 0, 1); struct envmap { - int radius, size, blur; - vec o; - GLuint tex; + int radius, size, blur; + vec o; + GLuint tex; - envmap() : radius(-1), size(0), blur(0), o(0, 0, 0), tex(0) {} + envmap() : radius(-1), size(0), blur(0), o(0, 0, 0), tex(0) {} - void clear() - { - if(tex) { glDeleteTextures(1, &tex); tex = 0; } - } + void clear() + { + if(tex) { glDeleteTextures(1, &tex); tex = 0; } + } }; static vector envmaps; @@ -2664,581 +2591,455 @@ static Texture *skyenvmap = NULL; void clearenvmaps() { - if(skyenvmap) - { - if(skyenvmap->type&Texture::TRANSIENT) cleanuptexture(skyenvmap); - skyenvmap = NULL; - } - loopv(envmaps) envmaps[i].clear(); - envmaps.shrink(0); + if(skyenvmap) + { + if(skyenvmap->type&Texture::TRANSIENT) cleanuptexture(skyenvmap); + skyenvmap = NULL; + } + loopv(envmaps) envmaps[i].clear(); + envmaps.shrink(0); } VAR(aaenvmap, 0, 2, 4); GLuint genenvmap(const vec &o, int envmapsize, int blur, bool onlysky) { - int rendersize = 1<<(envmapsize+aaenvmap), sizelimit = min(hwcubetexsize, min(screenw, screenh)); - if(maxtexsize) sizelimit = min(sizelimit, maxtexsize); - while(rendersize > sizelimit) rendersize /= 2; - int texsize = min(rendersize, 1< texsize) - { - scaletexture(src, rendersize, rendersize, 3, 3*rendersize, dst, texsize, texsize); - swap(src, dst); - } - if(blur > 0) - { - blurtexture(blur, 3, texsize, texsize, dst, src); - swap(src, dst); - } - createtexture(tex, texsize, texsize, src, 3, 2, GL_RGB5, side.target); - } - glFrontFace(GL_CW); - delete[] pixels; - glViewport(0, 0, screenw, screenh); - clientkeepalive(); - forcecubemapload(tex); - return tex; + int rendersize = 1<<(envmapsize+aaenvmap), sizelimit = min(hwcubetexsize, min(screenw, screenh)); + if(maxtexsize) sizelimit = min(sizelimit, maxtexsize); + while(rendersize > sizelimit) rendersize /= 2; + int texsize = min(rendersize, 1< texsize) + { + scaletexture(src, rendersize, rendersize, 3, 3*rendersize, dst, texsize, texsize); + swap(src, dst); + } + if(blur > 0) + { + blurtexture(blur, 3, texsize, texsize, dst, src); + swap(src, dst); + } + createtexture(tex, texsize, texsize, src, 3, 2, GL_RGB5, side.target); + } + glFrontFace(GL_CW); + delete[] pixels; + glViewport(0, 0, screenw, screenh); + clientkeepalive(); + forcecubemapload(tex); + return tex; } void initenvmaps() { - clearenvmaps(); - skyenvmap = NULL; - if(shouldrenderskyenvmap()) envmaps.add().size = 1; - else if(skybox[0]) skyenvmap = cubemapload(skybox, true, false, true); - const vector &ents = entities::getents(); - loopv(ents) - { - const extentity &ent = *ents[i]; - if(ent.type != ET_ENVMAP) continue; - envmap &em = envmaps.add(); - em.radius = ent.attr1 ? clamp(int(ent.attr1), 0, 10000) : envmapradius; - em.size = ent.attr2 ? clamp(int(ent.attr2), 4, 9) : 0; - em.blur = ent.attr3 ? clamp(int(ent.attr3), 1, 2) : 0; - em.o = ent.o; - } + clearenvmaps(); + skyenvmap = NULL; + if(shouldrenderskyenvmap()) envmaps.add().size = 1; + else if(skybox[0]) skyenvmap = cubemapload(skybox, true, false, true); + const vector &ents = entities::getents(); + loopv(ents) + { + const extentity &ent = *ents[i]; + if(ent.type != ET_ENVMAP) continue; + envmap &em = envmaps.add(); + em.radius = ent.attr1 ? clamp(int(ent.attr1), 0, 10000) : envmapradius; + em.size = ent.attr2 ? clamp(int(ent.attr2), 4, 9) : 0; + em.blur = ent.attr3 ? clamp(int(ent.attr3), 1, 2) : 0; + em.o = ent.o; + } } void genenvmaps() { - if(envmaps.empty()) return; - renderprogress(0, "generating environment maps..."); - int lastprogress = SDL_GetTicks(); - loopv(envmaps) - { - envmap &em = envmaps[i]; - em.tex = genenvmap(em.o, em.size ? min(em.size, envmapsize) : envmapsize, em.blur, em.radius < 0); - if(renderedframe) continue; - int millis = SDL_GetTicks(); - if(millis - lastprogress >= 250) - { - renderprogress(float(i+1)/envmaps.length(), "generating environment maps...", 0, true); - lastprogress = millis; - } - } + if(envmaps.empty()) return; + renderprogress(0, "generating environment maps..."); + int lastprogress = SDL_GetTicks(); + loopv(envmaps) + { + envmap &em = envmaps[i]; + em.tex = genenvmap(em.o, em.size ? min(em.size, envmapsize) : envmapsize, em.blur, em.radius < 0); + if(renderedframe) continue; + int millis = SDL_GetTicks(); + if(millis - lastprogress >= 250) + { + renderprogress(float(i+1)/envmaps.length(), "generating environment maps...", 0, true); + lastprogress = millis; + } + } } ushort closestenvmap(const vec &o) { - ushort minemid = EMID_SKY; - float mindist = 1e16f; - loopv(envmaps) - { - envmap &em = envmaps[i]; - float dist; - if(envmapbb) - { - if(!o.insidebb(vec(em.o).sub(em.radius), vec(em.o).add(em.radius))) continue; - dist = em.o.dist(o); - } - else - { - dist = em.o.dist(o); - if(dist > em.radius) continue; - } - if(dist < mindist) - { - minemid = EMID_RESERVED + i; - mindist = dist; - } - } - return minemid; + ushort minemid = EMID_SKY; + float mindist = 1e16f; + loopv(envmaps) + { + envmap &em = envmaps[i]; + float dist; + if(envmapbb) + { + if(!o.insidebb(vec(em.o).sub(em.radius), vec(em.o).add(em.radius))) continue; + dist = em.o.dist(o); + } + else + { + dist = em.o.dist(o); + if(dist > em.radius) continue; + } + if(dist < mindist) + { + minemid = EMID_RESERVED + i; + mindist = dist; + } + } + return minemid; } ushort closestenvmap(int orient, const ivec &co, int size) { - vec loc(co); - int dim = dimension(orient); - if(dimcoord(orient)) loc[dim] += size; - loc[R[dim]] += size/2; - loc[C[dim]] += size/2; - return closestenvmap(loc); + vec loc(co); + int dim = dimension(orient); + if(dimcoord(orient)) loc[dim] += size; + loc[R[dim]] += size/2; + loc[C[dim]] += size/2; + return closestenvmap(loc); } static inline GLuint lookupskyenvmap() { - return envmaps.length() && envmaps[0].radius < 0 ? envmaps[0].tex : (skyenvmap ? skyenvmap->id : 0); + return envmaps.length() && envmaps[0].radius < 0 ? envmaps[0].tex : (skyenvmap ? skyenvmap->id : 0); } GLuint lookupenvmap(Slot &slot) { - loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP && slot.sts[i].t) return slot.sts[i].t->id; - return lookupskyenvmap(); + loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP && slot.sts[i].t) return slot.sts[i].t->id; + return lookupskyenvmap(); } GLuint lookupenvmap(ushort emid) { - if(emid==EMID_SKY || emid==EMID_CUSTOM) return skyenvmap ? skyenvmap->id : 0; - if(emid==EMID_NONE || !envmaps.inrange(emid-EMID_RESERVED)) return 0; - GLuint tex = envmaps[emid-EMID_RESERVED].tex; - return tex ? tex : lookupskyenvmap(); + if(emid==EMID_SKY || emid==EMID_CUSTOM) return skyenvmap ? skyenvmap->id : 0; + if(emid==EMID_NONE || !envmaps.inrange(emid-EMID_RESERVED)) return 0; + GLuint tex = envmaps[emid-EMID_RESERVED].tex; + return tex ? tex : lookupskyenvmap(); } void cleanuptexture(Texture *t) { - DELETEA(t->alphamask); - if(t->id) { glDeleteTextures(1, &t->id); t->id = 0; } - if(t->type&Texture::TRANSIENT) textures.remove(t->name); + DELETEA(t->alphamask); + if(t->id) { glDeleteTextures(1, &t->id); t->id = 0; } + if(t->type&Texture::TRANSIENT) textures.remove(t->name); } void cleanuptextures() { - cleanupmipmaps(); - clearenvmaps(); - loopv(slots) slots[i]->cleanup(); - loopv(vslots) vslots[i]->cleanup(); - loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].cleanup(); - enumerate(textures, Texture, tex, cleanuptexture(&tex)); + clearenvmaps(); + loopv(slots) slots[i]->cleanup(); + loopv(vslots) vslots[i]->cleanup(); + loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].cleanup(); + enumerate(textures, Texture, tex, cleanuptexture(&tex)); } bool reloadtexture(const char *name) { - Texture *t = textures.access(path(name, true)); - if(t) return reloadtexture(*t); - return true; + Texture *t = textures.access(path(name, true)); + if(t) return reloadtexture(*t); + return true; } bool reloadtexture(Texture &tex) { - if(tex.id) return true; - switch(tex.type&Texture::TYPE) - { - case Texture::IMAGE: - { - int compress = 0; - ImageData s; - if(!texturedata(s, tex.name, NULL, true, &compress) || !newtexture(&tex, NULL, s, tex.clamp, tex.mipmap, false, false, compress)) return false; - break; - } + if(tex.id) return true; + switch(tex.type&Texture::TYPE) + { + case Texture::IMAGE: + { + int compress = 0; + ImageData s; + if(!texturedata(s, tex.name, NULL, true, &compress) || !newtexture(&tex, NULL, s, tex.clamp, tex.mipmap, false, false, compress)) return false; + break; + } - case Texture::CUBEMAP: - if(!cubemaploadwildcard(&tex, NULL, tex.mipmap, true)) return false; - break; - } - return true; + case Texture::CUBEMAP: + if(!cubemaploadwildcard(&tex, NULL, tex.mipmap, true)) return false; + break; + } + return true; } void reloadtex(char *name) { - Texture *t = textures.access(path(name, true)); - if(!t) { conoutf(CON_ERROR, "texture %s is not loaded", name); return; } - if(t->type&Texture::TRANSIENT) { conoutf(CON_ERROR, "can't reload transient texture %s", name); return; } - DELETEA(t->alphamask); - Texture oldtex = *t; - t->id = 0; - if(!reloadtexture(*t)) - { - if(t->id) glDeleteTextures(1, &t->id); - *t = oldtex; - conoutf(CON_ERROR, "failed to reload texture %s", name); - } + Texture *t = textures.access(path(name, true)); + if(!t) { conoutf(CON_ERROR, "texture %s is not loaded", name); return; } + if(t->type&Texture::TRANSIENT) { conoutf(CON_ERROR, "can't reload transient texture %s", name); return; } + DELETEA(t->alphamask); + Texture oldtex = *t; + t->id = 0; + if(!reloadtexture(*t)) + { + if(t->id) glDeleteTextures(1, &t->id); + *t = oldtex; + conoutf(CON_ERROR, "failed to reload texture %s", name); + } } COMMAND(reloadtex, "s"); void reloadtextures() { - int reloaded = 0; - enumerate(textures, Texture, tex, - { - loadprogress = float(++reloaded)/textures.numelems; - reloadtexture(tex); - }); - loadprogress = 0; + int reloaded = 0; + enumerate(textures, Texture, tex, + { + loadprogress = float(++reloaded)/textures.numelems; + reloadtexture(tex); + }); + loadprogress = 0; } void writepngchunk(stream *f, const char *type, uchar *data = NULL, uint len = 0) { - f->putbig(len); - f->write(type, 4); - f->write(data, len); + f->putbig(len); + f->write(type, 4); + f->write(data, len); - uint crc = crc32(0, Z_NULL, 0); - crc = crc32(crc, (const Bytef *)type, 4); - if(data) crc = crc32(crc, data, len); - f->putbig(crc); + uint crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, (const Bytef *)type, 4); + if(data) crc = crc32(crc, data, len); + f->putbig(crc); } VARP(compresspng, 0, 9, 9); void savepng(const char *filename, ImageData &image, bool flip) { - uchar ctype = 0; - switch(image.bpp) - { - case 1: ctype = 0; break; - case 2: ctype = 4; break; - case 3: ctype = 2; break; - case 4: ctype = 6; break; - default: conoutf(CON_ERROR, "failed saving png to %s", filename); return; - } - stream *f = openfile(filename, "wb"); - if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; } - - uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - f->write(signature, sizeof(signature)); - - struct pngihdr - { - uint width, height; - uchar bitdepth, colortype, compress, filter, interlace; - } ihdr = { bigswap(image.w), bigswap(image.h), 8, ctype, 0, 0, 0 }; - writepngchunk(f, "IHDR", (uchar *)&ihdr, 13); - - stream::offset idat = f->tell(); - uint len = 0; - f->write("\0\0\0\0IDAT", 8); - uint crc = crc32(0, Z_NULL, 0); - crc = crc32(crc, (const Bytef *)"IDAT", 4); - - z_stream z; - z.zalloc = NULL; - z.zfree = NULL; - z.opaque = NULL; - - if(deflateInit(&z, compresspng) != Z_OK) - goto error; - - uchar buf[1<<12]; - z.next_out = (Bytef *)buf; - z.avail_out = sizeof(buf); - - loopi(image.h) - { - uchar filter = 0; - loopj(2) - { - z.next_in = j ? (Bytef *)image.data + (flip ? image.h-i-1 : i)*image.pitch : (Bytef *)&filter; - z.avail_in = j ? image.w*image.bpp : 1; - while(z.avail_in > 0) - { - if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror; - #define FLUSHZ do { \ - int flush = sizeof(buf) - z.avail_out; \ - crc = crc32(crc, buf, flush); \ - len += flush; \ - f->write(buf, flush); \ - z.next_out = (Bytef *)buf; \ - z.avail_out = sizeof(buf); \ - } while(0) - FLUSHZ; - } - } - } - - for(;;) - { - int err = deflate(&z, Z_FINISH); - if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror; - FLUSHZ; - if(err == Z_STREAM_END) break; - } - - deflateEnd(&z); - - f->seek(idat, SEEK_SET); - f->putbig(len); - f->seek(0, SEEK_END); - f->putbig(crc); - - writepngchunk(f, "IEND"); - - delete f; - return; + uchar ctype = 0; + switch(image.bpp) + { + case 1: ctype = 0; break; + case 2: ctype = 4; break; + case 3: ctype = 2; break; + case 4: ctype = 6; break; + default: conoutf(CON_ERROR, "failed saving png to %s", filename); return; + } + stream *f = openfile(filename, "wb"); + if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; } + + uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + f->write(signature, sizeof(signature)); + + struct pngihdr + { + uint width, height; + uchar bitdepth, colortype, compress, filter, interlace; + } ihdr = { bigswap(image.w), bigswap(image.h), 8, ctype, 0, 0, 0 }; + writepngchunk(f, "IHDR", (uchar *)&ihdr, 13); + + stream::offset idat = f->tell(); + uint len = 0; + f->write("\0\0\0\0IDAT", 8); + uint crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, (const Bytef *)"IDAT", 4); + + z_stream z; + z.zalloc = NULL; + z.zfree = NULL; + z.opaque = NULL; + + if(deflateInit(&z, compresspng) != Z_OK) + goto error; + + uchar buf[1<<12]; + z.next_out = (Bytef *)buf; + z.avail_out = sizeof(buf); + + loopi(image.h) + { + uchar filter = 0; + loopj(2) + { + z.next_in = j ? (Bytef *)image.data + (flip ? image.h-i-1 : i)*image.pitch : (Bytef *)&filter; + z.avail_in = j ? image.w*image.bpp : 1; + while(z.avail_in > 0) + { + if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror; + #define FLUSHZ do { \ + int flush = sizeof(buf) - z.avail_out; \ + crc = crc32(crc, buf, flush); \ + len += flush; \ + f->write(buf, flush); \ + z.next_out = (Bytef *)buf; \ + z.avail_out = sizeof(buf); \ + } while(0) + FLUSHZ; + } + } + } + + for(;;) + { + int err = deflate(&z, Z_FINISH); + if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror; + FLUSHZ; + if(err == Z_STREAM_END) break; + } + + deflateEnd(&z); + + f->seek(idat, SEEK_SET); + f->putbig(len); + f->seek(0, SEEK_END); + f->putbig(crc); + + writepngchunk(f, "IEND"); + + delete f; + return; cleanuperror: - deflateEnd(&z); + deflateEnd(&z); error: - delete f; + delete f; - conoutf(CON_ERROR, "failed saving png to %s", filename); + conoutf(CON_ERROR, "failed saving png to %s", filename); } -struct tgaheader -{ - uchar identsize; - uchar cmaptype; - uchar imagetype; - uchar cmaporigin[2]; - uchar cmapsize[2]; - uchar cmapentrysize; - uchar xorigin[2]; - uchar yorigin[2]; - uchar width[2]; - uchar height[2]; - uchar pixelsize; - uchar descbyte; -}; - -VARP(compresstga, 0, 1, 1); - -void savetga(const char *filename, ImageData &image, bool flip) -{ - switch(image.bpp) - { - case 3: case 4: break; - default: conoutf(CON_ERROR, "failed saving tga to %s", filename); return; - } - - stream *f = openfile(filename, "wb"); - if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; } - - tgaheader hdr; - memset(&hdr, 0, sizeof(hdr)); - hdr.pixelsize = image.bpp*8; - hdr.width[0] = image.w&0xFF; - hdr.width[1] = (image.w>>8)&0xFF; - hdr.height[0] = image.h&0xFF; - hdr.height[1] = (image.h>>8)&0xFF; - hdr.imagetype = compresstga ? 10 : 2; - f->write(&hdr, sizeof(hdr)); - - uchar buf[128*4]; - loopi(image.h) - { - uchar *src = image.data + (flip ? i : image.h - i - 1)*image.pitch; - for(int remaining = image.w; remaining > 0;) - { - int raw = 1; - if(compresstga) - { - int run = 1; - for(uchar *scan = src; run < min(remaining, 128); run++) - { - scan += image.bpp; - if(src[0]!=scan[0] || src[1]!=scan[1] || src[2]!=scan[2] || (image.bpp==4 && src[3]!=scan[3])) break; - } - if(run > 1) - { - f->putchar(0x80 | (run-1)); - f->putchar(src[2]); f->putchar(src[1]); f->putchar(src[0]); - if(image.bpp==4) f->putchar(src[3]); - src += run*image.bpp; - remaining -= run; - if(remaining <= 0) break; - } - for(uchar *scan = src; raw < min(remaining, 128); raw++) - { - scan += image.bpp; - if(src[0]==scan[0] && src[1]==scan[1] && src[2]==scan[2] && (image.bpp!=4 || src[3]==scan[3])) break; - } - f->putchar(raw - 1); - } - else raw = min(remaining, 128); - uchar *dst = buf; - loopj(raw) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - if(image.bpp==4) dst[3] = src[3]; - dst += image.bpp; - src += image.bpp; - } - f->write(buf, raw*image.bpp); - remaining -= raw; - } - } - - delete f; -} - -enum -{ - IMG_BMP = 0, - IMG_TGA = 1, - IMG_PNG = 2, - IMG_JPG = 3, - NUMIMG -}; +enum { IMG_BMP, IMG_PNG, IMG_JPG, NUMIMG }; VARP(screenshotquality, 0, 97, 100); VARP(screenshotformat, 0, IMG_PNG, NUMIMG-1); -const char *imageexts[NUMIMG] = { ".bmp", ".tga", ".png", ".jpg" }; +const char *imageexts[NUMIMG] = { ".bmp", ".png", ".jpg" }; int guessimageformat(const char *filename, int format = IMG_BMP) { - int len = strlen(filename); - loopi(NUMIMG) - { - int extlen = strlen(imageexts[i]); - if(len >= extlen && !strcasecmp(&filename[len-extlen], imageexts[i])) return i; - } - return format; + int len = strlen(filename); + loopi(NUMIMG) + { + int extlen = strlen(imageexts[i]); + if(len >= extlen && !strcasecmp(&filename[len-extlen], imageexts[i])) return i; + } + return format; } void saveimage(const char *filename, int format, ImageData &image, bool flip = false) { - switch(format) - { - case IMG_PNG: savepng(filename, image, flip); break; - case IMG_TGA: savetga(filename, image, flip); break; - default: - { - ImageData flipped(image.w, image.h, image.bpp, image.data); - if(flip) texflip(flipped); - SDL_Surface *s = wrapsurface(flipped.data, flipped.w, flipped.h, flipped.bpp); - if(!s) break; - stream *f = openfile(filename, "wb"); - if(f) - { - switch(format) { - case IMG_JPG: + switch(format) + { + case IMG_PNG: savepng(filename, image, flip); break; + default: + { + ImageData flipped(image.w, image.h, image.bpp, image.data); + if(flip) texflip(flipped); + SDL_Surface *s = wrapsurface(flipped.data, flipped.w, flipped.h, flipped.bpp); + if(!s) break; + stream *f = openfile(filename, "wb"); + if(f) + { + switch(format) { + case IMG_JPG: #if SDL_IMAGE_VERSION_ATLEAST(2, 0, 2) - IMG_SaveJPG_RW(s, f->rwops(), 1, screenshotquality); + IMG_SaveJPG_RW(s, f->rwops(), 1, screenshotquality); #else - conoutf(CON_ERROR, "JPG screenshot support requires SDL_image 2.0.2"); + conoutf(CON_ERROR, "JPG screenshot support requires SDL_image 2.0.2"); #endif - break; - default: SDL_SaveBMP_RW(s, f->rwops(), 1); break; - } - delete f; - } - SDL_FreeSurface(s); - break; - } - } + break; + default: SDL_SaveBMP_RW(s, f->rwops(), 1); break; + } + delete f; + } + SDL_FreeSurface(s); + break; + } + } } bool loadimage(const char *filename, ImageData &image) { - SDL_Surface *s = loadsurface(path(filename, true)); - if(!s) return false; - image.wrap(s); - return true; + SDL_Surface *s = loadsurface(path(filename, true)); + if(!s) return false; + image.wrap(s); + return true; } SVARP(screenshotdir, "screenshot"); void screenshot(char *filename) { - static string buf; - int format = -1, dirlen = 0; - copystring(buf, screenshotdir); - if(screenshotdir[0]) - { - dirlen = strlen(buf); - if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < (int)sizeof(buf)) { buf[dirlen++] = '/'; buf[dirlen] = '\0'; } - const char *dir = findfile(buf, "w"); - if(!fileexists(dir, "w")) createdir(dir); - } - if(filename[0]) - { - concatstring(buf, filename); - format = guessimageformat(buf, -1); - } - else - { - string sstime; - time_t t = time(NULL); - size_t len = strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S", localtime(&t)); - sstime[min(len, sizeof(sstime)-1)] = '\0'; - concatstring(buf, sstime); - - const char *map = game::getclientmap(), *ssinfo = game::getscreenshotinfo(); - if(map && map[0]) - { - concatstring(buf, "_"); - concatstring(buf, map); - } - if(ssinfo && ssinfo[0]) - { - concatstring(buf, "_"); - concatstring(buf, ssinfo); - } - - for(char *s = &buf[dirlen]; *s; s++) if(iscubespace(*s) || *s == '/' || *s == '\\') *s = '-'; - } - if(format < 0) - { - format = screenshotformat; - concatstring(buf, imageexts[format]); - } - - ImageData image(screenw, screenh, 3); - glPixelStorei(GL_PACK_ALIGNMENT, texalign(image.data, screenw, 3)); - glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data); - saveimage(path(buf), format, image, true); + static string buf; + int format = -1, dirlen = 0; + copystring(buf, screenshotdir); + if(screenshotdir[0]) + { + dirlen = strlen(buf); + if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < (int)sizeof(buf)) { buf[dirlen++] = '/'; buf[dirlen] = '\0'; } + const char *dir = findfile(buf, "w"); + if(!fileexists(dir, "w")) createdir(dir); + } + if(filename[0]) + { + concatstring(buf, filename); + format = guessimageformat(buf, -1); + } + else + { + string sstime; + time_t t = time(NULL); + size_t len = strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S", localtime(&t)); + sstime[min(len, sizeof(sstime)-1)] = '\0'; + concatstring(buf, sstime); + + const char *map = game::getclientmap(), *ssinfo = game::getscreenshotinfo(); + if(map && map[0]) + { + concatstring(buf, "_"); + concatstring(buf, map); + } + if(ssinfo && ssinfo[0]) + { + concatstring(buf, "_"); + concatstring(buf, ssinfo); + } + + for(char *s = &buf[dirlen]; *s; s++) if(iscubespace(*s) || *s == '/' || *s == '\\') *s = '-'; + } + if(format < 0) + { + format = screenshotformat; + concatstring(buf, imageexts[format]); + } + + ImageData image(screenw, screenh, 3); + glPixelStorei(GL_PACK_ALIGNMENT, texalign(image.data, screenw, 3)); + glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data); + saveimage(path(buf), format, image, true); } COMMAND(screenshot, "s"); - -void flipnormalmapy(char *destfile, char *normalfile) // jpg/png /tga-> tga -{ - ImageData ns; - if(!loadimage(normalfile, ns)) return; - ImageData d(ns.w, ns.h, 3); - readwritetex(d, ns, - dst[0] = src[0]; - dst[1] = 255 - src[1]; - dst[2] = src[2]; - ); - saveimage(destfile, guessimageformat(destfile, IMG_TGA), d); -} - -void mergenormalmaps(char *heightfile, char *normalfile) // jpg/png/tga + tga -> tga -{ - ImageData hs, ns; - if(!loadimage(heightfile, hs) || !loadimage(normalfile, ns) || hs.w != ns.w || hs.h != ns.h) return; - ImageData d(ns.w, ns.h, 3); - read2writetex(d, hs, srch, ns, srcn, - *(bvec *)dst = bvec(((bvec *)srcn)->tonormal().mul(2).add(((bvec *)srch)->tonormal()).normalize()); - ); - saveimage(normalfile, guessimageformat(normalfile, IMG_TGA), d); -} - -COMMAND(flipnormalmapy, "ss"); -COMMAND(mergenormalmaps, "ss"); - -- cgit v1.2.3