yabba/texture.cpp
#include "texture.hpp"
#include "io.hpp"
#include <dirent.h>
Ppm::Ppm(GLfloat* bb, GLsizei ww, GLsizei hh, char* nn): buf(bb), w(ww), h(hh), name(nn) {
}
Ppm* Ppm::getInst(const char* fn) {
char* src;
size_t len;
if (!file_read(fn, src, len)) {
return NULL;
}
int l = 0;
unsigned type;
unsigned ww, hh, max;
if (sscanf(src, "P%u %u %u %u %n", &type, &ww, &hh, &max, &l) == 4) {
} else if (sscanf(src, "P%u #%*[^\n] %u %u %u %n", &type, &ww, &hh, &max, &l) == 4) {
} else {
LOG("'%s': invalid PPM header", fn);
free(src);
return NULL;
}
unsigned char* p = (unsigned char*)src + l;
const unsigned total = ww*hh*3;
if (!ww || !hh || !max || len != total+l) {
LOG("'%s': invalid PPM size", fn);
free(src);
return NULL;
}
GLfloat* buf = (GLfloat*)malloc(ww*hh*3*sizeof(GLfloat));
if (type == 6) {
for (unsigned i=0; i<total; ++i) {
buf[i] = (float)p[i] / (float)max; // TODO: textures start at (0,0), PPMs at (0,h) or so
assert(buf[i] >= 0.0 && buf[i] <= 1.0);
}
} else if (type == 3) {
for (unsigned i=0; i<ww*hh*3; i+=3) {
unsigned r, g, b;
if (sscanf((char*)p, "%u %u %u%n", &r, &g, &b, &l) != 3) {
LOG("'%s': cannot parse PPM pixel", fn);
free(src);
free(buf);
return NULL;
}
buf[i+0] = (float)r / (float)max;
buf[i+1] = (float)g / (float)max;
buf[i+2] = (float)b / (float)max;
assert(buf[i+0] >= 0.0 && buf[i+0] <= 1.0);
assert(buf[i+1] >= 0.0 && buf[i+1] <= 1.0);
assert(buf[i+2] >= 0.0 && buf[i+2] <= 1.0);
}
} else {
LOG("'%s': unknown PPM type", fn);
}
free(src);
const char* s = strrchr(fn, '/') ?: fn;
const char* e = strrchr(fn, '.') ?: fn+strlen(fn)-1;
char* n = strndup(s+1, e-s-1);
return new Ppm(buf, ww, hh, n);
}
Ppm::~Ppm() {
free((void*)buf);
free((void*)name);
}
GLuint Texture::textures[80] = {};
unsigned Texture::tex_no = 0;
unsigned Texture::refcount = 0;
GLenum Texture::os2tex(unsigned i) {
return GL_TEXTURE0 + i; // TODO: check if we do need a switch statement for all possible values here
}
Texture::Texture(const char* id): index(tex_no++), name(textures[index]), tex(os2tex(index)), file(strdup(id)), w(0), h(0) {
assert((unsigned)index < sizeof(textures)/sizeof(textures[0]));
if (refcount++ == 0) {
glGenTextures(sizeof(textures)/sizeof(textures[0]), textures);
}
glActiveTexture(tex);
glBindTexture(GL_TEXTURE_2D, name); // TODO: tilemap using glTexSubImage3D, glTexSubImage2D, GL_TEXTURE_2D_ARRAY?
}
bool Texture::set(const GLfloat* buf, GLsizei ww, GLsizei hh, bool mipmap) {
glActiveTexture(tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ww, hh, 0, GL_RGB, GL_FLOAT, buf);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (mipmap) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
w = ww;
h = hh;
return true;
}
bool Texture::set(const unsigned char* buf, GLsizei ww, GLsizei hh) {
glActiveTexture(tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, ww, hh, 0, GL_RED, GL_UNSIGNED_BYTE, buf);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
w = ww;
h = hh;
return true;
}
Texture::~Texture() {
free((void*)file);
if (--refcount == 0) {
glDeleteTextures(sizeof(textures)/sizeof(textures[0]), textures);
tex_no = 0;
}
}
Texture* Texture::load(const char* fn) {
Ppm* im = Ppm::getInst(fn);
if (!im) {
return NULL;
}
Texture* tx = new Texture(im->name);
if (!tx->set(im->buf, im->w, im->h, false)) { // TODO: can enable mipmaps again due to aliasing?
delete tx;
delete im;
return NULL;
}
delete im;
return tx;
}
bool Texture::load(const char* dir, std::vector<Texture*>& vec) {
DIR* dirp = opendir(dir);
if (!dirp) {
LOG_ERRNO("opendir(%s)", dir);
return false;
}
char fn[PATH_MAX+1];
strcpy(fn, dir);
char* fnp = fn+strlen(fn);
*fnp = '/';
fnp++;
struct dirent* dp;
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_type != DT_REG) continue;
strcpy(fnp, dp->d_name);
Texture* tx = load(fn);
if (tx) {
vec.push_back(tx);
}
}
closedir(dirp);
return true;
}