using a texture for the grid is better
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Mon, 25 Jul 2016 04:01:13 +0000 (07:01 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Mon, 25 Jul 2016 04:01:13 +0000 (07:01 +0300)
12 files changed:
Makefile
sdr/grid.p.glsl
sdr/grid.v.glsl
src/app.cc
src/assman.c [new file with mode: 0644]
src/assman.h [new file with mode: 0644]
src/assman_desktop.c [new file with mode: 0644]
src/image.cc [new file with mode: 0644]
src/image.h [new file with mode: 0644]
src/opengl.h
src/texture.cc [new file with mode: 0644]
src/texture.h [new file with mode: 0644]

index dcb3c86..998cd34 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,8 +13,8 @@ bin = vrfileman
 warn = -pedantic -Wall
 
 CFLAGS = $(warn) $(opt) $(dbg) $(inc) `pkg-config --cflags sdl2`
-CXXFLAGS = $(warn) $(opt) $(dbg) $(inc) `pkg-config --cflags sdl2`
-LDFLAGS = $(libgl) -lgmath `pkg-config --libs sdl2`
+CXXFLAGS = -std=c++11 $(warn) $(opt) $(dbg) $(inc) `pkg-config --cflags sdl2`
+LDFLAGS = $(libgl) -lgmath -limago `pkg-config --libs sdl2`
 
 
 ifeq ($(shell uname -s), Darwin)
index 86a30a5..825cdc6 100644 (file)
@@ -1,23 +1,14 @@
-varying vec3 pos, vpos;
+uniform sampler2D tex;
 
-float grid(vec2 p, float duty)
-{
-       float w = duty * 0.5;
-       p = fract(p);
-       return smoothstep(1.0 - w, 1.0, p.x) + (1.0 - smoothstep(0.0, w, p.x)) +
-               smoothstep(1.0 - w, 1.0, p.y) + (1.0 - smoothstep(0.0, w, p.y));
-}
+varying vec3 vpos;
 
 void main()
 {
-       vec3 p = pos * 500.0;
-
-       const vec3 grid_color = vec3(1.0, 0.2, 0.8);
        const vec3 bg_color = vec3(0.5, 0.1, 1.0);
-       vec3 color = grid_color * grid(p.xz, 0.2);
+       vec3 grid_color = texture2D(tex, gl_TexCoord[0].st).xyz;
 
        float fog = min(abs(vpos.z) * 0.05, 1.0);
 
-       gl_FragColor.xyz = mix(color, bg_color, fog);
+       gl_FragColor.xyz = mix(grid_color, bg_color, fog);
        gl_FragColor.a = 1.0;
 }
index 9f102eb..d6bda6e 100644 (file)
@@ -1,8 +1,8 @@
-varying vec3 pos, vpos;
+varying vec3 vpos;
 
 void main()
 {
        gl_Position = ftransform();
-       pos = gl_Vertex.xyz;
        vpos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+       gl_TexCoord[0].xy = gl_Vertex.xz * 500.0;
 }
index 1de3f89..f99e034 100644 (file)
@@ -6,6 +6,7 @@
 #include "mesh.h"
 #include "meshgen.h"
 #include "sdr.h"
+#include "texture.h"
 
 int win_width, win_height;
 float win_aspect;
@@ -13,6 +14,7 @@ long time_msec;
 
 static float cam_theta, cam_phi;
 static Mesh *mesh_torus;
+static Texture *tex_grid;
 
 static bool bnstate[16];
 static int prev_x, prev_y;
@@ -44,6 +46,10 @@ bool app_init(int argc, char **argv)
        if(!(sdr_grid = create_program_load("sdr/grid.v.glsl", "sdr/grid.p.glsl"))) {
                return false;
        }
+       if(!(tex_grid = load_texture("data/purple_grid.png"))) {
+               delete tex_grid;
+               return false;
+       }
 
        return true;
 }
@@ -71,6 +77,8 @@ void app_draw()
        glMultMatrixf(xform[0]);
 
        bind_program(sdr_grid);
+       bind_texture(tex_grid);
+
        glBegin(GL_QUADS);
        glNormal3f(0, 1, 0);
        glVertex3f(-1, 0, 1);
@@ -78,6 +86,8 @@ void app_draw()
        glVertex3f(1, 0, -1);
        glVertex3f(-1, 0, -1);
        glEnd();
+
+       bind_texture(0);
        bind_program(0);
 
        glPopMatrix();
diff --git a/src/assman.c b/src/assman.c
new file mode 100644 (file)
index 0000000..423c8de
--- /dev/null
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include "assman.h"
+
+int ass_fgetc(ass_file *fp)
+{
+       unsigned char c;
+
+       if(ass_fread(&c, 1, 1, fp) < 1) {
+               return -1;
+       }
+       return (int)c;
+}
+
+char *ass_fgets(char *s, int size, ass_file *fp)
+{
+       int i, c;
+       char *ptr = s;
+
+       if(!size) return 0;
+
+       for(i=0; i<size - 1; i++) {
+               if((c = ass_fgetc(fp)) == -1) {
+                       break;
+               }
+               *ptr++ = c;
+
+               if(c == '\n') break;
+       }
+       *ptr = 0;
+       return ptr == s ? 0 : s;
+}
diff --git a/src/assman.h b/src/assman.h
new file mode 100644 (file)
index 0000000..6d91c3b
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef ASSMAN_H_
+#define ASSMAN_H_
+
+#include <stdlib.h>
+
+typedef void ass_file;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ass_file *ass_fopen(const char *fname, const char *mode);
+void ass_fclose(ass_file *fp);
+long ass_fseek(ass_file *fp, long offs, int whence);
+long ass_ftell(ass_file *fp);
+int ass_feof(ass_file *fp);
+
+size_t ass_fread(void *buf, size_t size, size_t count, ass_file *fp);
+int ass_ungetc(int c, ass_file *fp);
+
+/* convenience functions, derived from the above */
+int ass_fgetc(ass_file *fp);
+char *ass_fgets(char *s, int size, ass_file *fp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ASSMAN_H_ */
diff --git a/src/assman_desktop.c b/src/assman_desktop.c
new file mode 100644 (file)
index 0000000..33e1eba
--- /dev/null
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include "assman.h"
+
+ass_file *ass_fopen(const char *fname, const char *mode)
+{
+       return (ass_file*)fopen(fname, mode);
+}
+
+void ass_fclose(ass_file *fp)
+{
+       fclose((FILE*)fp);
+}
+
+long ass_fseek(ass_file *fp, long offs, int whence)
+{
+       fseek((FILE*)fp, offs, whence);
+       return ftell((FILE*)fp);
+}
+
+long ass_ftell(ass_file *fp)
+{
+       return ftell((FILE*)fp);
+}
+
+int ass_feof(ass_file *fp)
+{
+       return feof((FILE*)fp);
+}
+
+size_t ass_fread(void *buf, size_t size, size_t count, ass_file *fp)
+{
+       return fread(buf, size, count, (FILE*)fp);
+}
+
+int ass_ungetc(int c, ass_file *fp)
+{
+       return ungetc(c, (FILE*)fp);
+}
diff --git a/src/image.cc b/src/image.cc
new file mode 100644 (file)
index 0000000..6d6e4b2
--- /dev/null
@@ -0,0 +1,251 @@
+#include <string.h>
+#include "assman.h"
+#include "imago2.h"
+#include "image.h"
+
+
+static int pixel_elements(Image::Format fmt);
+static int elem_size(Image::Format fmt);
+static int pixel_size(Image::Format fmt);
+
+Image::Image()
+{
+       fmt = FMT_RGBA;
+       width = height = 0;
+       pixels = 0;
+}
+
+Image::~Image()
+{
+       delete [] (char*)pixels;
+}
+
+Image::Image(const Image &img)
+{
+       pixels = 0;
+       set_pixels(img.width, img.height, img.pixels, img.fmt);
+}
+
+Image &Image::operator =(const Image &img)
+{
+       if(&img != this) {
+               delete [] (char*)pixels;
+               pixels = 0;
+               set_pixels(img.width, img.height, img.pixels, img.fmt);
+       }
+       return *this;
+}
+
+Image::Image(Image &&img)
+{
+       width = img.width;
+       height = img.height;
+       pixels = img.pixels;
+       fmt = img.fmt;
+
+       img.pixels = 0;
+}
+
+Image &Image::operator =(Image &&img)
+{
+       if(&img != this) {
+               width = img.width;
+               height = img.height;
+               pixels = img.pixels;
+               fmt = img.fmt;
+
+               img.pixels = 0;
+       }
+       return *this;
+}
+
+int Image::get_width() const
+{
+       return width;
+}
+
+int Image::get_height() const
+{
+       return height;
+}
+
+Image::Format Image::get_format() const
+{
+       return fmt;
+}
+
+bool Image::create(int x, int y, Format fmt)
+{
+       width = x;
+       height = y;
+       this->fmt = fmt;
+
+       try {
+               pixels = new char[x * y * pixel_size(fmt)];
+       }
+       catch(...) {
+               return false;
+       }
+       return true;
+}
+
+bool Image::set_pixels(int x, int y, void *pixels, Format fmt)
+{
+       if(!create(x, y, fmt)) {
+               return false;
+       }
+       memcpy(this->pixels, pixels, x * y * pixel_size(fmt));
+       return true;
+}
+
+void *Image::get_pixels() const
+{
+       return pixels;
+}
+
+static size_t io_read(void *buf, size_t bytes, void *uptr)
+{
+       return ass_fread(buf, 1, bytes, uptr);
+}
+
+static long io_seek(long offs, int whence, void *uptr)
+{
+       return ass_fseek(uptr, offs, whence);
+}
+
+bool Image::load(const char *fname)
+{
+       struct img_pixmap pixmap;
+       struct img_io io;
+
+       if(!(io.uptr = ass_fopen(fname, "rb"))) {
+               fprintf(stderr, "failed to open image: %s\n", fname);
+               return false;
+       }
+       io.read = io_read;
+       io.write = 0;
+       io.seek = io_seek;
+
+       img_init(&pixmap);
+       if(img_read(&pixmap, &io) == -1) {
+               return false;
+       }
+       ass_fclose(io.uptr);
+
+       Format fmt;
+       switch(pixmap.fmt) {
+       case IMG_FMT_GREY8:
+               fmt = FMT_GREY;
+               break;
+       case IMG_FMT_RGB24:
+               fmt = FMT_RGB;
+               break;
+       case IMG_FMT_RGBA32:
+               fmt = FMT_RGBA;
+               break;
+       case IMG_FMT_GREYF:
+               fmt = FMT_GREY_FLOAT;
+               break;
+       case IMG_FMT_RGBF:
+               fmt = FMT_RGB_FLOAT;
+               break;
+       case IMG_FMT_RGBAF:
+               fmt = FMT_RGBA_FLOAT;
+               break;
+       default:
+               img_destroy(&pixmap);
+               return false;
+       }
+
+       if(!set_pixels(pixmap.width, pixmap.height, pixmap.pixels, fmt)) {
+               img_destroy(&pixmap);
+               return false;
+       }
+       img_destroy(&pixmap);
+       return true;
+}
+
+bool Image::save(const char *fname) const
+{
+       struct img_pixmap pixmap;
+
+       img_init(&pixmap);
+
+       switch(fmt) {
+       case FMT_GREY:
+               pixmap.fmt = IMG_FMT_GREY8;
+               break;
+       case FMT_GREY_FLOAT:
+               pixmap.fmt = IMG_FMT_GREYF;
+               break;
+       case FMT_RGB:
+               pixmap.fmt = IMG_FMT_RGB24;
+               break;
+       case FMT_RGB_FLOAT:
+               pixmap.fmt = IMG_FMT_RGBF;
+               break;
+       case FMT_RGBA:
+               pixmap.fmt = IMG_FMT_RGBA32;
+               break;
+       case FMT_RGBA_FLOAT:
+               pixmap.fmt = IMG_FMT_RGBAF;
+               break;
+       default:
+               return false;
+       }
+
+       pixmap.width = width;
+       pixmap.height = height;
+       pixmap.pixels = pixels;
+       pixmap.pixelsz = pixel_size(fmt);
+
+       if(img_save(&pixmap, fname) == -1) {
+               return false;
+       }
+       return true;
+}
+
+static int pixel_elements(Image::Format fmt)
+{
+       switch(fmt) {
+       case Image::FMT_GREY:
+       case Image::FMT_GREY_FLOAT:
+               return 1;
+
+       case Image::FMT_RGB:
+       case Image::FMT_RGB_FLOAT:
+               return 3;
+
+       case Image::FMT_RGBA:
+       case Image::FMT_RGBA_FLOAT:
+               return 4;
+
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int elem_size(Image::Format fmt)
+{
+       switch(fmt) {
+       case Image::FMT_GREY:
+       case Image::FMT_RGB:
+       case Image::FMT_RGBA:
+               return 1;
+
+       case Image::FMT_GREY_FLOAT:
+       case Image::FMT_RGB_FLOAT:
+       case Image::FMT_RGBA_FLOAT:
+               return sizeof(float);
+
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int pixel_size(Image::Format fmt)
+{
+       return elem_size(fmt) * pixel_elements(fmt);
+}
diff --git a/src/image.h b/src/image.h
new file mode 100644 (file)
index 0000000..0ddbd12
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef IMAGE_H_
+#define IMAGE_H_
+
+
+class Image {
+public:
+       enum Format {
+               FMT_GREY,
+               FMT_RGB,
+               FMT_RGBA,
+               FMT_GREY_FLOAT,
+               FMT_RGB_FLOAT,
+               FMT_RGBA_FLOAT
+       };
+
+private:
+       Format fmt;
+       int width, height;
+       void *pixels;
+
+public:
+       Image();
+       ~Image();
+
+       Image(const Image &img);
+       Image &operator =(const Image &img);
+
+       Image(Image &&img);
+       Image &operator =(Image &&img);
+
+       int get_width() const;
+       int get_height() const;
+
+       Format get_format() const;
+
+       bool create(int x, int y, Format fmt = FMT_RGBA);
+       bool set_pixels(int x, int y, void *pixels, Format fmt = FMT_RGBA);
+       void *get_pixels() const;
+
+       bool load(const char *fname);
+       bool save(const char *fname) const;
+};
+
+#endif // IMAGE_H_
index 6d5387e..9acd561 100644 (file)
 
 #endif /* IPHONE */
 
+#ifndef GL_RGB16F
+#define GL_RGB16F      0x881b
+#endif
+#ifndef GL_RGBA16F
+#define GL_RGBA16F     0x881a
+#endif
+#ifndef GL_RGB32F
+#define GL_RGB32F      0x8815
+#endif
+#ifndef GL_RGBA32F
+#define GL_RGBA32F     0x8814
+#endif
+#ifndef GL_LUMINANCE16F
+#define GL_LUMINANCE16F        0x881e
+#endif
+#ifndef GL_LUMINANCE32F
+#define GL_LUMINANCE32F        0x8818
+#endif
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8    GL_DEPTH24_STENCIL8_OES
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/texture.cc b/src/texture.cc
new file mode 100644 (file)
index 0000000..95d34e3
--- /dev/null
@@ -0,0 +1,429 @@
+#include <stdlib.h>
+#include "texture.h"
+#include "image.h"
+#include "opengl.h"
+#include "imago2.h"
+
+#if defined(GL_ES_VERSION_2_0) || defined(GL_VERSION_3_0)
+#define USE_GL_GENERATE_MIPMAP
+#elif defined(__GLEW_H__)
+#define USE_SGIS_GENERATE_MIPMAP
+#endif
+
+static int glifmt_from_ifmt(unsigned int ifmt);
+static int glfmt_from_ifmt(unsigned int ifmt);
+static int gltype_from_ifmt(unsigned int ifmt);
+
+static int glifmt_from_imgfmt(Image::Format fmt);
+static Image::Format imgfmt_from_glifmt(unsigned int ifmt);
+
+static unsigned int cur_target[8] = {
+       GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
+       GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
+};
+
+void bind_texture(Texture *tex, int tunit)
+{
+       if(tex) {
+               tex->bind(tunit);
+       } else {
+               glActiveTexture(GL_TEXTURE0 + tunit);
+               glBindTexture(cur_target[tunit], 0);
+               glActiveTexture(GL_TEXTURE0);
+       }
+}
+
+Texture *load_texture(const char *fname)
+{
+       TextureCube *texcube = new TextureCube;
+       if(texcube->load(fname)) {
+               return texcube;
+       }
+       delete texcube;
+
+       Texture2D *tex = new Texture2D;
+       if(tex->load(fname)) {
+               return tex;
+       }
+       delete tex;
+       return 0;
+}
+
+
+Texture::Texture()
+{
+       target = 0;
+       sz[0] = sz[1] = sz[2] = 0;
+       texfmt = 0;
+
+       glGenTextures(1, &id);
+}
+
+Texture::~Texture()
+{
+       if(id) {
+               glDeleteTextures(1, &id);
+       }
+}
+
+void Texture::set_wrapping(unsigned int wrap)
+{
+       if(!target) {
+               return;
+       }
+
+       glBindTexture(target, id);
+       glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
+       glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
+}
+
+void Texture::set_filtering(unsigned int filt)
+{
+       unsigned int mag_filter;
+
+       if(!target) {
+               return;
+       }
+
+       switch(filt) {
+       case GL_LINEAR_MIPMAP_NEAREST:
+       case GL_LINEAR_MIPMAP_LINEAR:
+               mag_filter = GL_LINEAR;
+               break;
+
+       case GL_NEAREST_MIPMAP_NEAREST:
+       case GL_NEAREST_MIPMAP_LINEAR:
+               mag_filter = GL_NEAREST;
+               break;
+
+       default:
+               mag_filter = filt;
+       }
+
+       set_filtering(filt, mag_filter);
+}
+
+void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
+{
+       glBindTexture(target, id);
+       glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
+       glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
+}
+
+unsigned int Texture::get_format() const
+{
+       return texfmt;
+}
+
+int Texture::get_size(int dim) const
+{
+       if(dim < 0 || dim >= 3) {
+               return 0;
+       }
+       return sz[dim];
+}
+
+unsigned int Texture::get_id() const
+{
+       return id;
+}
+
+void Texture::bind(int tex_unit) const
+{
+       glActiveTexture(GL_TEXTURE0 + tex_unit);
+       glBindTexture(target, id);
+       glActiveTexture(GL_TEXTURE0);
+
+       cur_target[tex_unit] = target;
+}
+
+
+// ---- Texture2D ----
+
+Texture2D::Texture2D()
+{
+       target = GL_TEXTURE_2D;
+}
+
+TextureType Texture2D::get_type() const
+{
+       return TEXTURE_2D;
+}
+
+void Texture2D::create(int xsz, int ysz, unsigned int ifmt)
+{
+       int fmt = glfmt_from_ifmt(ifmt);
+       int type = gltype_from_ifmt(ifmt);
+
+       glBindTexture(GL_TEXTURE_2D, id);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
+       sz[0] = xsz;
+       sz[1] = ysz;
+       texfmt = ifmt;
+}
+
+void Texture2D::set_image(const Image &img, int idx)
+{
+       texfmt = glifmt_from_imgfmt(img.get_format());
+       unsigned int fmt = glfmt_from_ifmt(texfmt);
+       unsigned int type = gltype_from_ifmt(texfmt);
+
+       sz[0] = img.get_width();
+       sz[1] = img.get_height();
+
+       glBindTexture(GL_TEXTURE_2D, id);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+#ifdef USE_SGIS_GENERATE_MIPMAP
+       if(GLEW_SGIS_generate_mipmap) {
+               glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#endif
+               glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
+#ifdef USE_SGIS_GENERATE_MIPMAP
+       } else {
+               gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
+       }
+#endif
+
+#ifdef USE_GL_GENERATE_MIPMAP
+       glGenerateMipmap(GL_TEXTURE_2D);
+#endif
+}
+
+void Texture2D::get_image(Image *img, int idx) const
+{
+#ifndef GL_ES_VERSION_2_0
+       glBindTexture(GL_TEXTURE_2D, id);
+
+       Image::Format img_fmt = imgfmt_from_glifmt(texfmt);
+       img->create(sz[0], sz[1], img_fmt);
+
+       unsigned int fmt = glfmt_from_ifmt(texfmt);
+       unsigned int type = gltype_from_ifmt(texfmt);
+       glGetTexImage(GL_TEXTURE_2D, 0, fmt, type, img->get_pixels());
+#endif // GL_ES_VERSION_2_0
+}
+
+void Texture2D::set_subimage(const Image &img, int xoffs, int yoffs)
+{
+       unsigned int ifmt = glifmt_from_imgfmt(img.get_format());
+
+       glBindTexture(GL_TEXTURE_2D, id);
+       glTexSubImage2D(GL_TEXTURE_2D, 0, xoffs, yoffs, img.get_width(), img.get_height(),
+                       glfmt_from_ifmt(ifmt), gltype_from_ifmt(ifmt), img.get_pixels());
+}
+
+bool Texture2D::load(const char *fname)
+{
+       Image img;
+       if(!img.load(fname)) {
+               fprintf(stderr, "failed to load 2D texture: %s\n", fname);
+               return false;
+       }
+       set_image(img);
+
+       printf("loaded 2D texture: %s\n", fname);
+       return true;
+}
+
+bool Texture2D::save(const char *fname) const
+{
+#ifndef GL_ES_VERSION_2_0
+       unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4];
+
+       glBindTexture(GL_TEXTURE_2D, id);
+       glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+       if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) {
+               fprintf(stderr, "failed to save 2D texture: %s\n", fname);
+               delete [] pixels;
+               return false;
+       }
+
+       printf("saved 2D texture: %s\n", fname);
+       delete [] pixels;
+       return true;
+#else
+       return false;   // TODO
+#endif
+}
+
+// ---- TextureCube ----
+static unsigned int cube_faces[] = {
+       GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+       GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+       GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
+
+TextureCube::TextureCube()
+{
+       target = GL_TEXTURE_CUBE_MAP;
+}
+
+TextureType TextureCube::get_type() const
+{
+       return TEXTURE_CUBE;
+}
+
+
+void TextureCube::create(int xsz, int ysz, unsigned int ifmt)
+{
+       if(xsz != ysz) {
+               fprintf(stderr, "trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
+               return;
+       }
+
+       texfmt = ifmt;
+
+       glBindTexture(GL_TEXTURE_CUBE_MAP, id);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+       for(int i=0; i<6; i++) {
+               glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+       }
+}
+
+void TextureCube::set_image(const Image &img, int idx)
+{
+       // TODO
+}
+
+void TextureCube::get_image(Image *img, int idx) const
+{
+       // TODO
+}
+
+bool TextureCube::load(const char *fname)
+{
+       return false;   // TODO
+}
+
+bool TextureCube::save(const char *fname) const
+{
+       return false;   // TODO
+}
+
+static int glifmt_from_ifmt(unsigned int ifmt)
+{
+#ifdef GL_ES_VERSION_2_0
+       switch(ifmt) {
+       case GL_LUMINANCE16F:
+       case GL_LUMINANCE32F:
+               ifmt = GL_LUMINANCE;
+               break;
+
+       case GL_RGB16F:
+       case GL_RGB32F:
+               ifmt = GL_RGB;
+               break;
+
+       case GL_RGBA16F:
+       case GL_RGBA32F:
+               ifmt = GL_RGBA;
+               break;
+
+       default:
+               break;
+       }
+#endif
+       return ifmt;    // by default just pass it through...
+}
+
+static int glfmt_from_ifmt(unsigned int ifmt)
+{
+       switch(ifmt) {
+       case GL_LUMINANCE16F:
+       case GL_LUMINANCE32F:
+               return GL_LUMINANCE;
+
+       case GL_RGB16F:
+       case GL_RGB32F:
+               return GL_RGB;
+
+       case GL_RGBA16F:
+       case GL_RGBA32F:
+               return GL_RGBA;
+
+       default:
+               break;
+       }
+       return ifmt;
+}
+
+static int gltype_from_ifmt(unsigned int ifmt)
+{
+       switch(ifmt) {
+       case GL_RGB16F:
+       case GL_RGBA16F:
+       case GL_LUMINANCE16F:
+#ifdef GL_ES_VERSION_2_0
+               return GL_HALF_FLOAT_OES;
+#endif
+       case GL_RGB32F:
+       case GL_RGBA32F:
+       case GL_LUMINANCE32F:
+               return GL_FLOAT;
+
+       default:
+               break;
+       }
+       return GL_UNSIGNED_BYTE;
+}
+
+static int glifmt_from_imgfmt(Image::Format fmt)
+{
+       switch(fmt) {
+       case Image::FMT_GREY:
+               return GL_LUMINANCE;
+       case Image::FMT_GREY_FLOAT:
+               return GL_LUMINANCE16F;
+       case Image::FMT_RGB:
+               return GL_RGB;
+       case Image::FMT_RGB_FLOAT:
+               return GL_RGB16F;
+       case Image::FMT_RGBA:
+               return GL_RGBA;
+       case Image::FMT_RGBA_FLOAT:
+               return GL_RGBA16F;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static Image::Format imgfmt_from_glifmt(unsigned int ifmt)
+{
+       switch(ifmt) {
+       case GL_LUMINANCE:
+               return Image::FMT_GREY;
+       case GL_LUMINANCE16F:
+               return Image::FMT_GREY_FLOAT;
+       case GL_RGB:
+               return Image::FMT_RGB;
+       case GL_RGB16F:
+               return Image::FMT_RGB_FLOAT;
+       case GL_RGBA:
+               return Image::FMT_RGBA;
+       case GL_RGBA16F:
+               return Image::FMT_RGBA_FLOAT;
+       default:
+               break;
+       }
+
+       fprintf(stderr, "imgfmt_from_glifmt: unknown internal format: %x\n", ifmt);
+#ifndef NDEBUG
+       abort();
+#endif
+       return Image::FMT_RGBA; // ... whatever
+}
diff --git a/src/texture.h b/src/texture.h
new file mode 100644 (file)
index 0000000..e39efe0
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef TEXTURE_H_
+#define TEXTURE_H_
+
+#include "opengl.h"
+
+class Image;
+
+enum TextureType {
+       TEXTURE_2D,
+       TEXTURE_CUBE
+};
+
+class Texture {
+protected:
+       unsigned int id;
+       unsigned int target;
+       unsigned int texfmt;
+       int sz[3];
+
+       Texture(const Texture &tex) {}
+       Texture &operator =(const Texture &tex) {
+               return *this;
+       }
+
+public:
+       Texture();
+       virtual ~Texture();
+
+       virtual TextureType get_type() const = 0;
+
+       void set_wrapping(unsigned int wrap);
+       void set_filtering(unsigned int filt);
+       void set_filtering(unsigned int min_filt, unsigned int mag_filt);
+
+       unsigned int get_format() const;
+
+       virtual int get_size(int dim) const;
+
+       virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA) = 0;
+       virtual void set_image(const Image &img, int idx = 0) = 0;
+       virtual void get_image(Image *img, int idx = 0) const = 0;
+
+       virtual bool load(const char *fname) = 0;
+       virtual bool save(const char *fname) const = 0;
+
+       virtual unsigned int get_id() const;
+
+       virtual void bind(int tex_unit = 0) const;
+};
+
+class Texture2D : public Texture {
+public:
+       Texture2D();
+
+       virtual TextureType get_type() const;
+
+       virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA);
+       virtual void set_image(const Image &img, int idx = 0);
+       virtual void get_image(Image *img, int idx = 0) const;
+
+       virtual void set_subimage(const Image &img, int xoffs, int yoffs);
+
+       virtual bool load(const char *fname);
+       virtual bool save(const char *fname) const;
+};
+
+class TextureCube : public Texture {
+public:
+       TextureCube();
+
+       virtual TextureType get_type() const;
+
+       virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA);
+       virtual void set_image(const Image &img, int idx = 0);
+       virtual void get_image(Image *img, int idx = 0) const;
+
+       virtual bool load(const char *fname);
+       virtual bool save(const char *fname) const;
+};
+
+void bind_texture(Texture *tex, int tunit = 0);
+
+/** loads a texture autodetecting whether it's a 2D texture or
+ * cubemap and creating the correct Texture subclass instance.
+ */
+Texture *load_texture(const char *fname);
+
+#endif // TEXTURE_H_