Clean up object texture loading, custom mipmap generator
This commit is contained in:
236
render/Object.cc
236
render/Object.cc
@@ -45,6 +45,144 @@ namespace render {
|
|||||||
float xofs, yofs, xscale, yscale;
|
float xofs, yofs, xscale, yscale;
|
||||||
ObjDecoder::Texture const& tex;
|
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<unsigned>(x) > srcWidth-1)
|
||||||
|
x = srcWidth-1;
|
||||||
|
if (static_cast<unsigned>(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<uint8_t> genMipmap(uint8_t const* src, unsigned srcWidth, unsigned srcHeight,
|
||||||
|
unsigned levels, bool colorize = false)
|
||||||
|
{
|
||||||
|
if (!levels)
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
|
||||||
|
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<uint8_t> 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<std::array<uint8_t, 4>, 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<std::vector<TexAtlasInfo>, TextureResource>
|
std::tuple<std::vector<TexAtlasInfo>, TextureResource>
|
||||||
genTexAtlas(ObjDecoder::Textures const& texs,
|
genTexAtlas(ObjDecoder::Textures const& texs,
|
||||||
@@ -71,10 +209,10 @@ namespace render {
|
|||||||
++minLg;
|
++minLg;
|
||||||
minPow2 <<= 1;
|
minPow2 <<= 1;
|
||||||
}
|
}
|
||||||
if (minPow2 < 32) {
|
// if (minPow2 < 32) {
|
||||||
minPow2 = 32;
|
// minPow2 = 32;
|
||||||
minLg = 5;
|
// minLg = 5;
|
||||||
}
|
// }
|
||||||
printf("Minimum texture size %u, using %u alignment and %u mipmap levels\n",
|
printf("Minimum texture size %u, using %u alignment and %u mipmap levels\n",
|
||||||
minSize, minPow2, minLg);
|
minSize, minPow2, minLg);
|
||||||
|
|
||||||
@@ -117,39 +255,22 @@ namespace render {
|
|||||||
info.yscale = (info.tex.height-1.0f)/(atlasHeight*info.tex.height);
|
info.yscale = (info.tex.height-1.0f)/(atlasHeight*info.tex.height);
|
||||||
printf("Texture coordinate factors: %f, %f; %f, %f\n",
|
printf("Texture coordinate factors: %f, %f; %f, %f\n",
|
||||||
info.xofs, info.yofs, info.xscale, info.yscale);
|
info.xofs, info.yofs, info.xscale, info.yscale);
|
||||||
|
|
||||||
for (unsigned y = 0;y < info.alignHeight;++y) {
|
copyPaletteTexture(info.tex.pixels.data(), info.tex.width, info.tex.height,
|
||||||
for (unsigned x = 0;x < info.alignWidth;++x) {
|
pixels.data()+info.y*atlasWidth*4+info.x*4,
|
||||||
unsigned col;
|
atlasWidth*4,
|
||||||
if (y < info.tex.height) {
|
palt);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlasWidth, atlasHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data());
|
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));
|
return std::make_tuple(std::move(ret), std::move(tex));
|
||||||
}
|
}
|
||||||
@@ -162,10 +283,12 @@ namespace render {
|
|||||||
genTexAnim(ObjDecoder::TextureAnimation const& texAnim,
|
genTexAnim(ObjDecoder::TextureAnimation const& texAnim,
|
||||||
PaletteDecoder::Palette const& palt)
|
PaletteDecoder::Palette const& palt)
|
||||||
{
|
{
|
||||||
unsigned width = texAnim.width;
|
unsigned width = (1<<ilog2(texAnim.width));
|
||||||
if (width%4 != 0)
|
if (width < texAnim.width)
|
||||||
width += 4 - (width%4);
|
width <<= 1;
|
||||||
unsigned height = texAnim.height;
|
unsigned height = (1<<ilog2(texAnim.height));
|
||||||
|
if (height < texAnim.height)
|
||||||
|
height <<= 1;
|
||||||
TextureResource tex = create2DArrayTexture(width, height, texAnim.frames, true);
|
TextureResource tex = create2DArrayTexture(width, height, texAnim.frames, true);
|
||||||
|
|
||||||
AnimTexInfo animInfo;
|
AnimTexInfo animInfo;
|
||||||
@@ -177,27 +300,26 @@ namespace render {
|
|||||||
std::vector<uint8_t> pixels(texAnim.frames*width*height*4);
|
std::vector<uint8_t> pixels(texAnim.frames*width*height*4);
|
||||||
|
|
||||||
for (unsigned f = 0;f < texAnim.frames;++f) {
|
for (unsigned f = 0;f < texAnim.frames;++f) {
|
||||||
for (unsigned y = 0;y < texAnim.height;++y) {
|
copyPaletteTexture(texAnim.pixels[f].data(), texAnim.width, texAnim.height,
|
||||||
for (unsigned x = 0;x < texAnim.width;++x) {
|
pixels.data()+f*width*height*4, width*4,
|
||||||
unsigned col = texAnim.pixels[f][y*texAnim.width+x];
|
palt);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, texAnim.frames,
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, texAnim.frames,
|
||||||
GL_BGRA, GL_UNSIGNED_BYTE, pixels.data());
|
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));
|
return std::make_tuple(animInfo, std::move(tex));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,19 @@
|
|||||||
layout(location = 2) uniform sampler2D texBase;
|
layout(location = 2) uniform sampler2D texBase;
|
||||||
layout(location = 3) uniform sampler2DArray texAnim;
|
layout(location = 3) uniform sampler2DArray texAnim;
|
||||||
layout(location = 4) uniform uint animFrame;
|
layout(location = 4) uniform uint animFrame;
|
||||||
layout(location = 5) uniform vec3 IDcolor;
|
|
||||||
|
|
||||||
in vec2 fragTC;
|
in vec2 fragTC;
|
||||||
|
|
||||||
flat in uint fragUseAnimTex;
|
flat in uint fragUseAnimTex;
|
||||||
|
|
||||||
layout(location = 0) out vec4 color;
|
layout(location = 0) out vec4 color;
|
||||||
layout(location = 1) out vec4 IDcolor_out;
|
|
||||||
layout(depth_unchanged) out float gl_FragDepth;
|
layout(depth_unchanged) out float gl_FragDepth;
|
||||||
|
|
||||||
void main(void) {
|
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;
|
discard;
|
||||||
|
|
||||||
IDcolor_out = vec4(IDcolor, 1.0);
|
color = texel;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user