584 lines
20 KiB
C++
584 lines
20 KiB
C++
#include <set>
|
|
|
|
#include <glbinding/gl/gl.h>
|
|
|
|
#include <glm/gtx/transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/gtc/packing.hpp>
|
|
|
|
#include "Object.hh"
|
|
#include "ProgramProvider.hh"
|
|
#include "Renderer.hh"
|
|
#include "ObjDecoder.hh"
|
|
#include "PaletteDecoder.hh"
|
|
#include "renderutil.hh"
|
|
|
|
using namespace gl;
|
|
|
|
namespace render {
|
|
namespace {
|
|
struct VertexAttribs {
|
|
int32_t vertex[3];
|
|
uint16_t texCoords[2];
|
|
uint8_t useTexAnim;
|
|
|
|
bool operator<(VertexAttribs const& other) const {
|
|
if (vertex[0] == other.vertex[0]) {
|
|
if (vertex[1] == other.vertex[1]) {
|
|
if (vertex[2] == other.vertex[2]) {
|
|
if (texCoords[0] == other.texCoords[0]) {
|
|
if (texCoords[1] == other.texCoords[1])
|
|
return (useTexAnim < other.useTexAnim);
|
|
return (texCoords[1] < other.texCoords[1]);
|
|
}
|
|
return (texCoords[0] < other.texCoords[0]);
|
|
}
|
|
return (vertex[2] < other.vertex[2]);
|
|
}
|
|
return (vertex[1] < other.vertex[1]);
|
|
}
|
|
return (vertex[0] < other.vertex[0]);
|
|
}
|
|
};
|
|
|
|
struct TexAtlasInfo {
|
|
unsigned x, y;
|
|
unsigned alignWidth, alignHeight;
|
|
float xofs, yofs, xscale, yscale;
|
|
ObjDecoder::Texture const& tex;
|
|
};
|
|
|
|
/* Copy from paletted texture 'src' into BGRA texture 'dst' using palette 'palt'
|
|
Palette entry 0xff is transparent, all others are opaque.
|
|
For opaque pixels the color components are copied from a neighbouring
|
|
opaque pixel if available.
|
|
*/
|
|
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];
|
|
};
|
|
|
|
uint8_t min = 0xff, max = 0;
|
|
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
|
|
if (col > 0) {
|
|
min = std::min(min, col);
|
|
max = std::max(max, col);
|
|
}
|
|
}
|
|
}
|
|
dst += (dstStride - srcWidth*4);
|
|
}
|
|
printf("tex value range %hhu..%hhu\n", min, max);
|
|
}
|
|
|
|
/* Generate Mipmaps for BGRA texture 'src'.
|
|
The mipmapping algorithm computes the alpha-weighted average of the color
|
|
values, and the average of the alpha values, of a 2x2 block of pixels.
|
|
*/
|
|
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>
|
|
genTexAtlas(ObjDecoder::Textures const& texs,
|
|
std::set<uint16_t> const& usedTexs,
|
|
PaletteDecoder::Palette const& palt)
|
|
{
|
|
// Build texture atlas for object
|
|
unsigned maxTex, maxLayers;
|
|
std::tie(maxTex, maxLayers) = getGLTextureMaximums();
|
|
printf("Maximum texture size: %d, maximum array texture layers: %d\n", maxTex, maxLayers);
|
|
|
|
// If the atlas gets too large we may have problems with texture coordinate precision
|
|
if (maxTex > 8192)
|
|
maxTex = 8192;
|
|
|
|
unsigned minSize = std::numeric_limits<unsigned>::max();
|
|
unsigned accumWidth = 0, maxHeight = 0, maxWidth = 0;
|
|
unsigned i = 0;
|
|
unsigned pixelCount = 0;
|
|
for (auto& tex : texs) {
|
|
if (usedTexs.find(i++) == usedTexs.end())
|
|
continue;
|
|
|
|
minSize = std::min<unsigned>(minSize, std::min<unsigned>(tex.width, tex.height));
|
|
accumWidth += tex.width;
|
|
maxHeight = std::max<unsigned>(maxHeight, tex.height);
|
|
maxWidth = std::max<unsigned>(maxWidth, tex.width);
|
|
pixelCount += tex.height*tex.width;
|
|
}
|
|
|
|
// Set lower bound of minSize to ensure a minimum number of mipmap levels
|
|
minSize = std::max(minSize, 16u);
|
|
|
|
unsigned minLg = ilog2(minSize), minPow2 = (1<<minLg);
|
|
if (minPow2 < minSize) {
|
|
++minLg;
|
|
minPow2 <<= 1;
|
|
}
|
|
|
|
printf("Minimum texture size %u, using %u alignment and %u mipmap levels\n",
|
|
minSize, minPow2, minLg);
|
|
|
|
// Try to get an aprox. square atlas
|
|
unsigned accumWidth2 = 1<<(ilog2(sqrt(accumWidth*maxHeight)));
|
|
unsigned maxWidth2 = 1u<<ilog2(maxWidth);
|
|
if (maxWidth2 < maxWidth)
|
|
maxWidth2 <<= 1;
|
|
accumWidth2 = std::min(std::max(maxWidth2, accumWidth2), maxTex);
|
|
printf("Squarified target width %u\n", accumWidth2);
|
|
|
|
std::vector<TexAtlasInfo> ret;
|
|
unsigned xpos = 0, ypos = 0, lineMaxHeight = 0;
|
|
maxWidth = 0;
|
|
i = 0;
|
|
for (auto& tex : texs) {
|
|
if (usedTexs.find(i++) == usedTexs.end()) {
|
|
TexAtlasInfo info = {0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, tex};
|
|
ret.push_back(info);
|
|
continue;
|
|
}
|
|
|
|
unsigned alignWidth = (tex.width%minPow2 == 0)?tex.width:tex.width+(minPow2-tex.width%minPow2);
|
|
unsigned alignHeight = (tex.height%minPow2 == 0)?tex.height:tex.height+(minPow2-tex.height%minPow2);
|
|
|
|
if (xpos + alignWidth > accumWidth2) {
|
|
if (alignWidth > maxTex)
|
|
throw Exception{"Cannot fit obj textures into GL texture"};
|
|
ypos += lineMaxHeight;
|
|
maxWidth = std::max(maxWidth, xpos);
|
|
lineMaxHeight = 0;
|
|
xpos = 0;
|
|
}
|
|
if (ypos + alignHeight > static_cast<unsigned>(maxTex))
|
|
throw Exception{"Cannot fit obj textures into GL texture"};
|
|
|
|
TexAtlasInfo info = {xpos, ypos, alignWidth, alignHeight, 0.0f, 0.0f, 0.0f, 0.0f, tex};
|
|
ret.push_back(info);
|
|
|
|
lineMaxHeight = std::max<unsigned>(lineMaxHeight, alignHeight);
|
|
xpos += alignWidth;
|
|
}
|
|
unsigned atlasWidth = std::max(xpos, maxWidth), atlasHeight = ypos+lineMaxHeight;
|
|
printf("Texture atlas size: %ux%u, %.1f%% wasted\n",
|
|
atlasWidth, atlasHeight,
|
|
(atlasWidth*atlasHeight-pixelCount)*100.0/(atlasWidth*atlasHeight));
|
|
|
|
TextureResource tex = create2DTexture(atlasWidth, atlasHeight, true, minLg+1);
|
|
|
|
std::vector<uint8_t> pixels(atlasWidth*atlasHeight*4);
|
|
// Copy textures into atlas
|
|
for (auto& info : ret) {
|
|
if (info.alignWidth == 0)
|
|
continue;
|
|
|
|
info.xofs = (info.x+0.5f)/atlasWidth;
|
|
info.yofs = (info.y+0.5f)/atlasHeight;
|
|
info.xscale = (info.tex.width-1.0f)/(atlasWidth*info.tex.width);
|
|
info.yscale = (info.tex.height-1.0f)/(atlasHeight*info.tex.height);
|
|
#ifdef TEXDEBUG
|
|
printf("Texture coordinate factors: %f, %f; %f, %f\n",
|
|
info.xofs, info.yofs, info.xscale, info.yscale);
|
|
#endif
|
|
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());
|
|
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));
|
|
}
|
|
|
|
struct AnimTexInfo {
|
|
float xofs, yofs, xscale, yscale;
|
|
};
|
|
|
|
std::tuple<AnimTexInfo, TextureResource>
|
|
genTexAnim(ObjDecoder::TextureAnimation const& texAnim,
|
|
PaletteDecoder::Palette const& palt)
|
|
{
|
|
unsigned width = (1<<ilog2(texAnim.width));
|
|
if (width < texAnim.width)
|
|
width <<= 1;
|
|
unsigned height = (1<<ilog2(texAnim.height));
|
|
if (height < texAnim.height)
|
|
height <<= 1;
|
|
TextureResource tex = create2DArrayTexture(width, height, texAnim.frames, true);
|
|
|
|
AnimTexInfo animInfo;
|
|
animInfo.xofs = 0.5f/width;
|
|
animInfo.yofs = 0.5f/height;
|
|
animInfo.xscale = (texAnim.width-1.0f)/(width*texAnim.width);
|
|
animInfo.yscale = (texAnim.height-1.0f)/(height*texAnim.height);
|
|
|
|
std::vector<uint8_t> pixels(texAnim.frames*width*height*4);
|
|
|
|
for (unsigned f = 0;f < texAnim.frames;++f) {
|
|
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());
|
|
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));
|
|
}
|
|
|
|
std::tuple<std::vector<VertexAttribs>, std::vector<uint16_t> >
|
|
genVertexAttribs(ObjDecoder& obj, ObjDecoder::Triangles const& tris,
|
|
ObjDecoder::Quads const& quads, std::vector<TexAtlasInfo>& atlasInfo,
|
|
AnimTexInfo *animTex = nullptr)
|
|
{
|
|
// Deduplicate vertex attributes, track indices
|
|
std::map<VertexAttribs, size_t> vertexAttribsMap;
|
|
std::vector<VertexAttribs> vertexAttribs;
|
|
std::vector<uint16_t> indices;
|
|
|
|
auto& vertices = obj.getVertices();
|
|
for (auto& tri : tris) {
|
|
for (unsigned i = 0;i < 3;++i) {
|
|
VertexAttribs attrs;
|
|
std::copy(vertices.at(tri.vertIdx[i]).begin(), vertices.at(tri.vertIdx[i]).end(),
|
|
attrs.vertex);
|
|
if (tri.tex < atlasInfo.size()) {
|
|
auto& tex = atlasInfo[tri.tex];
|
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + tri.texCoords[i][0]*tex.xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + tri.texCoords[i][1]*tex.yscale);
|
|
attrs.useTexAnim = 0u;
|
|
} else if ((tri.tex == atlasInfo.size()) && animTex) {
|
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + tri.texCoords[i][0]*animTex->xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + tri.texCoords[i][1]*animTex->yscale);
|
|
attrs.useTexAnim = 1u;
|
|
} else
|
|
throw Exception{"Texture index out of range"};
|
|
|
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
|
if (ins.second) {
|
|
indices.push_back(vertexAttribs.size());
|
|
vertexAttribs.push_back(attrs);
|
|
} else
|
|
indices.push_back(ins.first->second);
|
|
}
|
|
}
|
|
|
|
for (auto& quad : quads) {
|
|
for (unsigned i = 0;i < 3;++i) {
|
|
VertexAttribs attrs;
|
|
std::copy(vertices.at(quad.vertIdx[i]).begin(), vertices.at(quad.vertIdx[i]).end(),
|
|
attrs.vertex);
|
|
if (quad.tex < atlasInfo.size()) {
|
|
auto& tex = atlasInfo[quad.tex];
|
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + quad.texCoords[i][0]*tex.xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + quad.texCoords[i][1]*tex.yscale);
|
|
attrs.useTexAnim = 0u;
|
|
} else if ((quad.tex == atlasInfo.size()) && animTex) {
|
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + quad.texCoords[i][0]*animTex->xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + quad.texCoords[i][1]*animTex->yscale);
|
|
attrs.useTexAnim = 1u;
|
|
} else
|
|
throw Exception{"Texture index out of range"};
|
|
|
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
|
if (ins.second) {
|
|
indices.push_back(vertexAttribs.size());
|
|
vertexAttribs.push_back(attrs);
|
|
} else
|
|
indices.push_back(ins.first->second);
|
|
}
|
|
for (unsigned i = 0;i < 3;++i) {
|
|
VertexAttribs attrs;
|
|
std::copy(vertices.at(quad.vertIdx[i?(i+1):i]).begin(), vertices.at(quad.vertIdx[i?(i+1):i]).end(),
|
|
attrs.vertex);
|
|
if (quad.tex < atlasInfo.size()) {
|
|
auto& tex = atlasInfo[quad.tex];
|
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + quad.texCoords[i?(i+1):i][0]*tex.xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + quad.texCoords[i?(i+1):i][1]*tex.yscale);
|
|
attrs.useTexAnim = 0u;
|
|
} else if ((quad.tex == atlasInfo.size()) && animTex) {
|
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + quad.texCoords[i?(i+1):i][0]*animTex->xscale);
|
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + quad.texCoords[i?(i+1):i][1]*animTex->yscale);
|
|
attrs.useTexAnim = 1u;
|
|
} else
|
|
throw Exception{"Texture index out of range"};
|
|
|
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
|
if (ins.second) {
|
|
indices.push_back(vertexAttribs.size());
|
|
vertexAttribs.push_back(attrs);
|
|
} else
|
|
indices.push_back(ins.first->second);
|
|
}
|
|
}
|
|
|
|
printf("%lu indices, %lu attributes, %.1f%% attrib reuse\n",
|
|
indices.size(), vertexAttribs.size(),
|
|
(indices.size()-vertexAttribs.size())*100.0/indices.size());
|
|
|
|
return std::make_tuple(std::move(vertexAttribs), std::move(indices));
|
|
}
|
|
|
|
}
|
|
|
|
Object::Object(Renderer& renderer, ObjDecoder& obj, PaletteDecoder& palt, unsigned lod)
|
|
: TransformDrawable(renderer),
|
|
vbo_(), rot_(0.0f), animFrame_(0)
|
|
{
|
|
// Acquire shader
|
|
program_ = ProgramProvider::getInstance().getProgram("object", "object");
|
|
|
|
// Determine used textures
|
|
auto tris = obj.getTriangles(lod);
|
|
auto quads = obj.getQuads(lod);
|
|
std::set<uint16_t> usedTexs;
|
|
|
|
for (auto& tri: tris)
|
|
usedTexs.insert(tri.tex);
|
|
for (auto& quad: quads)
|
|
usedTexs.insert(quad.tex);
|
|
|
|
// Generate texture atlas
|
|
auto& texs = obj.getTextures();
|
|
std::vector<TexAtlasInfo> atlasInfo;
|
|
if (texs.size() > 0) {
|
|
std::tie(atlasInfo, tex_) = genTexAtlas(texs, usedTexs, palt.getPalette());
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<int>(GL_LINEAR_MIPMAP_LINEAR));
|
|
}
|
|
|
|
auto& texAnims = obj.getTextureAnimations();
|
|
AnimTexInfo animInfo;
|
|
if ((usedTexs.find(atlasInfo.size()) != usedTexs.end()) && (texAnims.size() > 0)) {
|
|
std::tie(animInfo, texAnim_) = genTexAnim(texAnims[0], palt.getPalette());
|
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, static_cast<int>(GL_LINEAR_MIPMAP_LINEAR));
|
|
}
|
|
|
|
// Generate vertex attribute array
|
|
std::vector<VertexAttribs> vertexAttribs;
|
|
std::vector<uint16_t> indices;
|
|
if (texAnims.size() > 0)
|
|
std::tie(vertexAttribs, indices) = genVertexAttribs(obj, tris, quads, atlasInfo, &animInfo);
|
|
else
|
|
std::tie(vertexAttribs, indices) = genVertexAttribs(obj, tris, quads, atlasInfo);
|
|
|
|
// Setup GL vertex buffer and vertex array
|
|
glGenVertexArrays(1, &vertexArray_.get());
|
|
vbo_ = VBOManager::getInstance().alloc(sizeof(VertexAttribs)*vertexAttribs.size()+
|
|
2*indices.size());
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
|
|
glBindVertexArray(vertexArray_);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_.getVBOId());
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_INT, GL_FALSE, sizeof(VertexAttribs),
|
|
vbo_.getOfs(offsetof(VertexAttribs, vertex)));
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexAttribs),
|
|
vbo_.getOfs(offsetof(VertexAttribs, texCoords)));
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribIPointer(2, 1, GL_UNSIGNED_BYTE, sizeof(VertexAttribs),
|
|
vbo_.getOfs(offsetof(VertexAttribs, useTexAnim)));
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(),
|
|
sizeof(VertexAttribs)*vertexAttribs.size(),
|
|
vertexAttribs.data());
|
|
|
|
maxIndex_ = vertexAttribs.size()-1;
|
|
indexOfs_ = sizeof(VertexAttribs)*vertexAttribs.size();
|
|
numIndices_ = indices.size();
|
|
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, vbo_.getBase()+indexOfs_,
|
|
2*numIndices_,
|
|
indices.data());
|
|
|
|
vp_ = glm::perspectiveFov(75.0f, static_cast<float>(renderer.getWidth()),
|
|
static_cast<float>(renderer.getHeight()),
|
|
7500.f, 800000.0f) *
|
|
glm::lookAt(glm::vec3(0.0f, 0.0f, 200000),
|
|
glm::vec3(0.0f, 0.0f, 0),
|
|
glm::vec3(0, 1, 0));
|
|
|
|
int maxVertices, maxIndices;
|
|
glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
|
|
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
|
|
|
|
printf("Max recommened vertices: %d, indices: %d\n",
|
|
maxVertices, maxIndices);
|
|
}
|
|
|
|
void Object::draw()
|
|
{
|
|
glDisable(GL_CULL_FACE);
|
|
//glFrontFace(GL_CW);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
useProgram(program_);
|
|
glUniformMatrix4fv(0, 1, GL_FALSE,
|
|
glm::value_ptr(vp_));
|
|
glUniformMatrix4fv(1, 1, GL_FALSE,
|
|
glm::value_ptr(transformMatrix_));
|
|
glUniform1i(2, 0);
|
|
glUniform1i(3, 1);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex_);
|
|
if (texAnim_) {
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texAnim_);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glUniform1ui(4, animFrame_);
|
|
}
|
|
|
|
glBindVertexArray(vertexArray_);
|
|
|
|
glDrawRangeElements(GL_TRIANGLES, 0, maxIndex_,
|
|
numIndices_, GL_UNSIGNED_SHORT, vbo_.getOfs(indexOfs_));
|
|
}
|
|
}
|
|
|