diff --git a/render/Object.cc b/render/Object.cc index b716cae..10fcb64 100644 --- a/render/Object.cc +++ b/render/Object.cc @@ -45,6 +45,144 @@ namespace render { float xofs, yofs, xscale, yscale; ObjDecoder::Texture const& tex; }; + + void copyPaletteTexture(uint8_t const* src, unsigned srcWidth, unsigned srcHeight, + uint8_t *dst, unsigned dstStride, + PaletteDecoder::Palette const& palt) + { + const uint8_t TRANS = 0xff; + auto getPixel = [src, srcWidth, srcHeight](int x, int y) -> uint8_t { + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (static_cast(x) > srcWidth-1) + x = srcWidth-1; + if (static_cast(y) > srcHeight-1) + y = srcHeight-1; + + return src[y*srcWidth+x]; + }; + + for (unsigned y = 0;y < srcHeight;++y) { + for (unsigned x = 0;x < srcWidth;++x) { + auto col = getPixel(x, y); + if (col == TRANS) { + auto ocol = TRANS; + // // Copy color from neighbouring opaque pixel if available + if (getPixel(x-1, y) != TRANS) + ocol = getPixel(x-1, y); + else if (getPixel(x+1, y) != TRANS) + ocol = getPixel(x+1, y); + else if (getPixel(x, y-1) != TRANS) + ocol = getPixel(x, y-1); + else if (getPixel(x, y+1) != TRANS) + ocol = getPixel(x, y+1); + else if (getPixel(x-1, y-1) != TRANS) + ocol = getPixel(x-1, y-1); + else if (getPixel(x-1, y+1) != TRANS) + ocol = getPixel(x-1, y+1); + else if (getPixel(x+1, y-1) != TRANS) + ocol = getPixel(x+1, y-1); + else if (getPixel(x+1, y+1) != TRANS) + ocol = getPixel(x+1, y+1); + + if (ocol != TRANS) { + *dst++ = palt[ocol*3u+2u]; // b + *dst++ = palt[ocol*3u+1u]; // g + *dst++ = palt[ocol*3u]; // r + } else { + *dst++ = 0u; // b + *dst++ = 0u; // g + *dst++ = 0u; // r + } + *dst++ = 0u; // a + } else { + *dst++ = palt[col*3u+2u]; // b + *dst++ = palt[col*3u+1u]; // g + *dst++ = palt[col*3u]; // r + *dst++ = 255u; // a + } + } + dst += (dstStride - srcWidth*4); + } + } + + std::vector genMipmap(uint8_t const* src, unsigned srcWidth, unsigned srcHeight, + unsigned levels, bool colorize = false) + { + if (!levels) + return std::vector(); + + unsigned origWidth = srcWidth, origHeight = srcHeight; + unsigned size = 0; + for(unsigned i = 1;i <= levels;++i) { + unsigned lHeight = std::max(srcHeight>>i, 1u); + unsigned lWidth = std::max(srcWidth>>i, 1u); + size += lHeight*lWidth; + } + std::vector ret; + ret.reserve(size*4); + + for (unsigned lvl = 0;lvl < levels;++lvl) { + auto height = std::max(srcHeight>>1u, 1u), + width = std::max(srcWidth>>1u, 1u); + + uint8_t const* nextSrc = &ret.back()+1; + + for (unsigned y = 0;y < height;++y) + for (unsigned x = 0;x < width;++x) { + std::array, 4> pixels; + for (unsigned sy = 0;sy < 2;++sy) + for (unsigned sx = 0;sx < 2;++sx) + for (unsigned i = 0;i < 4;++i) { + if (y*2+sy > srcHeight-1) { + if (x*2+sx > srcWidth-1) { + pixels[sy*2+sx][i] = src[((y*2)*srcWidth+x*2)*4+i]; + } else + pixels[sy*2+sx][i] = src[((y*2)*srcWidth+x*2+sx)*4+i]; + } else if (x*2+sx > srcWidth-1) { + pixels[sy*2+sx][i] = src[((y*2+sy)*srcWidth+x*2)*4+i]; + } else + pixels[sy*2+sx][i] = src[((y*2+sy)*srcWidth+x*2+sx)*4+i]; + } + + unsigned asum = 0, rsum = 0, gsum = 0, bsum = 0; + for (unsigned i = 0;i < 4;++i) { + auto alpha = pixels[i][3]; + asum += alpha; + rsum += (pixels[i][2]*alpha)/255u; + gsum += (pixels[i][1]*alpha)/255u; + bsum += (pixels[i][0]*alpha)/255u; + } + if (asum == 0) { + ret.push_back(0u); + ret.push_back(0u); + ret.push_back(0u); + } else { + ret.push_back((255u*bsum)/asum); + ret.push_back((255u*gsum)/asum); + ret.push_back((255u*rsum)/asum); + } + ret.push_back(asum/4u); + } + src = nextSrc; + srcHeight = std::max(srcHeight>>1u, 1u); + srcWidth = std::max(srcWidth>>1u, 1u); + } + + if (colorize) { + size_t pos = 0; + for (unsigned lvl = 1;lvl <= levels;++lvl) { + for (unsigned y = 0;y < origHeight>>lvl;++y) + for (unsigned x = 0;x < origWidth>>lvl;++x) + ret[pos+(y*(origWidth>>lvl)+x)*4+(lvl%3)] = 255u; + pos += (origWidth>>lvl)*(origHeight>>lvl)*4; + } + } + + return ret; + } std::tuple, TextureResource> genTexAtlas(ObjDecoder::Textures const& texs, @@ -71,10 +209,10 @@ namespace render { ++minLg; minPow2 <<= 1; } - if (minPow2 < 32) { - minPow2 = 32; - minLg = 5; - } + // if (minPow2 < 32) { + // minPow2 = 32; + // minLg = 5; + // } printf("Minimum texture size %u, using %u alignment and %u mipmap levels\n", minSize, minPow2, minLg); @@ -117,39 +255,22 @@ namespace render { info.yscale = (info.tex.height-1.0f)/(atlasHeight*info.tex.height); printf("Texture coordinate factors: %f, %f; %f, %f\n", info.xofs, info.yofs, info.xscale, info.yscale); - - for (unsigned y = 0;y < info.alignHeight;++y) { - for (unsigned x = 0;x < info.alignWidth;++x) { - unsigned col; - if (y < info.tex.height) { - if (x < info.tex.width) { - col = info.tex.pixels[y*info.tex.width + x]; - } else { - col = info.tex.pixels[y*info.tex.width + info.tex.width-1]; - } - } else { - if (x < info.tex.width) { - col = info.tex.pixels[(info.tex.height-1)*info.tex.width + x]; - } else { - col = info.tex.pixels[info.tex.height*info.tex.width - 1]; - } - } - if (col == 0xff) { - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4] = 0u; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+1] = 0u; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+2] = 0u; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+3] = 0u; - } else { - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4] = palt[col*3+2]; //vgaPalette[col]>>16; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+1] = palt[col*3+1]; //vgaPalette[col]>>8; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+2] = palt[col*3]; //vgaPalette[col]; - pixels[(y+info.y)*atlasWidth*4+(info.x+x)*4+3] = 255u; - } - } - } + + copyPaletteTexture(info.tex.pixels.data(), info.tex.width, info.tex.height, + pixels.data()+info.y*atlasWidth*4+info.x*4, + atlasWidth*4, + palt); } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlasWidth, atlasHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data()); - glGenerateMipmap(GL_TEXTURE_2D); + auto mipdata = genMipmap(pixels.data(), atlasWidth, atlasHeight, minLg); + size_t pos = 0; + for (unsigned lvl = 1;lvl <= minLg;++lvl) { + unsigned lWidth = std::max(atlasWidth>>lvl, 1u), + lHeight = std::max(atlasHeight>>lvl, 1u); + glTexSubImage2D(GL_TEXTURE_2D, lvl, 0, 0, lWidth, lHeight, + GL_BGRA, GL_UNSIGNED_BYTE, mipdata.data()+pos); + pos += lWidth*lHeight*4; + } return std::make_tuple(std::move(ret), std::move(tex)); } @@ -162,10 +283,12 @@ namespace render { genTexAnim(ObjDecoder::TextureAnimation const& texAnim, PaletteDecoder::Palette const& palt) { - unsigned width = texAnim.width; - if (width%4 != 0) - width += 4 - (width%4); - unsigned height = texAnim.height; + unsigned width = (1< pixels(texAnim.frames*width*height*4); for (unsigned f = 0;f < texAnim.frames;++f) { - for (unsigned y = 0;y < texAnim.height;++y) { - for (unsigned x = 0;x < texAnim.width;++x) { - unsigned col = texAnim.pixels[f][y*texAnim.width+x]; - - if (col == 0xff) { - pixels[f*width*height*4+y*width*4+x*4] = 0u; - pixels[f*width*height*4+y*width*4+x*4+1] = 0u; - pixels[f*width*height*4+y*width*4+x*4+2] = 0u; - pixels[f*width*height*4+y*width*4+x*4+3] = 0u; - } else { - pixels[f*width*height*4+y*width*4+x*4] = palt[col*3+2]; - pixels[f*width*height*4+y*width*4+x*4+1] = palt[col*3+1]; - pixels[f*width*height*4+y*width*4+x*4+2] = palt[col*3]; - pixels[f*width*height*4+y*width*4+x*4+3] = 255u; - } - } - } + copyPaletteTexture(texAnim.pixels[f].data(), texAnim.width, texAnim.height, + pixels.data()+f*width*height*4, width*4, + palt); } glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, texAnim.frames, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data()); - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + unsigned logWidth = ilog2(width), logHeight = ilog2(height); + unsigned levels = std::max(logWidth, logHeight); + for (unsigned f = 0;f < texAnim.frames;++f) { + auto mipdata = genMipmap(pixels.data()+f*width*height*4, width, height, levels); + size_t pos = 0; + for (unsigned lvl = 1;lvl <= levels;++lvl) { + unsigned lWidth = std::max(width>>lvl, 1u), + lHeight = std::max(height>>lvl, 1u); + + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, lvl, 0, 0, f, lWidth, lHeight, 1, + GL_BGRA, GL_UNSIGNED_BYTE, mipdata.data()+pos); + pos += lWidth*lHeight*4; + } + } return std::make_tuple(animInfo, std::move(tex)); } diff --git a/shaders/object.fs b/shaders/object.fs index f3bf1f0..6f98a7d 100644 --- a/shaders/object.fs +++ b/shaders/object.fs @@ -5,21 +5,19 @@ layout(location = 2) uniform sampler2D texBase; layout(location = 3) uniform sampler2DArray texAnim; layout(location = 4) uniform uint animFrame; -layout(location = 5) uniform vec3 IDcolor; in vec2 fragTC; flat in uint fragUseAnimTex; layout(location = 0) out vec4 color; -layout(location = 1) out vec4 IDcolor_out; layout(depth_unchanged) out float gl_FragDepth; void main(void) { - color = bool(fragUseAnimTex)?texture(texAnim, vec3(fragTC, animFrame)):texture(texBase, fragTC); + vec4 texel = bool(fragUseAnimTex)?texture(texAnim, vec3(fragTC, animFrame)):texture(texBase, fragTC); - if (color.w < 0.5) + if (texel.a < 0.5) discard; - - IDcolor_out = vec4(IDcolor, 1.0); + + color = texel; }