Files
2018-07-17 14:15:37 +02:00

424 lines
11 KiB
C++

#include "render/gl_texture.h"
#include "main/references.h"
#include "main/logging.h"
#include <assert.h>
#include <algorithm>
namespace render {
extern bool isIntelCard;
void GLTexture::bind() {
glBindTexture(GL_TEXTURE_2D, glName);
}
unsigned GLTexture::getID() const {
return glName;
}
GLTexture::GLTexture()
: glName(0), pixelDepth(1)
{
type = TT_2D;
loaded = false;
}
GLTexture::GLTexture(Image& image, bool mipmap, bool cachePixels)
: glName(0), pixelDepth(1)
{
type = TT_2D;
loaded = false;
load(image, mipmap, cachePixels);
}
void createTexture(Image& image, bool mipmap) {
GLenum format;
unsigned levels = 1;
if(mipmap) {
unsigned smallDim = std::min(image.width, image.height);
while(smallDim > 2) {
smallDim /= 2;
levels += 1;
}
}
if(GLEW_ARB_texture_storage) {
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE8;
//{
// GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
// glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
//}
break;
case FMT_Alpha:
format = GL_ALPHA8;
//{
// GLint swizzleMask[] = {GL_ONE, GL_ONE, GL_ONE, GL_RED};
// glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
//}
break;
case FMT_RGB:
format = GL_RGB8; break;
case FMT_RGBA:
format = GL_RGBA8; break;
NO_DEFAULT
}
glTexStorage2D(GL_TEXTURE_2D, levels, format, image.width, image.height);
if(!isIntelCard)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
}
else {
//Sized lum/alpha are from texture storage arb
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
unsigned w = image.width, h = image.height;
for(unsigned i = 0; i < levels; ++i) {
glTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0, format, GL_UNSIGNED_BYTE, nullptr);
w /= 2;
h /= 2;
}
if(!isIntelCard)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
}
devices.render->reportErrors("creating texture");
}
void loadTexture(Image& image, const recti& pixels, unsigned lod = 0) {
devices.render->reportErrors();
GLenum format;
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
assert((unsigned)(pixels.topLeft.x + pixels.getWidth()) <= image.width);
assert((unsigned)(pixels.topLeft.y + pixels.getHeight()) <= image.height);
if(pixels.getSize() == vec2i(image.width, image.height)) {
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)image.grey; break;
case FMT_Alpha:
ptrPixels = (void*)image.grey; break;
case FMT_RGB:
ptrPixels = (void*)image.rgb; break;
case FMT_RGBA:
ptrPixels = (void*)image.rgba; break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, pixels.topLeft.y, pixels.getWidth(), pixels.getHeight(), format, GL_UNSIGNED_BYTE, ptrPixels);
}
else if((unsigned)pixels.getWidth() == image.width) {
assert(pixels.topLeft.x == 0);
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)&image.get_grey(0, pixels.topLeft.y); break;
case FMT_Alpha:
ptrPixels = (void*)&image.get_alpha(0, pixels.topLeft.y); break;
case FMT_RGB:
ptrPixels = (void*)&image.get_rgb(0, pixels.topLeft.y); break;
case FMT_RGBA:
ptrPixels = (void*)&image.get_rgba(0, pixels.topLeft.y); break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, pixels.topLeft.y, pixels.getWidth(), pixels.getHeight(), format, GL_UNSIGNED_BYTE, ptrPixels);
}
else {
//Must load line by line
for(unsigned y = pixels.topLeft.y, endY = pixels.botRight.y; y < endY; ++y) {
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)&image.get_grey(pixels.topLeft.x, y); break;
case FMT_Alpha:
ptrPixels = (void*)&image.get_alpha(pixels.topLeft.x, y); break;
case FMT_RGB:
ptrPixels = (void*)&image.get_rgb(pixels.topLeft.x, y); break;
case FMT_RGBA:
ptrPixels = (void*)&image.get_rgba(pixels.topLeft.x, y); break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, y, pixels.getWidth(), 1, format, GL_UNSIGNED_BYTE, ptrPixels);
}
}
devices.render->reportErrors("filling sub image");
}
void GLTexture::loadStart(Image& image, bool mipmap, bool cachePixels, unsigned lod) {
devices.render->reportErrors();
loaded = true;
if(lod == 0) {
//Destroy old texture
if(glName != 0) {
glDeleteTextures(1, &glName);
glName = 0;
}
//Generate a new texture
glGenTextures(1,&glName);
//Store texture size
size = vec2i(image.width, image.height);
pixelDepth = ColorDepths[image.format];
if(cachePixels)
activePixels.resize(size.x * size.y);
}
glBindTexture(GL_TEXTURE_2D, glName);
if(lod == 0)
createTexture(image, mipmap);
}
void GLTexture::loadPartial(Image& image, const recti& pixels, bool cachePixels, unsigned lod) {
glBindTexture(GL_TEXTURE_2D, glName);
loadTexture(image, pixels, lod);
if(cachePixels && lod == 0) {
for(int y = pixels.topLeft.y; y < pixels.getHeight(); ++y) {
for(int x = pixels.topLeft.x; x < pixels.getWidth(); ++x) {
unsigned char a = image.get(x, y).a;
activePixels[y * size.width + x] = a != 0;
}
}
}
}
void GLTexture::loadFinish(bool mipmap, unsigned lod) {
devices.render->reportErrors();
glBindTexture(GL_TEXTURE_2D, glName);
hasMipMaps = mipmap;
if(mipmap) {
//Set linear mipmap filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if(lod == 0) {
if(size.width * size.height > (1024 * 1024))
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
else
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glGenerateMipmap(GL_TEXTURE_2D);
}
}
else if(lod == 0) {
//Set linear filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else {
//We're defining mipmap data now, switch back to mipmaping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, lod);
}
devices.render->reportErrors("finalizing texture");
}
unsigned GLTexture::getTextureBytes() const {
unsigned bytes = size.width * size.height * pixelDepth;
if(hasMipMaps)
bytes += bytes / 3;
return bytes;
}
void GLTexture::load(Image& image, bool mipmap, bool cachePixels) {
loadStart(image, mipmap, cachePixels);
loadPartial(image, recti(0,0, image.width, image.height), cachePixels);
loadFinish(mipmap);
}
void GLTexture::save(Image& image) const {
image.resize(size.width, size.height, FMT_RGBA);
glBindTexture(GL_TEXTURE_2D, glName);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.rgba);
}
bool GLTexture::isPixelActive(vec2i px) const {
unsigned index = px.y * size.width + px.x;
if(activePixels.empty())
return true;
if(index >= activePixels.size())
return false;
return activePixels[index];
}
GLTexture::~GLTexture() {
glDeleteTextures(1, &glName);
}
bool GLCubeMap::isPixelActive(vec2i px) const {
return true;
}
void GLCubeMap::load(Image& image, bool mipmap, bool cachePixels) {
devices.render->reportErrors();
loaded = false;
vec2u tile = vec2u(image.width / 4, image.height / 3);
if(tile.width != tile.height || tile.width == 0) {
error("ERROR loading cubemap: Cubemap must have a 4:3 aspect ratio.");
return;
}
//Destroy old texture
if(glName != 0) {
glDeleteTextures(1, &glName);
glName = 0;
}
//Generate a new texture
glGenTextures(1,&glName);
//Store texture size
size = vec2i(image.width, image.height);
loaded = true;
pixelDepth = ColorDepths[image.format];
glBindTexture(GL_TEXTURE_CUBE_MAP, glName);
unsigned char* data = image.grey;
GLenum format;
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
auto getTile = [&](unsigned x, unsigned y) -> Image* {
vec2u top = vec2u(tile.width * x, tile.height * y);
return image.crop(rect<unsigned>(top, top + tile));
};
auto* img = getTile(1,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(2,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(1,2);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(3,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(0,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(1,0);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
devices.render->reportErrors("loading cubemap images");
hasMipMaps = mipmap;
if(mipmap) {
//Set linear mipmap filter
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if(size.width * size.height > (1024 * 1024))
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
else
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
}
else {
//Set linear filter
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
devices.render->reportErrors("finalizing texture");
}
unsigned GLCubeMap::getTextureBytes() const {
unsigned bytes = size.width * size.height * pixelDepth * 6;
if(hasMipMaps)
bytes += bytes / 3;
return bytes;
}
void GLCubeMap::save(Image& image) const {}
void GLCubeMap::bind() {
glBindTexture(GL_TEXTURE_CUBE_MAP, glName);
}
unsigned GLCubeMap::getID() const {
return glName;
}
GLCubeMap::GLCubeMap(Image& image, bool mipmap) : glName(0), pixelDepth(1) {
loaded = false;
type = TT_Cubemap;
load(image, mipmap);
}
GLCubeMap::GLCubeMap() : glName(0), pixelDepth(0) {
loaded = false;
type = TT_Cubemap;
}
GLCubeMap::~GLCubeMap() {
glDeleteTextures(1, &glName);
}
void GLCubeMap::loadStart(Image& image, bool mipmap, bool cachePixels, unsigned lod) {
load(image, mipmap);
}
void GLCubeMap::loadPartial(Image& image, const recti& pixels, bool cachePixels, unsigned lod) {
}
void GLCubeMap::loadFinish(bool mipmap, unsigned lod) {
}
};