added stb, more binaryout changes"
[henge/apc.git] / stb / tests / caveview / stb_gl.h
diff --git a/stb/tests/caveview/stb_gl.h b/stb/tests/caveview/stb_gl.h
new file mode 100644 (file)
index 0000000..6498e28
--- /dev/null
@@ -0,0 +1,1103 @@
+// stbgl - v0.04 - Sean Barrett 2008 - public domain
+//
+// Note that the gl extensions support requires glext.h. In fact, it works
+// if you just concatenate glext.h onto the end of this file. In that case,
+// this file is covered by the SGI FreeB license, and is not public domain.
+//
+// Extension usage:
+//
+//    1. Make a file called something like "extlist.txt" which contains stuff like:
+//         GLE(ShaderSourceARB,SHADERSOURCEARB)
+//         GLE(Uniform1iARB,UNIFORM1IARB)
+//         GLARB(ActiveTexture,ACTIVETEXTURE)   // same as GLE(ActiveTextureARB,ACTIVETEXTUREARB)
+//         GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE)
+//         GLE(MultiTexCoord2f,MULTITEXCOORD2F)
+//
+//    2. To declare functions (to make a header file), do this:
+//         #define STB_GLEXT_DECLARE "extlist.txt"
+//         #include "stb_gl.h"
+//
+//       A good way to do this is to define STB_GLEXT_DECLARE project-wide.
+//
+//    3. To define functions (implement), do this in some C file:
+//         #define STB_GLEXT_DEFINE "extlist.txt"
+//         #include "stb_gl.h"
+//
+//       If you've already defined STB_GLEXT_DECLARE, you can just do:
+//         #define STB_GLEXT_DEFINE_DECLARE
+//         #include "stb_gl.h"
+//
+//    4. Now you need to initialize:
+//
+//         stbgl_initExtensions();
+
+
+#ifndef INCLUDE_STB_GL_H
+#define INCLUDE_STB_GL_H
+
+#define STB_GL
+
+#ifdef _WIN32
+#ifndef WINGDIAPI
+#define CALLBACK    __stdcall
+#define WINGDIAPI   __declspec(dllimport)
+#define APIENTRY    __stdcall
+#endif
+#endif //_WIN32
+
+#include <stddef.h>
+
+#include <gl/gl.h>
+#include <gl/glu.h>
+
+#ifndef M_PI
+#define M_PI  3.14159265358979323846f
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// like gluPerspective, but:
+//    fov is chosen to satisfy both hfov <= max_hfov & vfov <= max_vfov;
+//            set one to 179 or 0 to ignore it
+//    zoom is applied separately, so you can do linear zoom without
+//            mucking with trig with fov; 1 -> use exact fov
+//    'aspect' is inferred from the current viewport, and ignores the
+//            possibility of non-square pixels
+extern void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar);
+extern void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float max_hfov, float max_vfov, float znear, float zfar);
+extern void stbgl_initCamera_zup_facing_x(void);
+extern void stbgl_initCamera_zup_facing_y(void);
+extern void stbgl_positionCameraWithEulerAngles(float *loc, float *ang);
+extern void stbgl_drawRect(float x0, float y0, float x1, float y1);
+extern void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1);
+extern void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw);
+
+extern int stbgl_hasExtension(char *ext);
+extern void stbgl_SimpleLight(int index, float bright, float x, float y, float z);
+extern void stbgl_GlobalAmbient(float r, float g, float b);
+
+extern int stbgl_LoadTexture(char *filename, char *props); // only if stb_image is available
+
+extern int stbgl_TestTexture(int w);
+extern int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2);
+extern unsigned int stbgl_rand(void); // internal, but exposed just in case; LCG, so use middle bits
+
+extern int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props);
+extern int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data);
+// "props" is a series of characters (and blocks of characters), a la fopen()'s mode,
+// e.g.:
+//   GLuint texid = stbgl_LoadTexture("myfile.jpg", "mbc")
+//      means: load the image "myfile.jpg", and do the following:
+//                generate mipmaps
+//                use bilinear filtering (not trilinear)
+//                use clamp-to-edge on both channels
+//
+// input descriptor: AT MOST ONE
+//   TEXT     MEANING
+//    1         1 channel of input (intensity/alpha)
+//    2         2 channels of input (luminance, alpha)
+//    3         3 channels of input (RGB)
+//    4         4 channels of input (RGBA)
+//    l         1 channel of input (luminance)
+//    a         1 channel of input (alpha)
+//    la        2 channels of input (lum/alpha)
+//    rgb       3 channels of input (RGB)
+//    ycocg     3 channels of input (YCoCg - forces YCoCg output)
+//    ycocgj    4 channels of input (YCoCgJunk - forces YCoCg output)
+//    rgba      4 channels of input (RGBA)
+//    
+// output descriptor: AT MOST ONE
+//   TEXT     MEANING
+//    A         1 channel of output (alpha)
+//    I         1 channel of output (intensity)
+//    LA        2 channels of output (lum/alpha)
+//    RGB       3 channels of output (RGB)
+//    RGBA      4 channels of output (RGBA)
+//    DXT1      encode as a DXT1 texture (RGB unless input has RGBA)
+//    DXT3      encode as a DXT3 texture
+//    DXT5      encode as a DXT5 texture
+//    YCoCg     encode as a DXT5 texture with Y in alpha, CoCg in RG
+//    D         GL_DEPTH_COMPONENT
+//    NONE      no input/output, don't call TexImage2D at all
+//
+// when reading from a file or using another interface with an explicit
+// channel count, the input descriptor is ignored and instead the channel
+// count is used as the input descriptor. if the file read is a DXT DDS,
+// then it is passed directly to OpenGL in the file format.
+//
+// if an input descriptor is supplied but no output descriptor, the output
+// is assumed to be the same as the input. if an output descriptor is supplied
+// but no input descriptor, the input is assumed to be the same as the
+// output. if neither is supplied, the input is assumed to be 4-channel.
+// If DXT1 or YCoCG output is requested with no input, the input is assumed
+// to be 4-channel but the alpha channel is ignored.
+//
+// filtering descriptor (default is no mipmaps)
+//   TEXT     MEANING
+//    m         generate mipmaps
+//    M         mipmaps are provided, concatenated at end of data (from largest to smallest)
+//    t         use trilinear filtering (default if mipmapped)
+//    b         use bilinear filtering (default if not-mipmapped)
+//    n         use nearest-neighbor sampling
+//
+// wrapping descriptor
+//   TEXT     MEANING
+//    w         wrap (default)
+//    c         clamp-to-edge
+//    C         GL_CLAMP (uses border color)
+//
+// If only one wrapping descriptor is supplied, it is applied to both channels.
+//
+// special:
+//   TEXT     MEANING
+//    f         input data is floats (default unsigned bytes)
+//    F         input&output data is floats (default unsigned bytes)
+//    p         explicitly pre-multiply the alpha
+//    P         pad to power-of-two (default stretches)
+//    NP2       non-power-of-two
+//    +         can overwrite the texture data with temp data
+//    !         free the texture data with "free"
+//
+// the properties string can also include spaces
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#ifdef STB_GL_IMPLEMENTATION
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <memory.h>
+
+int stbgl_hasExtension(char *ext)
+{
+   const char *s = glGetString(GL_EXTENSIONS);
+   for(;;) {
+      char *e = ext;
+      for (;;) {
+         if (*e == 0) {
+            if (*s == 0 || *s == ' ') return 1;
+            break;
+         }
+         if (*s != *e)
+            break;
+         ++s, ++e;
+      }
+      while (*s && *s != ' ') ++s;
+      if (!*s) return 0;
+      ++s; // skip space
+   }
+}
+
+void stbgl_drawRect(float x0, float y0, float x1, float y1)
+{
+   glBegin(GL_POLYGON);
+      glTexCoord2f(0,0); glVertex2f(x0,y0);
+      glTexCoord2f(1,0); glVertex2f(x1,y0);
+      glTexCoord2f(1,1); glVertex2f(x1,y1);
+      glTexCoord2f(0,1); glVertex2f(x0,y1);
+   glEnd();
+}
+
+void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1)
+{
+   glBegin(GL_POLYGON);
+      glTexCoord2f(s0,t0); glVertex2f(x0,y0);
+      glTexCoord2f(s1,t0); glVertex2f(x1,y0);
+      glTexCoord2f(s1,t1); glVertex2f(x1,y1);
+      glTexCoord2f(s0,t1); glVertex2f(x0,y1);
+   glEnd();
+}
+
+void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw)
+{
+   float x0,y0,z0,x1,y1,z1;
+   sx /=2, sy/=2, sz/=2;
+   x0 = x-sx; y0 = y-sy; z0 = z-sz;
+   x1 = x+sx; y1 = y+sy; z1 = z+sz;
+
+   glBegin(GL_QUADS);
+      if (cw) {
+         glNormal3f(0,0,-1);
+         glTexCoord2f(0,0); glVertex3f(x0,y0,z0);
+         glTexCoord2f(1,0); glVertex3f(x1,y0,z0);
+         glTexCoord2f(1,1); glVertex3f(x1,y1,z0);
+         glTexCoord2f(0,1); glVertex3f(x0,y1,z0);
+
+         glNormal3f(0,0,1);
+         glTexCoord2f(0,0); glVertex3f(x1,y0,z1);
+         glTexCoord2f(1,0); glVertex3f(x0,y0,z1);
+         glTexCoord2f(1,1); glVertex3f(x0,y1,z1);
+         glTexCoord2f(0,1); glVertex3f(x1,y1,z1);
+
+         glNormal3f(-1,0,0);
+         glTexCoord2f(0,0); glVertex3f(x0,y1,z1);
+         glTexCoord2f(1,0); glVertex3f(x0,y0,z1);
+         glTexCoord2f(1,1); glVertex3f(x0,y0,z0);
+         glTexCoord2f(0,1); glVertex3f(x0,y1,z0);
+
+         glNormal3f(1,0,0);
+         glTexCoord2f(0,0); glVertex3f(x1,y0,z1);
+         glTexCoord2f(1,0); glVertex3f(x1,y1,z1);
+         glTexCoord2f(1,1); glVertex3f(x1,y1,z0);
+         glTexCoord2f(0,1); glVertex3f(x1,y0,z0);
+
+         glNormal3f(0,-1,0);
+         glTexCoord2f(0,0); glVertex3f(x0,y0,z1);
+         glTexCoord2f(1,0); glVertex3f(x1,y0,z1);
+         glTexCoord2f(1,1); glVertex3f(x1,y0,z0);
+         glTexCoord2f(0,1); glVertex3f(x0,y0,z0);
+
+         glNormal3f(0,1,0);
+         glTexCoord2f(0,0); glVertex3f(x1,y1,z1);
+         glTexCoord2f(1,0); glVertex3f(x0,y1,z1);
+         glTexCoord2f(1,1); glVertex3f(x0,y1,z0);
+         glTexCoord2f(0,1); glVertex3f(x1,y1,z0);
+      } else {
+         glNormal3f(0,0,-1);
+         glTexCoord2f(0,0); glVertex3f(x0,y0,z0);
+         glTexCoord2f(0,1); glVertex3f(x0,y1,z0);
+         glTexCoord2f(1,1); glVertex3f(x1,y1,z0);
+         glTexCoord2f(1,0); glVertex3f(x1,y0,z0);
+
+         glNormal3f(0,0,1);
+         glTexCoord2f(0,0); glVertex3f(x1,y0,z1);
+         glTexCoord2f(0,1); glVertex3f(x1,y1,z1);
+         glTexCoord2f(1,1); glVertex3f(x0,y1,z1);
+         glTexCoord2f(1,0); glVertex3f(x0,y0,z1);
+
+         glNormal3f(-1,0,0);
+         glTexCoord2f(0,0); glVertex3f(x0,y1,z1);
+         glTexCoord2f(0,1); glVertex3f(x0,y1,z0);
+         glTexCoord2f(1,1); glVertex3f(x0,y0,z0);
+         glTexCoord2f(1,0); glVertex3f(x0,y0,z1);
+
+         glNormal3f(1,0,0);
+         glTexCoord2f(0,0); glVertex3f(x1,y0,z1);
+         glTexCoord2f(0,1); glVertex3f(x1,y0,z0);
+         glTexCoord2f(1,1); glVertex3f(x1,y1,z0);
+         glTexCoord2f(1,0); glVertex3f(x1,y1,z1);
+
+         glNormal3f(0,-1,0);
+         glTexCoord2f(0,0); glVertex3f(x0,y0,z1);
+         glTexCoord2f(0,1); glVertex3f(x0,y0,z0);
+         glTexCoord2f(1,1); glVertex3f(x1,y0,z0);
+         glTexCoord2f(1,0); glVertex3f(x1,y0,z1);
+
+         glNormal3f(0,1,0);
+         glTexCoord2f(0,0); glVertex3f(x1,y1,z1);
+         glTexCoord2f(0,1); glVertex3f(x1,y1,z0);
+         glTexCoord2f(1,1); glVertex3f(x0,y1,z0);
+         glTexCoord2f(1,0); glVertex3f(x0,y1,z1);
+      }
+   glEnd();
+}
+
+void stbgl_SimpleLight(int index, float bright, float x, float y, float z)
+{
+   float d = (float) (1.0f/sqrt(x*x+y*y+z*z));
+   float dir[4] = { x*d,y*d,z*d,0 }, zero[4] = { 0,0,0,0 };
+   float c[4] = { bright,bright,bright,0 };
+   GLuint light = GL_LIGHT0 + index;
+   glLightfv(light, GL_POSITION, dir);
+   glLightfv(light, GL_DIFFUSE, c);
+   glLightfv(light, GL_AMBIENT, zero);
+   glLightfv(light, GL_SPECULAR, zero);
+   glEnable(light);
+   glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
+   glEnable(GL_COLOR_MATERIAL);
+}
+
+void stbgl_GlobalAmbient(float r, float g, float b)
+{
+   float v[4] = { r,g,b,0 };
+   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, v);
+}
+
+
+#define stbgl_rad2deg(r)  ((r)*180.0f / M_PI)
+#define stbgl_deg2rad(r)  ((r)/180.0f * M_PI)
+
+void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar)
+{
+   float unit_width, unit_height, aspect, vfov;
+   int data[4],w,h;
+   glGetIntegerv(GL_VIEWPORT, data);
+   w = data[2];
+   h = data[3];
+   aspect = (float) w / h;
+
+   if (max_hfov <= 0) max_hfov = 179;
+   if (max_vfov <= 0) max_vfov = 179;
+
+   // convert max_hfov, max_vfov to worldspace width at depth=1
+   unit_width  = (float) tan(stbgl_deg2rad(max_hfov/2)) * 2;
+   unit_height = (float) tan(stbgl_deg2rad(max_vfov/2)) * 2;
+   // check if hfov = max_hfov is enough to satisfy it
+   if (unit_width <= aspect * unit_height) {
+      float height = unit_width / aspect;
+      vfov = (float) atan((     height/2) / zoom);
+   } else {
+      vfov = (float) atan((unit_height/2) / zoom);
+   }
+   vfov = (float) stbgl_rad2deg(vfov * 2);
+   gluPerspective(vfov, aspect, znear, zfar);
+}
+
+void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float min_hfov, float min_vfov, float znear, float zfar)
+{
+   if (znear <= 0.0001f) znear = 0.0001f;
+   glViewport(x,y,w,h);
+   glScissor(x,y,w,h);
+   glMatrixMode(GL_PROJECTION);
+   glLoadIdentity();
+   stbgl_Perspective(zoom, min_hfov, min_vfov, znear, zfar);
+   glMatrixMode(GL_MODELVIEW);
+}
+
+// point the camera along the positive X axis, Z-up
+void stbgl_initCamera_zup_facing_x(void)
+{
+   glRotatef(-90, 1,0,0);
+   glRotatef( 90, 0,0,1);
+}
+
+// point the camera along the positive Y axis, Z-up
+void stbgl_initCamera_zup_facing_y(void)
+{
+   glRotatef(-90, 1,0,0);
+}
+
+// setup a camera using Euler angles
+void stbgl_positionCameraWithEulerAngles(float *loc, float *ang)
+{
+   glRotatef(-ang[1], 0,1,0);
+   glRotatef(-ang[0], 1,0,0);
+   glRotatef(-ang[2], 0,0,1);
+   glTranslatef(-loc[0], -loc[1], -loc[2]);
+}
+
+static int stbgl_m(char *a, char *b)
+{
+   // skip first character
+   do { ++a,++b; } while (*b && *a == *b);
+   return *b == 0;
+}
+
+#ifdef STBI_VERSION
+#ifndef STBI_NO_STDIO
+int stbgl_LoadTexture(char *filename, char *props)
+{
+   // @TODO: handle DDS files directly
+   int res;
+   void *data;
+   int w,h,c;
+   #ifndef STBI_NO_HDR
+   if (stbi_is_hdr(filename)) {
+      data = stbi_loadf(filename, &w, &h, &c, 0);
+      if (!data) return 0;
+      res = stbgl_TexImage2D_Extra(0, w,h,data, -c, props, 0);
+      free(data);
+      return res;
+   }
+   #endif
+
+   data = stbi_load(filename, &w, &h, &c, 0);
+   if (!data) return 0;
+   res = stbgl_TexImage2D_Extra(0, w,h,data, c, props, 0);
+   free(data);
+   return res;
+}
+#endif
+#endif // STBI_VERSION
+
+int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props)
+{
+   return stbgl_TexImage2D_Extra(texid, w, h, data, 0, props,1);
+}
+
+int stbgl_TestTexture(int w)
+{
+   char scale_table[] = { 10,20,30,30,35,40,5,18,25,13,7,5,3,3,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 };
+   return stbgl_TestTextureEx(w, scale_table, 2, 140,130,200, 180,200,170);
+}
+
+unsigned int stbgl_rand(void)
+{
+   static unsigned int stbgl__rand_seed = 3248980923; // random typing
+   return stbgl__rand_seed = stbgl__rand_seed * 2147001325 + 715136305; // BCPL generator
+}
+
+// wish this could be smaller, since it's so frivolous
+int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2)
+{
+   int rt[2] = {r1,r2}, gt[2] = {g1,g2}, bt[2] = {b1,b2};
+   signed char modded[256];
+   int i,j, m = w-1, s,k,scale;
+   unsigned char *data = (unsigned char *) malloc(w*w*3);
+   assert((m & w) == 0);
+   data[0] = 128;
+   for (s=0; s < 16; ++s) if ((1 << s) == w) break;
+   assert(w == (1 << s));
+   // plasma fractal noise
+   for (k=s-1; k >= 0; --k) {
+      int step = 1 << k;
+      // interpolate from "parents"
+      for (j=0; j < w; j += step*2) {
+         for (i=0; i < w; i += step*2) {
+            int i1 = i+step, j1=j+step;
+            int i2 = (i+step*2)&m, j2 = (j+step*2)&m;
+            int p00 = data[(j*w+i )*3], p01 = data[(j2*w+i )*3];
+            int p10 = data[(j*w+i2)*3], p11 = data[(j2*w+i2)*3];
+            data[(j*w+i1)*3] = (p00+p10)>>1;
+            data[(j1*w+i)*3] = (p00+p01)>>1;
+            data[(j1*w+i1)*3]= (p00+p01+p10+p11)>>2;
+         }
+      }
+      scale = scale_table[s-k+1];
+      if (!scale) continue; // just interpolate down the remaining data
+      for (j=0,i=0; i < 256; i += 2, j == scale ? j=0 : ++j)
+         modded[i] = j, modded[i+1] = -j; // precompute i%scale (plus sign)
+      for (j=0; j < w; j += step)
+         for (i=0; i < w; i += step) {
+            int x = data[(j*w+i)*3] + modded[(stbgl_rand() >> 12) & 255];
+            data[(j*w+i)*3] = x < 0 ? 0 : x > 255 ? 255 : x;
+         }
+   }
+   for (j=0; j < w; ++j)
+      for (i=0; i < w; ++i) {
+         int check = ((i^j) & (1 << (s-checks_log2))) == 0;
+         int v = data[(j*w+i)*3] >> 2;
+         data[(j*w+i)*3+0] = rt[check]-v;
+         data[(j*w+i)*3+1] = gt[check]-v;
+         data[(j*w+i)*3+2] = bt[check]-v;
+      }
+   return stbgl_TexImage2D(0, w, w, data, "3m!"); // 3 channels, mipmap, free
+}
+
+#ifdef _WIN32
+#ifndef WINGDIAPI
+typedef int (__stdcall *stbgl__voidfunc)(void);
+__declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *);
+#endif
+#define STB__HAS_WGLPROC
+static void (__stdcall *stbgl__CompressedTexImage2DARB)(int target, int level,
+                                   int internalformat, int width,
+                                   int height, int border, 
+                                   int imageSize, void *data);
+static void stbgl__initCompTex(void)
+{
+   *((void **) &stbgl__CompressedTexImage2DARB) = (void *) wglGetProcAddress("glCompressedTexImage2DARB");
+}
+#else
+static void (*stbgl__CompressedTexImage2DARB)(int target, int level,
+                                   int internalformat, int width,
+                                   int height, int border, 
+                                   int imageSize, void *data);
+static void stbgl__initCompTex(void)
+{
+}
+#endif // _WIN32
+
+#define STBGL_COMPRESSED_RGB_S3TC_DXT1    0x83F0
+#define STBGL_COMPRESSED_RGBA_S3TC_DXT1   0x83F1
+#define STBGL_COMPRESSED_RGBA_S3TC_DXT3   0x83F2
+#define STBGL_COMPRESSED_RGBA_S3TC_DXT5   0x83F3
+
+#ifdef STB_COMPRESS_DXT_BLOCK
+static void stbgl__convert(uint8 *p, uint8 *q, int n, int input_desc, uint8 *end)
+{
+   int i;
+   switch (input_desc) {
+      case GL_RED:
+      case GL_LUMINANCE: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=255, q+=1; break;
+      case GL_ALPHA:     for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = 0, p[3] = q[0], q+=1; break;
+      case GL_LUMINANCE_ALPHA: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=q[1], q+=2; break;
+      case GL_RGB:       for (i=0; i < n; ++i,p+=4) p[0]=q[0],p[1]=q[1],p[2]=q[2],p[3]=255,q+=3; break;
+      case GL_RGBA:      memcpy(p, q, n*4); break;
+      case GL_INTENSITY: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = p[3] = q[0], q+=1; break;
+   }
+   assert(p <= end);
+}
+
+static void stbgl__compress(uint8 *p, uint8 *rgba, int w, int h, int output_desc, uint8 *end)
+{
+   int i,j,y,y2;
+   int alpha = (output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT5);
+   for (j=0; j < w; j += 4) {
+      int x=4;
+      for (i=0; i < h; i += 4) {
+         uint8 block[16*4];
+         if (i+3 >= w) x = w-i;
+         for (y=0; y < 4; ++y) {
+            if (j+y >= h) break;
+            memcpy(block+y*16, rgba + w*4*(j+y) + i*4, x*4);
+         }
+         if (x < 4) {
+            switch (x) {
+               case 0: assert(0);
+               case 1:
+                  for (y2=0; y2 < y; ++y2) {
+                     memcpy(block+y2*16+1*4, block+y2*16+0*4, 4);
+                     memcpy(block+y2*16+2*4, block+y2*16+0*4, 8);
+                  }
+                  break;
+               case 2:
+                  for (y2=0; y2 < y; ++y2)
+                     memcpy(block+y2*16+2*4, block+y2*16+0*4, 8);
+                  break;
+               case 3:
+                  for (y2=0; y2 < y; ++y2)
+                     memcpy(block+y2*16+3*4, block+y2*16+1*4, 4);
+                  break;
+            }
+         }
+         y2 = 0;
+         for(; y<4; ++y,++y2)
+            memcpy(block+y*16, block+y2*16, 4*4);
+         stb_compress_dxt_block(p, block, alpha, 10);
+         p += alpha ? 16 : 8;
+      }
+   }
+   assert(p <= end);
+}
+#endif // STB_COMPRESS_DXT_BLOCK
+
+// use the reserved temporary-use enumerant range, since no
+// OpenGL enumerants should fall in that range
+enum
+{
+   STBGL_UNDEFINED = 0x6000,
+   STBGL_YCOCG,
+   STBGL_YCOCGJ,
+   STBGL_GEN_MIPMAPS,
+   STBGL_MIPMAPS,
+   STBGL_NO_DOWNLOAD,
+};
+
+#define STBGL_CLAMP_TO_EDGE               0x812F
+#define STBGL_CLAMP_TO_BORDER             0x812D
+
+#define STBGL_DEPTH_COMPONENT16           0x81A5
+#define STBGL_DEPTH_COMPONENT24           0x81A6
+#define STBGL_DEPTH_COMPONENT32           0x81A7
+
+int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data)
+{
+   static int has_s3tc = -1; // haven't checked yet
+   int free_data = 0, is_compressed = 0;
+   int pad_to_power_of_two = 0, non_power_of_two = 0;
+   int premultiply_alpha = 0; // @TODO
+   int float_tex   = 0; // @TODO
+   int input_type  = GL_UNSIGNED_BYTE;
+   int input_desc  = STBGL_UNDEFINED;
+   int output_desc = STBGL_UNDEFINED;
+   int mipmaps     = STBGL_UNDEFINED;
+   int filter      = STBGL_UNDEFINED, mag_filter;
+   int wrap_s = STBGL_UNDEFINED, wrap_t = STBGL_UNDEFINED;
+
+   // parse out the properties
+   if (props == NULL) props = "";
+   while (*props) {
+      switch (*props) {
+         case '1' :  input_desc = GL_LUMINANCE; break;
+         case '2' :  input_desc = GL_LUMINANCE_ALPHA; break;
+         case '3' :  input_desc = GL_RGB; break;
+         case '4' :  input_desc = GL_RGBA; break;
+         case 'l' :  if (props[1] == 'a') { input_desc = GL_LUMINANCE_ALPHA; ++props; }
+                     else input_desc = GL_LUMINANCE;
+                     break;
+         case 'a' :  input_desc = GL_ALPHA; break;
+         case 'r' :  if (stbgl_m(props, "rgba")) { input_desc = GL_RGBA; props += 3; break; }
+                     if (stbgl_m(props, "rgb")) { input_desc = GL_RGB; props += 2; break; }
+                     input_desc = GL_RED;
+                     break;
+         case 'y' :  if (stbgl_m(props, "ycocg")) {
+                        if (props[5] == 'j') { props += 5; input_desc = STBGL_YCOCGJ; }
+                        else { props += 4; input_desc = STBGL_YCOCG; }
+                        break;
+                     }
+                     return 0;
+         case 'L' :  if (props[1] == 'A') { output_desc = GL_LUMINANCE_ALPHA; ++props; }
+                     else output_desc = GL_LUMINANCE;
+                     break;
+         case 'I' :  output_desc = GL_INTENSITY; break;
+         case 'A' :  output_desc = GL_ALPHA; break;
+         case 'R' :  if (stbgl_m(props, "RGBA")) { output_desc = GL_RGBA; props += 3; break; }
+                     if (stbgl_m(props, "RGB")) { output_desc = GL_RGB; props += 2; break; }
+                     output_desc = GL_RED;
+                     break;
+         case 'Y' :  if (stbgl_m(props, "YCoCg") || stbgl_m(props, "YCOCG")) {
+                        props += 4;
+                        output_desc = STBGL_YCOCG;
+                        break;
+                     }
+                     return 0;
+         case 'D' :  if (stbgl_m(props, "DXT")) {
+                        switch (props[3]) {
+                           case '1': output_desc = STBGL_COMPRESSED_RGB_S3TC_DXT1; break;
+                           case '3': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT3; break;
+                           case '5': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5; break;
+                           default: return 0;
+                        }
+                        props += 3;
+                     } else if (stbgl_m(props, "D16")) {
+                        output_desc = STBGL_DEPTH_COMPONENT16;
+                        input_desc  = GL_DEPTH_COMPONENT;
+                        props += 2;
+                     } else if (stbgl_m(props, "D24")) {
+                        output_desc = STBGL_DEPTH_COMPONENT24;
+                        input_desc  = GL_DEPTH_COMPONENT;
+                        props += 2;
+                     } else if (stbgl_m(props, "D32")) {
+                        output_desc = STBGL_DEPTH_COMPONENT32;
+                        input_desc  = GL_DEPTH_COMPONENT;
+                        props += 2;
+                     } else {
+                        output_desc = GL_DEPTH_COMPONENT;
+                        input_desc  = GL_DEPTH_COMPONENT;
+                     }
+                     break;
+         case 'N' :  if (stbgl_m(props, "NONE")) {
+                        props += 3;
+                        input_desc = STBGL_NO_DOWNLOAD;
+                        output_desc = STBGL_NO_DOWNLOAD;
+                        break;
+                     }
+                     if (stbgl_m(props, "NP2")) {
+                        non_power_of_two = 1;
+                        props += 2;
+                        break;
+                     }
+                     return 0;
+         case 'm' :  mipmaps = STBGL_GEN_MIPMAPS; break;
+         case 'M' :  mipmaps = STBGL_MIPMAPS; break;
+         case 't' :  filter = GL_LINEAR_MIPMAP_LINEAR; break;
+         case 'b' :  filter = GL_LINEAR; break;
+         case 'n' :  filter = GL_NEAREST; break;
+         case 'w' :  if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT; else wrap_t = GL_REPEAT; break;
+         case 'C' :  if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_BORDER; else wrap_t = STBGL_CLAMP_TO_BORDER; break;
+         case 'c' :  if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_EDGE; else wrap_t = STBGL_CLAMP_TO_EDGE; break;
+         case 'f' :  input_type = GL_FLOAT; break;
+         case 'F' :  input_type = GL_FLOAT; float_tex = 1; break;
+         case 'p' :  premultiply_alpha = 1; break;
+         case 'P' :  pad_to_power_of_two = 1; break;
+         case '+' :  preserve_data = 0; break;
+         case '!' :  preserve_data = 0; free_data = 1; break;
+         case ' ' :  break;
+         case '-' :  break;
+         default  :  if (free_data) free(data);
+                     return 0;
+      }
+      ++props;
+   }
+   
+   // override input_desc based on channel count
+   if (output_desc != STBGL_NO_DOWNLOAD) {
+      switch (abs(chan)) {
+         case 1: input_desc = GL_LUMINANCE; break;
+         case 2: input_desc = GL_LUMINANCE_ALPHA; break;
+         case 3: input_desc = GL_RGB; break;
+         case 4: input_desc = GL_RGBA; break;
+         case 0: break;
+         default: return 0;
+      }
+   }
+
+   // override input_desc based on channel info
+   if (chan > 0) { input_type = GL_UNSIGNED_BYTE; }
+   if (chan < 0) { input_type = GL_FLOAT; }
+
+   if (output_desc == GL_ALPHA) {
+      if (input_desc == GL_LUMINANCE)
+         input_desc = GL_ALPHA;
+      if (input_desc == GL_RGB) {
+         // force a presumably-mono image to alpha
+         // @TODO handle 'preserve_data' case?
+         if (data && !preserve_data && input_type == GL_UNSIGNED_BYTE) {
+            int i;
+            unsigned char *p = (unsigned char *) data, *q = p;
+            for (i=0; i < w*h; ++i) {
+               *q = (p[0] + 2*p[1] + p[2]) >> 2;
+               p += 3;
+               q += 1;
+            }
+            input_desc = GL_ALPHA;
+         }
+      }
+   }
+
+   // set undefined input/output based on the other
+   if (input_desc == STBGL_UNDEFINED && output_desc == STBGL_UNDEFINED) {
+      input_desc = output_desc = GL_RGBA;
+   } else if (output_desc == STBGL_UNDEFINED) {
+      switch (input_desc) {
+         case GL_LUMINANCE:
+         case GL_ALPHA:
+         case GL_LUMINANCE_ALPHA:
+         case GL_RGB:
+         case GL_RGBA:
+            output_desc = input_desc;
+            break;
+         case GL_RED:
+            output_desc = GL_INTENSITY;
+            break;
+         case STBGL_YCOCG:
+         case STBGL_YCOCGJ:
+            output_desc = STBGL_YCOCG;
+            break;
+         default: assert(0); return 0;
+      }
+   } else if (input_desc == STBGL_UNDEFINED) {
+      switch (output_desc) {
+         case GL_LUMINANCE:
+         case GL_ALPHA:
+         case GL_LUMINANCE_ALPHA:
+         case GL_RGB:
+         case GL_RGBA:
+            input_desc = output_desc;
+            break;
+         case GL_INTENSITY:
+            input_desc = GL_RED;
+            break;
+         case STBGL_YCOCG:
+         case STBGL_COMPRESSED_RGB_S3TC_DXT1:
+         case STBGL_COMPRESSED_RGBA_S3TC_DXT3:
+         case STBGL_COMPRESSED_RGBA_S3TC_DXT5:
+            input_desc = GL_RGBA;
+            break;
+      }
+   } else {
+      if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1) {
+         // if input has alpha, force output alpha
+         switch (input_desc) {
+            case GL_ALPHA:
+            case GL_LUMINANCE_ALPHA:
+            case GL_RGBA:
+               output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5;
+               break;
+         }
+      }
+   }
+
+   switch(input_desc) {
+      case GL_LUMINANCE:
+      case GL_RED:
+      case GL_ALPHA:
+         chan = 1;
+         break;
+      case GL_LUMINANCE_ALPHA:
+         chan = 2;
+         break;
+      case GL_RGB:
+         chan = 3;
+         break;
+      case GL_RGBA:
+         chan = 4;
+         break;
+   }
+
+   if (pad_to_power_of_two && ((w & (w-1)) || (h & (h-1)))) {
+      if (output_desc != STBGL_NO_DOWNLOAD && input_type == GL_UNSIGNED_BYTE && chan > 0) {
+         unsigned char *new_data;
+         int w2 = w, h2 = h, j;
+         while (w & (w-1))
+            w = (w | (w>>1))+1;
+         while (h & (h-1))
+            h = (h | (h>>1))+1;
+         new_data = malloc(w * h * chan);
+         for (j=0; j < h2; ++j) {
+            memcpy(new_data + j * w * chan, (char *) data+j*w2*chan, w2*chan);
+            memset(new_data + (j * w+w2) * chan, 0, (w-w2)*chan);
+         }
+         for (; j < h; ++j)
+            memset(new_data + j*w*chan, 0, w*chan);
+         if (free_data)
+            free(data);
+         data = new_data;
+         free_data = 1;
+      }
+   }
+
+   switch (output_desc) {
+      case STBGL_COMPRESSED_RGB_S3TC_DXT1:
+      case STBGL_COMPRESSED_RGBA_S3TC_DXT1:
+      case STBGL_COMPRESSED_RGBA_S3TC_DXT3:
+      case STBGL_COMPRESSED_RGBA_S3TC_DXT5:
+         is_compressed = 1;
+         if (has_s3tc == -1) {
+            has_s3tc = stbgl_hasExtension("GL_EXT_texture_compression_s3tc");
+            if (has_s3tc) stbgl__initCompTex();
+         }
+         if (!has_s3tc) {
+            is_compressed = 0;
+            if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1)
+               output_desc = GL_RGB;
+            else
+               output_desc = GL_RGBA;
+         }
+   }
+
+   if (output_desc == STBGL_YCOCG) {
+      assert(0);
+      output_desc = GL_RGB; // @TODO!
+      if (free_data) free(data);
+      return 0;
+   }
+
+   mag_filter = 0;
+   if (mipmaps != STBGL_UNDEFINED) {
+      switch (filter) {
+         case STBGL_UNDEFINED: filter = GL_LINEAR_MIPMAP_LINEAR; break;
+         case GL_NEAREST     : mag_filter = GL_NEAREST; filter = GL_LINEAR_MIPMAP_LINEAR; break;
+         case GL_LINEAR      : filter = GL_LINEAR_MIPMAP_NEAREST; break;
+      }
+   } else {
+      if (filter == STBGL_UNDEFINED)
+         filter = GL_LINEAR;
+   }
+
+   // update filtering
+   if (!mag_filter) {
+      if (filter == GL_NEAREST)
+         mag_filter = GL_NEAREST;
+      else
+         mag_filter = GL_LINEAR;
+   }
+
+   // update wrap/clamp
+   if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT;
+   if (wrap_t == STBGL_UNDEFINED) wrap_t = wrap_s;
+
+   // if no texture id, generate one
+   if (texid == 0) {
+      GLuint tex;
+      glGenTextures(1, &tex);
+      if (tex == 0) { if (free_data) free(data); return 0; }
+      texid = tex;
+   }
+
+   if (data == NULL && mipmaps == STBGL_GEN_MIPMAPS)
+      mipmaps = STBGL_MIPMAPS;
+
+   if (output_desc == STBGL_NO_DOWNLOAD)
+      mipmaps = STBGL_NO_DOWNLOAD;
+
+   glBindTexture(GL_TEXTURE_2D, texid);
+
+#ifdef STB_COMPRESS_DXT_BLOCK
+   if (!is_compressed || !stbgl__CompressedTexImage2DARB || output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT3 || data == NULL)
+#endif
+   {
+      switch (mipmaps) {
+         case STBGL_NO_DOWNLOAD:
+            break;
+
+         case STBGL_UNDEFINED:
+            // check if actually power-of-two
+            if (non_power_of_two || ((w & (w-1)) == 0 && (h & (h-1)) == 0))
+               glTexImage2D(GL_TEXTURE_2D, 0, output_desc, w, h, 0, input_desc, input_type, data);
+            else
+               gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data);
+               // not power of two, so use glu to resize (generates mipmaps needlessly)
+            break;
+
+         case STBGL_MIPMAPS: {
+            int level = 0;
+            int size = input_type == GL_FLOAT ? sizeof(float) : 1;
+            if (data == NULL) size = 0; // reuse same block of memory for all mipmaps
+            assert((w & (w-1)) == 0 && (h & (h-1)) == 0); // verify power-of-two
+            while (w > 1 && h > 1) {
+               glTexImage2D(GL_TEXTURE_2D, level, output_desc, w, h, 0, input_desc, input_type, data);
+               data = (void *) ((char *) data + w * h * size * chan);
+               if (w > 1) w >>= 1;
+               if (h > 1) h >>= 1;
+               ++level;
+            }
+            break;
+         }
+         case STBGL_GEN_MIPMAPS:
+            gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data);
+            break;
+
+         default:
+            assert(0);
+            if (free_data) free(data);
+            return 0;
+      }
+#ifdef STB_COMPRESS_DXT_BLOCK
+   } else {
+      uint8 *out, *rgba=0, *end_out, *end_rgba;
+      int level = 0, alpha = (output_desc != STBGL_COMPRESSED_RGB_S3TC_DXT1);
+      int size = input_type == GL_FLOAT ? sizeof(float) : 1;
+      int osize = alpha ? 16 : 8;
+      if (!free_data && mipmaps == STBGL_GEN_MIPMAPS) {
+         uint8 *temp = malloc(w*h*chan);
+         if (!temp) { if (free_data) free(data); return 0; }
+         memcpy(temp, data, w*h*chan);
+         if (free_data) free(data);
+         free_data = 1;
+         data = temp;
+      }
+      if (chan != 4 || size != 1) {
+         rgba = malloc(w*h*4);
+         if (!rgba) return 0;
+         end_rgba = rgba+w*h*4;
+      }
+      out = malloc((w+3)*(h+3)/16*osize); // enough storage for the s3tc data
+      if (!out) return 0;
+      end_out = out + ((w+3)*(h+3))/16*osize;
+
+      for(;;) {
+         if (chan != 4)
+            stbgl__convert(rgba, data, w*h, input_desc, end_rgba);
+         stbgl__compress(out, rgba ? rgba : data, w, h, output_desc, end_out);
+         stbgl__CompressedTexImage2DARB(GL_TEXTURE_2D, level, output_desc, w, h, 0, ((w+3)&~3)*((h+3)&~3)/16*osize, out);
+         //glTexImage2D(GL_TEXTURE_2D, level, alpha?GL_RGBA:GL_RGB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba ? rgba : data);
+
+         if (mipmaps == STBGL_UNDEFINED) break;
+         if (w <= 1 && h <= 1) break;
+         if (mipmaps == STBGL_MIPMAPS) data = (void *) ((char *) data + w * h * size * chan);
+         if (mipmaps == STBGL_GEN_MIPMAPS) {
+            int w2 = w>>1, h2=h>>1, i,j,k, s=w*chan;
+            uint8 *p = data, *q=data;
+            if (w == 1) {
+               for (j=0; j < h2; ++j) {
+                  for (k=0; k < chan; ++k)
+                     *p++ = (q[k] + q[s+k] + 1) >> 1;
+                  q += s*2;
+               }
+            } else if (h == 1) {
+               for (i=0; i < w2; ++i) {
+                  for (k=0; k < chan; ++k)
+                     *p++ = (q[k] + q[k+chan] + 1) >> 1;
+                  q += chan*2;
+               }
+            } else {
+               for (j=0; j < h2; ++j) {
+                  for (i=0; i < w2; ++i) {
+                     for (k=0; k < chan; ++k)
+                        *p++ = (q[k] + q[k+chan] + q[s+k] + q[s+k+chan] + 2) >> 2;
+                     q += chan*2;
+                  }
+                  q += s;
+               }
+            }
+         }
+         if (w > 1) w >>= 1;
+         if (h > 1) h >>= 1;
+         ++level;
+      }
+      if (out) free(out);
+      if (rgba) free(rgba);
+#endif // STB_COMPRESS_DXT_BLOCK
+   }
+
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+
+   if (free_data) free(data);
+   return texid;
+}
+
+#endif // STB_DEFINE
+#undef STB_EXTERN
+
+#endif //INCLUDE_STB_GL_H
+
+// Extension handling... must be outside the INCLUDE_ brackets
+
+#if defined(STB_GLEXT_DEFINE) || defined(STB_GLEXT_DECLARE)
+
+#ifndef STB_GLEXT_SKIP_DURING_RECURSION
+
+#ifndef GL_GLEXT_VERSION
+
+   // First check if glext.h is concatenated on the end of this file
+   // (if it's concatenated on the beginning, we'll have GL_GLEXT_VERSION)
+
+   #define  STB_GLEXT_SKIP_DURING_RECURSION
+   #include __FILE__
+   #undef   STB_GLEXT_SKIP_DURING_RECURSION
+
+   // now check if it's still undefined; if so, try going for it by name;
+   // if this errors, that's fine, since we can't compile without it
+
+   #ifndef GL_GLEXT_VERSION
+   #include "glext.h"
+   #endif
+#endif
+
+#define GLARB(a,b) GLE(a##ARB,b##ARB)
+#define GLEXT(a,b) GLE(a##EXT,b##EXT)
+#define GLNV(a,b)  GLE(a##NV ,b##NV)
+#define GLATI(a,b) GLE(a##ATI,b##ATI)
+#define GLCORE(a,b) GLE(a,b)
+
+#ifdef STB_GLEXT_DEFINE_DECLARE
+#define STB_GLEXT_DEFINE STB_GLEXT_DECLARE
+#endif
+
+#if defined(STB_GLEXT_DECLARE) && defined(STB_GLEXT_DEFINE)
+#undef STB_GLEXT_DECLARE
+#endif
+
+#if defined(STB_GLEXT_DECLARE) && !defined(STB_GLEXT_DEFINE)
+   #define GLE(a,b) extern PFNGL##b##PROC gl##a;
+
+   #ifdef __cplusplus
+   extern "C" {
+   #endif
+
+   extern void stbgl_initExtensions(void);
+
+   #include STB_GLEXT_DECLARE
+
+   #ifdef __cplusplus
+   };
+   #endif
+
+#else
+
+   #ifndef STB_GLEXT_DEFINE
+   #error "Header file is screwed up somehow"
+   #endif
+
+   #ifdef _WIN32
+   #ifndef WINGDIAPI
+   #ifndef STB__HAS_WGLPROC
+   typedef int (__stdcall *stbgl__voidfunc)(void);
+   __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *);
+   #endif
+   #endif
+   #define STBGL__GET_FUNC(x)   wglGetProcAddress(x)
+   #endif
+
+   #ifdef GLE
+   #undef GLE
+   #endif
+
+   #define GLE(a,b)  PFNGL##b##PROC gl##a;
+   #include STB_GLEXT_DEFINE
+
+   #undef GLE
+   #define GLE(a,b) gl##a = (PFNGL##b##PROC) STBGL__GET_FUNC("gl" #a );
+
+   void stbgl_initExtensions(void)
+   {
+      #include STB_GLEXT_DEFINE
+   }
+
+   #undef GLE
+
+#endif // STB_GLEXT_DECLARE
+
+#endif // STB_GLEXT_SKIP
+
+#endif // STB_GLEXT_DEFINE || STB_GLEXT_DECLARE