From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / jwzgles.c
index e10d2e6ed623328868b6b2dcb1cb2ccdc09b2ec7..325e4654b4b597527d1c222ccf849c2bfbd4a8d9 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2012-2014 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 
    As a result of that, these savers look wrong:
 
+      atlantis        Uses EYE_PLANE.
       blocktube       Uses SPHERE_MAP.
-      bouncingcow     Uses OBJECT_LINEAR (when run with -texture)
       dnalogo         Uses GLUtesselator.
       extrusion       Uses all kinds of GLUT crap.
-      flyingtoasters  Uses SPHERE_MAP and OBJECT_LINEAR.
+      flyingtoasters  Uses SPHERE_MAP.
+      winduprobot     Uses SPHERE_MAP.
       jigglypuff      Uses SPHERE_MAP (in chrome mode), GL_LINE (in wireframe)
       jigsaw          Uses GLUtesselator.
       lockward        Puts verts in lists without glBegin!
       pinion         Uses glSelectBuffer and gluPickMatrix for mouse-clicks.
       pipes           Uses glMap2f for the Utah Teapot.
-      polyhedra       Uses GLUtesselator; also Utah Teapot.
-      skytentacles    Uses glTexImage1D and GL_LINE in -cel mode.
+      polyhedra       Uses GLUtesselator (concave objects); also Utah Teapot.
+      skytentacles    Uses GL_LINE in -cel mode.
       timetunnel      Uses GL_CONSTANT_ALPHA and all kinds of other stuff.
 
 */
 #elif defined(HAVE_COCOA)
 # include <OpenGL/gl.h>
 # include <OpenGL/glu.h>
-#else
+#else /* X11 */
+# ifndef  GL_GLEXT_PROTOTYPES
+#  define GL_GLEXT_PROTOTYPES /* for glBindBuffer */
+# endif
 # include <GL/glx.h>
 # include <GL/glu.h>
 #endif
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
 #undef  Assert
-#define Assert(C,S) do { \
-  if (!(C)) { \
-    fprintf (stderr, "jwzgles: %s\n", S); \
-    abort(); \
-  }} while(0)
+
+#ifdef HAVE_COCOA
+  extern void jwxyz_abort (const char *fmt, ...) __dead2;
+# define Assert(C,S) do { if (!(C)) { jwxyz_abort ("%s",S); }} while(0)
+#else
+# define Assert(C,S) do { \
+    if (!(C)) { \
+      fprintf (stderr, "jwzgles: %s\n", S); \
+      abort(); \
+    }} while(0)
+#endif
 
 
 typedef struct { GLfloat x, y, z; }    XYZ;
@@ -225,10 +235,12 @@ typedef void (*list_fn_cb) (void);
    or vice versa.  They tend to be passed in different registers,
    and you need to know about that because it's still 1972 here.
  */
-typedef union { const void *v; GLfloat f; GLuint i; } void_int;
+typedef union {
+  const void *v; GLfloat f; GLuint i; GLshort s; GLdouble d;
+} void_int;
 
 typedef struct {               /* saved args for glDrawArrays */
-  int size, type, stride, bytes;
+  int binding, size, type, stride, bytes;
   void *data;
 } draw_array;
 
@@ -261,14 +273,18 @@ typedef struct {          /* A single element of a display list */
 } list_fn;
 
 
-typedef struct {       /* saved activity within glNewList */
+typedef struct {       /* a display list: saved activity within glNewList */
   int id;
   int size, count;
   list_fn *fns;
+
+  /* Named buffer that should be freed when this display list is deleted. */
+  GLuint buffer;
+
 } list;
 
 
-typedef struct {       /* A display list */
+typedef struct {       /* All display lists */
   list *lists;
   int count, size;
 } list_set;
@@ -277,17 +293,25 @@ typedef struct {  /* A display list */
 #define ISENABLED_TEXTURE_2D   (1<<0)
 #define ISENABLED_TEXTURE_GEN_S        (1<<1)
 #define ISENABLED_TEXTURE_GEN_T        (1<<2)
-#define ISENABLED_LIGHTING     (1<<3)
-#define ISENABLED_BLEND                (1<<4)
-#define ISENABLED_DEPTH_TEST   (1<<5)
-#define ISENABLED_CULL_FACE    (1<<6)
-#define ISENABLED_NORMALIZE    (1<<7)
-#define ISENABLED_FOG          (1<<8)
-#define ISENABLED_COLMAT       (1<<9)
-#define ISENABLED_VERT_ARRAY   (1<<10)
-#define ISENABLED_NORM_ARRAY   (1<<11)
-#define ISENABLED_TEX_ARRAY    (1<<12)
-#define ISENABLED_COLOR_ARRAY  (1<<13)
+#define ISENABLED_TEXTURE_GEN_R        (1<<3)
+#define ISENABLED_TEXTURE_GEN_Q        (1<<4)
+#define ISENABLED_LIGHTING     (1<<5)
+#define ISENABLED_BLEND                (1<<6)
+#define ISENABLED_DEPTH_TEST   (1<<7)
+#define ISENABLED_CULL_FACE    (1<<8)
+#define ISENABLED_NORMALIZE    (1<<9)
+#define ISENABLED_FOG          (1<<10)
+#define ISENABLED_COLMAT       (1<<11)
+#define ISENABLED_VERT_ARRAY   (1<<12)
+#define ISENABLED_NORM_ARRAY   (1<<13)
+#define ISENABLED_TEX_ARRAY    (1<<14)
+#define ISENABLED_COLOR_ARRAY  (1<<15)
+
+
+typedef struct {
+  GLuint mode;
+  GLfloat obj[4], eye[4];
+} texgen_state;
 
 
 typedef struct {       /* global state */
@@ -299,13 +323,16 @@ typedef struct {  /* global state */
   int compiling_verts; /* inside glBegin */
 
   list_set lists;      /* saved lists */
-  unsigned long enabled;
+
+  unsigned long enabled;       /* enabled flags, immediate mode */
+  unsigned long list_enabled;  /* and for the list-in-progress */
+
+  texgen_state s, t, r, q;
 
 } jwzgles_state;
 
 
-static jwzgles_state global_state = { { 0, }, 0, 0, 0, { 0, }, 0, };
-static jwzgles_state *state = &global_state;
+static jwzgles_state *state = 0;
 
 
 #ifdef DEBUG
@@ -349,19 +376,16 @@ mode_desc (int mode)      /* for debugging messages */
   switch (mode) {
 # define SS(X) case GL_##X: return STRINGIFY(X);
   SS(ALPHA)
+  SS(ALPHA_TEST)
   SS(AMBIENT)
   SS(AMBIENT_AND_DIFFUSE)
+  SS(ARRAY_BUFFER)
   SS(AUTO_NORMAL)
   SS(BACK)
   SS(BLEND)
   SS(BLEND_DST)
   SS(BLEND_SRC)
   SS(BLEND_SRC_ALPHA)
-  SS(RGBA_MODE)
-  SS(DOUBLEBUFFER)
-  SS(GREATER)
-  SS(ALPHA_TEST)
-  SS(LESS)
   SS(BYTE)
   SS(C3F_V3F)
   SS(C4F_N3F_V3F)
@@ -370,6 +394,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(CCW)
   SS(CLAMP)
   SS(COLOR_ARRAY)
+  SS(COLOR_ARRAY_BUFFER_BINDING);
   SS(COLOR_MATERIAL)
   SS(COLOR_MATERIAL_FACE)
   SS(COLOR_MATERIAL_PARAMETER)
@@ -380,8 +405,11 @@ mode_desc (int mode)       /* for debugging messages */
   SS(DEPTH_BUFFER_BIT)
   SS(DEPTH_TEST)
   SS(DIFFUSE)
+  SS(DOUBLEBUFFER)
   SS(DST_ALPHA)
   SS(DST_COLOR)
+  SS(DYNAMIC_DRAW)
+  SS(ELEMENT_ARRAY_BUFFER)
   SS(EYE_LINEAR)
   SS(EYE_PLANE)
   SS(FEEDBACK)
@@ -391,10 +419,12 @@ mode_desc (int mode)      /* for debugging messages */
   SS(FOG)
   SS(FRONT)
   SS(FRONT_AND_BACK)
+  SS(GREATER)
   SS(INTENSITY)
   SS(INVALID_ENUM)
   SS(INVALID_OPERATION)
   SS(INVALID_VALUE)
+  SS(LESS)
   SS(LIGHT0)
   SS(LIGHT1)
   SS(LIGHT2)
@@ -422,6 +452,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(NEAREST_MIPMAP_NEAREST)
   SS(NORMALIZE)
   SS(NORMAL_ARRAY)
+  SS(NORMAL_ARRAY_BUFFER_BINDING);
   SS(OBJECT_LINEAR)
   SS(OBJECT_PLANE)
   SS(ONE_MINUS_DST_ALPHA)
@@ -445,6 +476,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(REPEAT)
   SS(RGB)
   SS(RGBA)
+  SS(RGBA_MODE)
   SS(S)
   SS(SELECT)
   SS(SEPARATE_SPECULAR_COLOR)
@@ -460,6 +492,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(SRC_COLOR)
   SS(STACK_OVERFLOW)
   SS(STACK_UNDERFLOW)
+  SS(STATIC_DRAW)
   SS(STENCIL_BUFFER_BIT)
   SS(T)
   SS(T2F_C3F_V3F)
@@ -479,6 +512,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(TEXTURE_BORDER_COLOR)
   SS(TEXTURE_COMPONENTS)
   SS(TEXTURE_COORD_ARRAY)
+  SS(TEXTURE_COORD_ARRAY_BUFFER_BINDING);
   SS(TEXTURE_ENV)
   SS(TEXTURE_ENV_COLOR)
   SS(TEXTURE_ENV_MODE)
@@ -507,6 +541,7 @@ mode_desc (int mode)        /* for debugging messages */
   SS(V2F)
   SS(V3F)
   SS(VERTEX_ARRAY)
+  SS(VERTEX_ARRAY_BUFFER_BINDING);
 /*SS(COLOR_BUFFER_BIT) -- same value as GL_LIGHT0 */
 # undef SS
   case (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT):
@@ -539,6 +574,48 @@ check_gl_error (const char *s)
 #endif /* DEBUG */
 
 
+static void
+make_room (const char *name, void **array, int span, int *count, int *size)
+{
+  if (*count + 1 >= *size)
+    {
+      int new_size = (*count + 20) * 1.2;   /* mildly exponential */
+      *array = realloc (*array, new_size * span);
+      Assert (*array, "out of memory");
+      /* LOG3("%s: grew %d -> %d", name, *size, new_size); */
+      *size = new_size;
+    }
+}
+
+
+void
+jwzgles_reset (void)
+{
+  if (! state)
+    state = (jwzgles_state *) calloc (1, sizeof (*state));
+
+  if (state->lists.lists)
+    {
+      state->compiling_list = 0;
+      if (state->lists.count)
+        jwzgles_glDeleteLists (1, state->lists.count);
+      free (state->lists.lists);
+    }
+
+  if (state->set.verts)   free (state->set.verts);
+  if (state->set.norms)   free (state->set.norms);
+  if (state->set.tex)     free (state->set.tex);
+  if (state->set.color)   free (state->set.color);
+
+  memset (state, 0, sizeof(*state));
+
+  state->s.mode = state->t.mode = state->r.mode = state->q.mode =
+    GL_EYE_LINEAR;
+  state->s.obj[0] = state->s.eye[0] = 1;  /* s = 1 0 0 0 */
+  state->t.obj[1] = state->t.eye[1] = 1;  /* t = 0 1 0 0 */
+}
+
+
 int
 jwzgles_glGenLists (int n)
 {
@@ -557,19 +634,10 @@ jwzgles_glGenLists (int n)
     {
       list *L;
       int id = 0;
-
-      /* Adding a new list at the end.  Make room for it.
-       */
-      if (state->lists.count >= state->lists.size - 1)
-        {
-          int new_size = 20 + (state->lists.size * 1.2);
-          state->lists.lists = (list *) 
-            realloc (state->lists.lists, 
-                     new_size * sizeof (*state->lists.lists));
-          Assert (state->lists.lists, "out of memory");
-          state->lists.size = new_size;
-          LOG1("glGenLists grew -> %d", new_size);
-        }
+      make_room ("glGenLists", 
+                 (void **) &state->lists.lists,
+                 sizeof (*state->lists.lists),
+                 &state->lists.count, &state->lists.size);
       state->lists.count++;
       id = state->lists.count;
       L = &state->lists.lists[id-1];
@@ -604,10 +672,19 @@ jwzgles_glNewList (int id, int mode)
   
   state->compiling_list = id;
 
+  state->list_enabled = state->enabled;
+
   LOG1("glNewList -> %d", id);
 }
 
 
+static void save_arrays (list_fn *, int);
+static void restore_arrays (list_fn *, int);
+static void copy_array_data (draw_array *, int, const char *);
+static void optimize_arrays (void);
+static void generate_texture_coords (GLuint, GLuint);
+
+
 void
 jwzgles_glEndList (void)
 {
@@ -615,14 +692,12 @@ jwzgles_glEndList (void)
   Assert (state->set.count == 0, "missing glEnd");
   Assert (!state->compiling_verts, "glEndList not allowed inside glBegin");
   LOG1("glEndList %d", state->compiling_list);
+  optimize_arrays();
   state->compiling_list = 0;
+  state->list_enabled = state->enabled;
 }
 
 
-static void save_arrays (list_fn *, int);
-static void restore_arrays (list_fn *, int);
-static void copy_array_data (draw_array *, int, const char *);
-
 static void
 list_push (const char * const name, 
            list_fn_cb fn, fn_proto proto, void_int *av)
@@ -637,16 +712,10 @@ list_push (const char * const name,
   L = &state->lists.lists[state->compiling_list-1];
   Assert (L, "glNewList: no list");
 
-  if (L->count >= L->size - 1)
-    {
-      int new_size = 20 + (L->size * 1.2);
-      L->fns = (list_fn *) realloc (L->fns, new_size * sizeof (*L->fns));
-      Assert (L->fns, "glNewList: no functions");
-      L->size = new_size;
-    }
-
+  make_room ("glNewLists", 
+             (void **) &L->fns, sizeof (*L->fns),
+             &L->count, &L->size);
   memset (&L->fns[L->count], 0, sizeof (*L->fns));
-
   F = L->fns + L->count;
 
   F->name = name;
@@ -678,7 +747,8 @@ list_push (const char * const name,
     LOG2 ("  push %-12s %7.3f", name, av[0].f);
     break;
   case PROTO_II:
-    if (fn == (list_fn_cb) &jwzgles_glBindTexture)
+    if (fn == (list_fn_cb) &jwzgles_glBindTexture ||
+        fn == (list_fn_cb) &jwzgles_glBindBuffer)
       LOG3 ("  push %-12s %s %d", name, mode_desc (av[0].i), av[1].i);
     else
       LOG3 ("  push %-12s %d %d", name, av[0].i, av[1].i);
@@ -834,14 +904,17 @@ jwzgles_glDeleteLists (int id0, int range)
                 {
                   int j;
                   for (j = 0; j < 4; j++)
-                    {
-                      if (lf->arrays[j].data)
-                        free (lf->arrays[j].data);
-                    }
+                    /* If there's a binding, 'data' is an index, not a ptr. */
+                    if (!lf->arrays[j].binding &&
+                        lf->arrays[j].data)
+                      free (lf->arrays[j].data);
+                  free (lf->arrays);
                 }
             }
           if (L->fns) 
             free (L->fns);
+          if (L->buffer)
+            glDeleteBuffers (1, &L->buffer);
 
           memset (L, 0, sizeof (*L));
           L->id = id;
@@ -878,14 +951,16 @@ jwzgles_glNormal3fv (const GLfloat *v)
               (state->compiling_verts ? "  rec  " : ""),
               v[0], v[1], v[2]);
 
-      state->set.cnorm.x = v[0];
-      state->set.cnorm.y = v[1];
-      state->set.cnorm.z = v[2];
-      state->set.ncount++;
-      if (state->set.count > 0 && state->set.ncount == 1)  /* not first! */
-        state->set.ncount++;
-
-      if (! state->compiling_verts)    /* outside glBegin */
+      if (state->compiling_verts)      /* inside glBegin */
+        {
+          state->set.cnorm.x = v[0];
+          state->set.cnorm.y = v[1];
+          state->set.cnorm.z = v[2];
+          state->set.ncount++;
+          if (state->set.count > 0 && state->set.ncount == 1)  /* not first! */
+            state->set.ncount++;
+        }
+      else                             /* outside glBegin */
         {
           glNormal3f (v[0], v[1], v[2]);
           CHECK("glNormal3f");
@@ -926,15 +1001,18 @@ jwzgles_glTexCoord4fv (const GLfloat *v)
               (state->compiling_verts ? "  rec  " : ""),
               v[0], v[1], v[2], v[3]);
 
-      state->set.ctex.s = v[0];
-      state->set.ctex.t = v[1];
-      state->set.ctex.r = v[2];
-      state->set.ctex.q = v[3];
-      state->set.tcount++;
-      if (state->set.count > 0 && state->set.tcount == 1)  /* not first! */
-        state->set.tcount++;
-
       Assert (state->compiling_verts, "glTexCoord4fv outside glBegin");
+
+      if (state->compiling_verts)      /* inside glBegin */
+        {
+          state->set.ctex.s = v[0];
+          state->set.ctex.t = v[1];
+          state->set.ctex.r = v[2];
+          state->set.ctex.q = v[3];
+          state->set.tcount++;
+          if (state->set.count > 0 && state->set.tcount == 1)  /* not first! */
+            state->set.tcount++;
+        }
     }
 }
 
@@ -996,6 +1074,7 @@ jwzgles_glTexCoord1f (GLfloat s)
 }
 
 
+/* glColor: GLfloat */
 
 void
 jwzgles_glColor4fv (const GLfloat *v)
@@ -1018,15 +1097,17 @@ jwzgles_glColor4fv (const GLfloat *v)
               (state->compiling_verts ? "  rec  " : ""),
               v[0], v[1], v[2], v[3]);
 
-      state->set.ccolor.r = v[0];
-      state->set.ccolor.g = v[1];
-      state->set.ccolor.b = v[2];
-      state->set.ccolor.a = v[3];
-      state->set.ccount++;
-      if (state->set.count > 0 && state->set.ccount == 1)  /* not first! */
-        state->set.ccount++;
-
-      if (! state->compiling_verts)    /* outside glBegin */
+      if (state->compiling_verts)      /* inside glBegin */
+        {
+          state->set.ccolor.r = v[0];
+          state->set.ccolor.g = v[1];
+          state->set.ccolor.b = v[2];
+          state->set.ccolor.a = v[3];
+          state->set.ccount++;
+          if (state->set.count > 0 && state->set.ccount == 1)  /* not first! */
+            state->set.ccount++;
+        }
+      else                             /* outside glBegin */
         {
           glColor4f (v[0], v[1], v[2], v[3]);
           CHECK("glColor4");
@@ -1046,78 +1127,231 @@ jwzgles_glColor4f (GLfloat r, GLfloat g, GLfloat b, GLfloat a)
   jwzgles_glColor4fv (v);
 }
 
-
 void
 jwzgles_glColor3f (GLfloat r, GLfloat g, GLfloat b)
 {
   jwzgles_glColor4f (r, g, b, 1);
 }
 
-
 void
 jwzgles_glColor3fv (const GLfloat *v)
 {
-  GLfloat vv[4];
-  vv[0] = v[0];
-  vv[1] = v[1];
-  vv[2] = v[2];
-  vv[3] = 1;
-  jwzgles_glColor4fv (vv);
+  jwzgles_glColor3f (v[0], v[1], v[2]);
 }
 
 
+/* glColor: GLdouble */
+
 void
-jwzgles_glColor4i (GLuint r, GLuint g, GLuint b, GLuint a)
+jwzgles_glColor4d (GLdouble r, GLdouble g, GLdouble b, GLdouble a)
 {
   jwzgles_glColor4f (r, g, b, a);
 }
 
+void
+jwzgles_glColor4dv (const GLdouble *v)
+{
+  jwzgles_glColor4d (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3d (GLdouble r, GLdouble g, GLdouble b)
+{
+  jwzgles_glColor4d (r, g, b, 1.0);
+}
 
 void
-jwzgles_glColor3i (GLuint r, GLuint g, GLuint b)
+jwzgles_glColor3dv (const GLdouble *v)
 {
-  jwzgles_glColor4f (r, g, b, 1);
+  jwzgles_glColor3d (v[0], v[1], v[2]);
 }
 
 
+/* glColor: GLint (INT_MIN - INT_MAX) */
+
+void
+jwzgles_glColor4i (GLint r, GLint g, GLint b, GLint a)
+{
+  /* -0x8000000 - 0x7FFFFFFF  =>  0.0 - 1.0 */
+  jwzgles_glColor4f (0.5 + (GLfloat) r / 0xFFFFFFFF,
+                     0.5 + (GLfloat) g / 0xFFFFFFFF, 
+                     0.5 + (GLfloat) b / 0xFFFFFFFF,
+                     0.5 + (GLfloat) a / 0xFFFFFFFF);
+}
+
 void
 jwzgles_glColor4iv (const GLint *v)
 {
-  GLfloat vv[4];
-  vv[0] = v[0];
-  vv[1] = v[1];
-  vv[2] = v[2];
-  vv[3] = v[3];
-  jwzgles_glColor4fv (vv);
+  jwzgles_glColor4i (v[0], v[1], v[2], v[3]);
 }
 
 
+void
+jwzgles_glColor3i (GLint r, GLint g, GLint b)
+{
+  jwzgles_glColor4i (r, g, b, 0x7FFFFFFF);
+}
+
 void
 jwzgles_glColor3iv (const GLint *v)
 {
-  GLfloat vv[4];
-  vv[0] = v[0];
-  vv[1] = v[1];
-  vv[2] = v[2];
-  vv[3] = 1;
-  jwzgles_glColor4fv (vv);
+  jwzgles_glColor3i (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLuint (0 - UINT_MAX) */
+
+void
+jwzgles_glColor4ui (GLuint r, GLuint g, GLuint b, GLuint a)
+{
+  /* 0 - 0xFFFFFFFF  =>  0.0 - 1.0 */
+  jwzgles_glColor4f ((GLfloat) r / 0xFFFFFFFF,
+                     (GLfloat) g / 0xFFFFFFFF, 
+                     (GLfloat) b / 0xFFFFFFFF,
+                     (GLfloat) a / 0xFFFFFFFF);
+}
+
+void
+jwzgles_glColor4uiv (const GLuint *v)
+{
+  jwzgles_glColor4ui (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3ui (GLuint r, GLuint g, GLuint b)
+{
+  jwzgles_glColor4ui (r, g, b, 0xFFFFFFFF);
+}
+
+void
+jwzgles_glColor3uiv (const GLuint *v)
+{
+  jwzgles_glColor3ui (v[0], v[1], v[2]);
 }
 
 
+/* glColor: GLshort (SHRT_MIN - SHRT_MAX) */
+
+void
+jwzgles_glColor4s (GLshort r, GLshort g, GLshort b, GLshort a)
+{
+  /* -0x8000 - 0x7FFF  =>  0.0 - 1.0 */
+  jwzgles_glColor4f (0.5 + (GLfloat) r / 0xFFFF,
+                     0.5 + (GLfloat) g / 0xFFFF,
+                     0.5 + (GLfloat) b / 0xFFFF,
+                     0.5 + (GLfloat) a / 0xFFFF);
+}
+
+void
+jwzgles_glColor4sv (const GLshort *v)
+{
+  jwzgles_glColor4s (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3s (GLshort r, GLshort g, GLshort b)
+{
+  jwzgles_glColor4s (r, g, b, 0x7FFF);
+}
+
+void
+jwzgles_glColor3sv (const GLshort *v)
+{
+  jwzgles_glColor3s (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLushort (0 - USHRT_MAX) */
+
+void
+jwzgles_glColor4us (GLushort r, GLushort g, GLushort b, GLushort a)
+{
+  /* 0 - 0xFFFF  =>  0.0 - 1.0 */
+  jwzgles_glColor4f ((GLfloat) r / 0xFFFF,
+                     (GLfloat) g / 0xFFFF,
+                     (GLfloat) b / 0xFFFF,
+                     (GLfloat) a / 0xFFFF);
+}
+
+void
+jwzgles_glColor4usv (const GLushort *v)
+{
+  jwzgles_glColor4us (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3us (GLushort r, GLushort g, GLushort b)
+{
+  jwzgles_glColor4us (r, g, b, 0xFFFF);
+}
+
+void
+jwzgles_glColor3usv (const GLushort *v)
+{
+  jwzgles_glColor3us (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLbyte (-128 - 127) */
+
+void
+jwzgles_glColor4b (GLbyte r, GLbyte g, GLbyte b, GLbyte a)
+{
+  /* -128 - 127  =>  0.0 - 1.0 */
+  jwzgles_glColor4f (0.5 + (GLfloat) r / 255,
+                     0.5 + (GLfloat) g / 255,
+                     0.5 + (GLfloat) b / 255,
+                     0.5 + (GLfloat) a / 255);
+}
+
+void
+jwzgles_glColor4bv (const GLbyte *v)
+{
+  jwzgles_glColor4b (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3b (GLbyte r, GLbyte g, GLbyte b)
+{
+  jwzgles_glColor4b (r, g, b, 127);
+}
+
+void
+jwzgles_glColor3bv (const GLbyte *v)
+{
+  jwzgles_glColor3b (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLubyte (0 - 255) */
+
 void
 jwzgles_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a)
 {
-  jwzgles_glColor4f (r, g, b, a);
+  /* 0 - 255  =>  0.0 - 1.0 */
+  jwzgles_glColor4f (r / 255.0, g / 255.0, b / 255.0, a / 255.0);
 }
 
+void
+jwzgles_glColor4ubv (const GLubyte *v)
+{
+  jwzgles_glColor4ub (v[0], v[1], v[2], v[3]);
+}
 
 void
 jwzgles_glColor3ub (GLubyte r, GLubyte g, GLubyte b)
 {
-  jwzgles_glColor4f (r, g, b, 1);
+  jwzgles_glColor4ub (r, g, b, 255);
+}
+
+void
+jwzgles_glColor3ubv (const GLubyte *v)
+{
+  jwzgles_glColor3ub (v[0], v[1], v[2]);
 }
 
 
+
 void
 jwzgles_glMaterialfv (GLenum face, GLenum pname, const GLfloat *color)
 {
@@ -1556,6 +1790,8 @@ jwzgles_glEnd (void)
   Assert (state->compiling_verts == 1, "missing glBegin");
   state->compiling_verts--;
 
+  Assert (!state->replaying_list, "how did glEnd get into a display list?");
+
   if (!state->replaying_list)
     {
       LOG5 ("%s  [V = %d, N = %d, T = %d, C = %d]",
@@ -1574,11 +1810,11 @@ jwzgles_glEnd (void)
   else if (s->mode == GL_POLYGON)
     s->mode = GL_TRIANGLE_FAN;         /* They do the same thing! */
 
-  glVertexPointer   (4, GL_FLOAT, sizeof(*s->verts), s->verts);  /* XYZW */
-  glNormalPointer   (   GL_FLOAT, sizeof(*s->norms), s->norms);  /* XYZ  */
-  glTexCoordPointer (4, GL_FLOAT, sizeof(*s->tex),   s->tex);    /* STRQ */
-  glColorPointer    (4, GL_FLOAT, sizeof(*s->color), s->color);  /* RGBA */
-  CHECK("glColorPointer");
+  jwzgles_glColorPointer   (4,GL_FLOAT, sizeof(*s->color),s->color); /* RGBA */
+  jwzgles_glNormalPointer  (  GL_FLOAT, sizeof(*s->norms),s->norms); /* XYZ  */
+  jwzgles_glTexCoordPointer(4,GL_FLOAT, sizeof(*s->tex),  s->tex);   /* STRQ */
+  jwzgles_glVertexPointer  (4,GL_FLOAT, sizeof(*s->verts),s->verts); /* XYZW */
+  /* glVertexPointer must come after glTexCoordPointer */
 
   /* If there were no calls to glNormal3f inside of glBegin/glEnd,
      don't bother enabling the normals array.
@@ -1592,15 +1828,22 @@ jwzgles_glEnd (void)
 
      Be careful to leave the arrays' enabled/disabled state the same as
      before, or a later caller might end up using one of our arrays by
-     mistake.  #### Actually this isn't quite right: if glEnd is in a
-     list, it saves the trailing enable/disable calls in the list, instead
-     if restoring them to what their state was before the list was run.
+     mistake.  (Remember that jwzgles_glIsEnabled() tracks the enablement
+     of the list-in-progress as well as the global state.)
   */
   was_norm  = jwzgles_glIsEnabled (GL_NORMAL_ARRAY);
   was_tex   = jwzgles_glIsEnabled (GL_TEXTURE_COORD_ARRAY);
   was_color = jwzgles_glIsEnabled (GL_COLOR_ARRAY);
   was_mat   = jwzgles_glIsEnabled (GL_COLOR_MATERIAL);
 
+  /* If we're executing glEnd in immediate mode, not from inside a display
+     list (which is the only way it happens, because glEnd doesn't go into
+     display lists), make sure we're not stomping on a saved buffer list:
+     in immediate mode, vertexes are client-side only.
+   */
+  if (! state->compiling_list)
+    jwzgles_glBindBuffer (GL_ARRAY_BUFFER, 0);
+
   if (s->ncount > 1)
     {
       is_norm = 1;
@@ -1614,8 +1857,13 @@ jwzgles_glEnd (void)
       jwzgles_glDisableClientState (GL_NORMAL_ARRAY);
     }
 
-  if (s->tcount > 1)
+  if (s->tcount > 1 ||
+      ((state->compiling_list ? state->list_enabled : state->enabled)
+       & (ISENABLED_TEXTURE_GEN_S | ISENABLED_TEXTURE_GEN_T |
+          ISENABLED_TEXTURE_GEN_R | ISENABLED_TEXTURE_GEN_Q)))
     {
+      /* Enable texture coords if any were specified; or if generation
+         is on in immediate mode; or if this list turned on generation. */
       is_tex = 1;
       jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY);
     }
@@ -1654,7 +1902,9 @@ jwzgles_glEnd (void)
   else
     is_mat = 0;
 
+  glBindBuffer (GL_ARRAY_BUFFER, 0);    /* This comes later. */
   jwzgles_glDrawArrays (s->mode, 0, s->count);
+  glBindBuffer (GL_ARRAY_BUFFER, 0);    /* Keep out of others' hands */
 
 # define RESET(VAR,FN,ARG) do { \
          if (is_##VAR != was_##VAR) { \
@@ -1675,6 +1925,116 @@ jwzgles_glEnd (void)
 }
 
 
+/* The display list is full of calls to glDrawArrays(), plus saved arrays
+   of the values we need to restore before calling it.  "Restore" means
+   "ship them off to the GPU before each call".
+
+   So instead, this function walks through the display list and
+   combines all of those vertex, normal, texture and color values into
+   a single VBO array; ships those values off to the GPU *once* at the
+   time of glEndList; and when running the list with glCallList, the
+   values are already on the GPU and don't need to be sent over again.
+
+   The VBO persists in the GPU until the display list is deleted.
+ */
+static void
+optimize_arrays (void)
+{
+  list *L = &state->lists.lists[state->compiling_list-1];
+  int i, j;
+  GLfloat *combo = 0;
+  int combo_count = 0;
+  int combo_size = 0;
+  GLuint buf_name = 0;
+
+  Assert (state->compiling_list, "not compiling a list");
+  Assert (L, "no list");
+  Assert (!L->buffer, "list already has a buffer");
+
+  glGenBuffers (1, &buf_name);
+  CHECK("glGenBuffers");
+  if (! buf_name) return;
+
+  L->buffer = buf_name;
+
+  /* Go through the list and dump the contents of the various saved arrays
+     into one large array.
+   */
+  for (i = 0; i < L->count; i++)
+    {
+      list_fn *F = &L->fns[i];
+/*      int count; */
+      if (! F->arrays)
+        continue;
+/*      count = F->argv[2].i;*/  /* 3rd arg to glDrawArrays */
+
+      for (j = 0; j < 4; j++)
+        {
+          draw_array *A = &F->arrays[j];
+          int ocount = combo_count;
+
+          /* If some caller is using arrays that don't have floats in them,
+             we just leave them as-is and ship them over at each call.
+             Doubt this ever really happens.
+           */
+          if (A->type != GL_FLOAT)
+            continue;
+
+          if (! A->data)       /* No array. */
+            continue;
+
+          Assert (A->bytes > 0, "no bytes in draw_array");
+          Assert (((unsigned long) A->data > 0xFFFF),
+                  "buffer data not a pointer");
+
+          combo_count += A->bytes / sizeof(*combo);
+          make_room ("optimize_arrays",
+                     (void **) &combo, sizeof(*combo),
+                     &combo_count, &combo_size);
+          memcpy (combo + ocount, A->data, A->bytes);
+          A->binding = buf_name;
+          free (A->data);
+          /* 'data' is now the byte offset into the VBO. */
+          A->data = (void *) (ocount * sizeof(*combo));
+          /* LOG3("    loaded %lu floats to pos %d of buffer %d",
+               A->bytes / sizeof(*combo), ocount, buf_name); */
+        }
+    }
+
+  if (combo_count == 0)                /* Nothing to do! */
+    {
+      if (combo) free (combo);
+      glDeleteBuffers (1, &buf_name);
+      L->buffer = 0;
+      return;
+    }
+
+  glBindBuffer (GL_ARRAY_BUFFER, buf_name);
+  glBufferData (GL_ARRAY_BUFFER, 
+                combo_count * sizeof (*combo),
+                combo,
+                GL_STATIC_DRAW);
+  glBindBuffer (GL_ARRAY_BUFFER, 0);    /* Keep out of others' hands */
+
+  LOG3("  loaded %d floats of list %d into VBO %d",
+       combo_count, state->compiling_list, buf_name);
+
+# ifdef DEBUG
+#  if 0
+  for (i = 0; i < combo_count; i++)
+    {
+      if (i % 4 == 0)
+        fprintf (stderr, "\njwzgles:    %4d: ", i);
+      fprintf (stderr, " %7.3f", combo[i]);
+    }
+  fprintf (stderr, "\n");
+#  endif
+# endif /* DEBUG */
+
+  if (combo) free (combo);
+}
+
+
 void
 jwzgles_glCallList (int id)
 {
@@ -1735,7 +2095,8 @@ jwzgles_glCallList (int id)
             break;
 
           case PROTO_II:
-            if (fn == (list_fn_cb) &jwzgles_glBindTexture)
+            if (fn == (list_fn_cb) &jwzgles_glBindTexture ||
+                fn == (list_fn_cb) &jwzgles_glBindBuffer)
               LOG3 ("  call %-12s %s %d", F->name, 
                     mode_desc (av[0].i), av[1].i);
             else
@@ -1902,6 +2263,7 @@ save_arrays (list_fn *F, int count)
 
 /*  if (state->set.count > 0) */
     {
+      glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A[i].binding);
       glGetIntegerv (GL_VERTEX_ARRAY_SIZE,    &A[i].size);
       glGetIntegerv (GL_VERTEX_ARRAY_TYPE,    &A[i].type);
       glGetIntegerv (GL_VERTEX_ARRAY_STRIDE,  &A[i].stride);
@@ -1914,6 +2276,7 @@ save_arrays (list_fn *F, int count)
   if (state->set.ncount > 1)
     {
       A[i].size = 3;
+      glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A[i].binding);
       glGetIntegerv (GL_NORMAL_ARRAY_TYPE,    &A[i].type);
       glGetIntegerv (GL_NORMAL_ARRAY_STRIDE,  &A[i].stride);
       glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A[i].data);
@@ -1924,6 +2287,7 @@ save_arrays (list_fn *F, int count)
   i++;
   if (state->set.tcount > 1)
     {
+      glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A[i].binding);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE,    &A[i].size);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE,    &A[i].type);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE,  &A[i].stride);
@@ -1935,6 +2299,7 @@ save_arrays (list_fn *F, int count)
   i++;
   if (state->set.ccount > 1)
     {
+      glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A[i].binding);
       glGetIntegerv (GL_COLOR_ARRAY_SIZE,    &A[i].size);
       glGetIntegerv (GL_COLOR_ARRAY_TYPE,    &A[i].type);
       glGetIntegerv (GL_COLOR_ARRAY_STRIDE,  &A[i].stride);
@@ -1958,20 +2323,64 @@ dump_array_data (draw_array *A, int count,
 {
   int bytes = count * A->stride;
 
-  Assert (bytes == A->bytes, "array data corrupted");
+  if (A->binding)
+    {
+      fprintf (stderr, 
+               "jwzgles:     %s %s %d %s %2d, %4d = %5d   bind %d @ %d\n", 
+               action, name,
+               A->size, mode_desc(A->type), A->stride, 
+               count, bytes, A->binding, (int) A->data);
+    }
+  else
+    {
+      Assert (bytes == A->bytes, "array data corrupted");
+
+      fprintf (stderr, "jwzgles:     %s %s %d %s %2d, %4d = %5d @ %lX", 
+               action, name,
+               A->size, mode_desc(A->type), A->stride, 
+               count, bytes, (unsigned long) A->data);
+      if (old)
+        fprintf (stderr, " / %lX", (unsigned long) old);
+      fprintf (stderr, "\n");
+    }
 
-  fprintf (stderr, "jwzgles:     %s %s %d %s %2d, %4d = %5d @ %lX", 
-           action, name,
-           A->size, mode_desc(A->type), A->stride, 
-           count, bytes, (unsigned long) A->data);
-  if (old)
-    fprintf (stderr, " / %lX", (unsigned long) old);
-  fprintf (stderr, "\n");
+  if (A->binding)
+    {
+      Assert (((unsigned long) A->data < 0xFFFF),
+              "buffer binding should be a numeric index,"
+              " but looks like a pointer");
 
 # if 0
-  {
+      /* glGetBufferSubData doesn't actually exist in OpenGLES, but this
+         was helpful for debugging on real OpenGL... */
+      GLfloat *d;
+      int i;
+      fprintf (stderr, "jwzgles: read back:\n");
+      d = (GLfloat *) malloc (A->bytes);
+      glGetBufferSubData (GL_ARRAY_BUFFER, (int) A->data,
+                          count * A->stride, (void *) d);
+      CHECK("glGetBufferSubData");
+      for (i = 0; i < count * A->size; i++)
+        {
+          if (i % 4 == 0)
+            fprintf (stderr, "\njwzgles:    %4d: ", 
+                     i + (int) A->data / sizeof(GLfloat));
+          fprintf (stderr, " %7.3f", d[i]);
+        }
+      fprintf (stderr, "\n");
+      free (d);
+# endif
+    }
+# if 0
+  else
+    {
       unsigned char *b = (unsigned char *) A->data;
       int i;
+      if ((unsigned long) A->data < 0xFFFF)
+        {
+          Assert (0, "buffer data not a pointer");
+          return;
+        }
       for (i = 0; i < count; i++)
         {
           int j;
@@ -1991,10 +2400,11 @@ dump_array_data (draw_array *A, int count,
 static void
 dump_direct_array_data (int count)
 {
-  draw_array A;
+  draw_array A = { 0, };
 
   if (jwzgles_glIsEnabled (GL_VERTEX_ARRAY))
     {
+      glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding);
       glGetIntegerv (GL_VERTEX_ARRAY_SIZE,    &A.size);
       glGetIntegerv (GL_VERTEX_ARRAY_TYPE,    &A.type);
       glGetIntegerv (GL_VERTEX_ARRAY_STRIDE,  &A.stride);
@@ -2005,6 +2415,7 @@ dump_direct_array_data (int count)
   if (jwzgles_glIsEnabled (GL_NORMAL_ARRAY))
     {
       A.size = 0;
+      glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A.binding);
       glGetIntegerv (GL_NORMAL_ARRAY_TYPE,    &A.type);
       glGetIntegerv (GL_NORMAL_ARRAY_STRIDE,  &A.stride);
       glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A.data);
@@ -2013,6 +2424,7 @@ dump_direct_array_data (int count)
     }
   if (jwzgles_glIsEnabled (GL_TEXTURE_COORD_ARRAY))
     {
+      glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A.binding);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE,    &A.size);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE,    &A.type);
       glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE,  &A.stride);
@@ -2022,6 +2434,7 @@ dump_direct_array_data (int count)
     }
   if (jwzgles_glIsEnabled (GL_COLOR_ARRAY))
     {
+      glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A.binding);
       glGetIntegerv (GL_COLOR_ARRAY_SIZE,    &A.size);
       glGetIntegerv (GL_COLOR_ARRAY_TYPE,    &A.type);
       glGetIntegerv (GL_COLOR_ARRAY_STRIDE,  &A.stride);
@@ -2044,14 +2457,17 @@ copy_array_data (draw_array *A, int count, const char *name)
      data multiple times.
    */
   int stride2, bytes, i, j;
-  const void *old;
   void *data2;
   const GLfloat *IF;
   GLfloat *OF;
   const unsigned char *IB;
   unsigned char *OB;
 
-  if (! A->data) return;
+  if (((unsigned long) A->data) < 0xFFFF)
+    {
+      Assert (0, "buffer data not a pointer");
+      return;
+    }
 
   Assert (A->size >= 2 && A->size <= 4, "bogus array size");
 
@@ -2094,13 +2510,12 @@ copy_array_data (draw_array *A, int count, const char *name)
     break;
   }
 
-  old = A->data;
   A->data = data2;
   A->bytes = bytes;
   A->stride = stride2;
 
 # ifdef DEBUG
-  dump_array_data (A, count, "saved", name, old);
+  dump_array_data (A, count, "saved", name, 0);
 # endif
 }
 
@@ -2114,8 +2529,17 @@ restore_arrays (list_fn *F, int count)
 
   for (i = 0; i < 4; i++)
     {
-      const char *name;
-      if (! A[i].data) continue;
+      const char *name = 0;
+
+      if (!A[i].size)
+        continue;
+
+      Assert ((A[i].binding || A[i].data),
+              "array has neither buffer binding nor data");
+
+      glBindBuffer (GL_ARRAY_BUFFER, A[i].binding);
+      CHECK("glBindBuffer");
+
       switch (i) {
       case 0: glVertexPointer  (A[i].size, A[i].type, A[i].stride, A[i].data);
         name = "vertex ";
@@ -2140,12 +2564,25 @@ restore_arrays (list_fn *F, int count)
       dump_array_data (&A[i], count, "restored", name, 0);
 # endif
     }
+
+  glBindBuffer (GL_ARRAY_BUFFER, 0);    /* Keep out of others' hands */
 }
 
 
 void
 jwzgles_glDrawArrays (GLuint mode, GLuint first, GLuint count)
 {
+  /* If we are auto-generating texture coordinates, do that now, after
+     the vertex array was installed, but before drawing, This happens
+     when recording into a list, or in direct mode.  It must happen
+     before calling optimize_arrays() from glEndList().
+   */
+  if (! state->replaying_list &&
+      ((state->compiling_list ? state->list_enabled : state->enabled)
+       & (ISENABLED_TEXTURE_GEN_S | ISENABLED_TEXTURE_GEN_T |
+          ISENABLED_TEXTURE_GEN_R | ISENABLED_TEXTURE_GEN_Q)))
+    generate_texture_coords (first, count);
+
   if (state->compiling_list)
     {
       void_int vv[3];
@@ -2376,81 +2813,6 @@ jwzgles_glInterleavedArrays (GLenum format, GLsizei stride, const void *data)
 
 
 
-void
-jwzgles_glEnableClientState (GLuint cap)
-{
-  if (state->compiling_list)
-    {
-      void_int vv[1];
-      vv[0].i = cap;
-      list_push ("glEnableClientState", 
-                 (list_fn_cb) &jwzgles_glEnableClientState,
-                 PROTO_I, vv);
-    }
-  else
-    {
-      if (! state->replaying_list)
-        LOG2 ("direct %-12s %s", "glEnableClientState", mode_desc(cap));
-      glEnableClientState (cap);  /* the real one */
-      CHECK("glEnableClientState");
-    }
-
-  switch (cap) {
-  case GL_NORMAL_ARRAY:
-    state->set.ncount += 2;
-    state->enabled |= ISENABLED_NORM_ARRAY;
-    break;
-  case GL_TEXTURE_COORD_ARRAY:
-    state->set.tcount += 2;
-    state->enabled |= ISENABLED_TEX_ARRAY;
-    break;
-  case GL_COLOR_ARRAY:
-    state->set.ccount += 2;
-    state->enabled |= ISENABLED_COLOR_ARRAY;
-    break;
-  default: break;
-  }
-}
-
-
-void
-jwzgles_glDisableClientState (GLuint cap)
-{
-  if (state->compiling_list)
-    {
-      void_int vv[1];
-      vv[0].i = cap;
-      list_push ("glDisableClientState", 
-                 (list_fn_cb) &jwzgles_glDisableClientState,
-                 PROTO_I, vv);
-    }
-  else
-    {
-      if (! state->replaying_list)
-        LOG2 ("direct %-12s %s", "glDisableClientState", mode_desc(cap));
-      glDisableClientState (cap);  /* the real one */
-      CHECK("glDisableClientState");
-    }
-
-  switch (cap) {
-  case GL_NORMAL_ARRAY:
-    state->set.ncount = 0;
-    state->enabled &= ~ISENABLED_NORM_ARRAY;
-    break;
-  case GL_TEXTURE_COORD_ARRAY:
-    state->set.tcount = 0;
-    state->enabled &= ~ISENABLED_TEX_ARRAY;
-    break;
-  case GL_COLOR_ARRAY:
-    state->set.ccount = 0;
-    state->enabled &= ~ISENABLED_COLOR_ARRAY;
-    break;
-  default:
-    break;
-  }
-}
-
-
 void
 jwzgles_glMultMatrixf (const GLfloat *m)
 {
@@ -2568,13 +2930,16 @@ jwzgles_glTexImage1D (GLenum target, GLint level,
                       GLint internalFormat,
                       GLsizei width, GLint border,
                       GLenum format, GLenum type,
-                      const GLvoid *pixels)
+                      const GLvoid *data)
 {
   Assert (!state->compiling_verts, "glTexImage1D not allowed inside glBegin");
   /* technically legal, but stupid! */
   Assert (!state->compiling_list, "glTexImage1D inside glNewList");
   Assert (width  == to_pow2(width), "width must be a power of 2");
-  Assert (0, "glTexImage1D unimplemented");  /* does not exist in GLES */
+
+  if (target == GL_TEXTURE_1D) target = GL_TEXTURE_2D;
+  jwzgles_glTexImage2D (target, level, internalFormat, width, 1,
+                        border, format, type, data);
 }
 
 void
@@ -2613,6 +2978,8 @@ jwzgles_glTexImage2D (GLenum target,
 
   if (internalFormat == GL_RGB && format == GL_RGBA)
     internalFormat = GL_RGBA;  /* WTF */
+  if (type == GL_UNSIGNED_INT_8_8_8_8_REV)
+    type = GL_UNSIGNED_BYTE;
 
   if (! state->replaying_list)
     LOG10 ("direct %-12s %s %d %s %d %d %d %s %s 0x%lX", "glTexImage2D", 
@@ -2666,30 +3033,41 @@ jwzgles_glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat,
 }
 
 
+/* OpenGLES doesn't have auto texture-generation at all!
+   "Oh, just rewrite that code to use GPU shaders", they say.
+   How fucking convenient.
+ */
 void
 jwzgles_glTexGenfv (GLenum coord, GLenum pname, const GLfloat *params)
 {
-  /* OpenGLES doesn't have this at all!
-     "Oh, just rewrite that code to use GPU shaders", they say.
-     How fucking convenient.
+  texgen_state *s;
 
-     So, when this is enabled, we could emit a GL_TEXTURE_COORD_ARRAY
-     and compute coords for each vertex in the current GL_VERTEX_ARRAY
-     as per http://www.opengl.org/wiki/Mathematics_of_glTexGen
-     but holy shit, what a pain in the ass!
-
-     For GL_OBJECT_LINEAR, we can just re-use the vertex array as
-     the texture array, using a proper stride.  That's hardly worth
-     the effort, though, because bouncingcow is the only hack that
-     uses that, and not even by default.
-   */
-  Assert (coord == GL_S || coord == GL_T, "glTexGenfv: unimplemented coord");
-
-  /* This is probably default-ish, so do nothing. */
-  if (pname == GL_EYE_PLANE) return;
+  if (pname == GL_TEXTURE_GEN_MODE)
+    LOG5 ("%sdirect %-12s %s %s %s", 
+          (state->compiling_list || state->replaying_list ? "  " : ""),
+          "glTexGenfv",
+          mode_desc(coord), mode_desc(pname), mode_desc(params[0]));
+  else
+    LOG8 ("%sdirect %-12s %s %s %3.1f %3.1f %3.1f %3.1f",
+          (state->compiling_list || state->replaying_list ? "  " : ""),
+          "glTexGenfv",
+          mode_desc(coord), mode_desc(pname),
+          params[0], params[1], params[2], params[3]);
+
+  switch (coord) {
+  case GL_S: s = &state->s; break;
+  case GL_T: s = &state->t; break;
+  case GL_R: s = &state->r; break;
+  case GL_Q: s = &state->q; break;
+  default: Assert (0, "glGetTexGenfv: unknown coord"); break;
+  }
 
-  Assert (pname == GL_TEXTURE_GEN_MODE, "glTexGenfv: unimplemented name");
-  Assert (params[0] == GL_EYE_LINEAR, "glTexGenfv: unimplemented mode");
+  switch (pname) {
+  case GL_TEXTURE_GEN_MODE: s->mode = params[0]; break;
+  case GL_OBJECT_PLANE:     memcpy (s->obj, params, sizeof(s->obj)); break;
+  case GL_EYE_PLANE:        memcpy (s->eye, params, sizeof(s->eye)); break;
+  default: Assert (0, "glTexGenfv: unknown pname"); break;
+  }
 }
 
 void
@@ -2699,6 +3077,168 @@ jwzgles_glTexGeni (GLenum coord, GLenum pname, GLint param)
   jwzgles_glTexGenfv (coord, pname, &v);
 }
 
+void
+jwzgles_glGetTexGenfv (GLenum coord, GLenum pname, GLfloat *params)
+{
+  texgen_state *s;
+
+  switch (coord) {
+  case GL_S: s = &state->s; break;
+  case GL_T: s = &state->t; break;
+  case GL_R: s = &state->r; break;
+  case GL_Q: s = &state->q; break;
+  default: Assert (0, "glGetTexGenfv: unknown coord"); break;
+  }
+
+  switch (pname) {
+  case GL_TEXTURE_GEN_MODE: params[0] = s->mode; break;
+  case GL_OBJECT_PLANE:     memcpy (params, s->obj, sizeof(s->obj)); break;
+  case GL_EYE_PLANE:        memcpy (params, s->eye, sizeof(s->eye)); break;
+  default: Assert (0, "glGetTexGenfv: unknown pname"); break;
+  }
+
+  if (pname == GL_TEXTURE_GEN_MODE)
+    LOG5 ("%sdirect %-12s %s %s -> %s", 
+          (state->compiling_list || state->replaying_list ? "  " : ""),
+          "glGetTexGenfv",
+          mode_desc(coord), mode_desc(pname), mode_desc(params[0]));
+  else
+    LOG8 ("%sdirect %-12s %s %s -> %3.1f %3.1f %3.1f %3.1f",
+          (state->compiling_list || state->replaying_list ? "  " : ""),
+          "glGetTexGenfv",
+          mode_desc(coord), mode_desc(pname),
+          params[0], params[1], params[2], params[3]);
+}
+
+
+static GLfloat
+dot_product (int rank, GLfloat *a, GLfloat *b)
+{
+  /* A dot B  =>  (A[1] * B[1]) + ... + (A[n] * B[n]) */
+  GLfloat ret = 0;
+  int i;
+  for (i = 0; i < rank; i++) 
+    ret += a[i] * b[i];
+  return ret;
+}
+
+
+
+/* Compute the texture coordinates of the prevailing list of verts as per
+   http://www.opengl.org/wiki/Mathematics_of_glTexGen
+ */
+static void
+generate_texture_coords (GLuint first, GLuint count)
+{
+  GLfloat *tex_out, *tex_array;
+  GLsizei tex_stride;
+  GLuint i;
+  draw_array A = { 0, };
+  char *verts_in;
+
+  struct { GLuint which, flag, mode; GLfloat plane[4]; } tg[4] = {
+    { GL_S, ISENABLED_TEXTURE_GEN_S, 0, { 0, } },
+    { GL_T, ISENABLED_TEXTURE_GEN_T, 0, { 0, } },
+    { GL_R, ISENABLED_TEXTURE_GEN_R, 0, { 0, } },
+    { GL_Q, ISENABLED_TEXTURE_GEN_Q, 0, { 0, }}};
+                                                    
+  int tcoords = 0;
+
+  /* Read the texture plane configs that were stored with glTexGen.
+   */
+  for (i = 0; i < countof(tg); i++)
+    {
+      GLfloat mode = 0;
+      if (! ((state->compiling_list ? state->list_enabled : state->enabled)
+             & tg[i].flag))
+        continue;
+      jwzgles_glGetTexGenfv (tg[i].which, GL_TEXTURE_GEN_MODE, &mode);
+      jwzgles_glGetTexGenfv (tg[i].which, GL_OBJECT_PLANE, tg[i].plane);
+      tg[i].mode = mode;
+      tcoords++;
+    }
+
+  if (tcoords == 0) return;  /* Nothing to do! */
+
+
+  /* Make the array to store our texture coords in. */
+
+  tex_stride = tcoords * sizeof(GLfloat);
+  tex_array = (GLfloat *) calloc (first + count, tex_stride);
+  tex_out = tex_array;
+
+
+  /* Read the prevailing vertex array, that was stored with
+     glVertexPointer or glInterleavedArrays.
+   */
+  glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding);
+  glGetIntegerv (GL_VERTEX_ARRAY_SIZE,    &A.size);
+  glGetIntegerv (GL_VERTEX_ARRAY_TYPE,    &A.type);
+  glGetIntegerv (GL_VERTEX_ARRAY_STRIDE,  &A.stride);
+  glGetPointerv (GL_VERTEX_ARRAY_POINTER, &A.data);
+  A.bytes = count * A.stride;
+
+  verts_in = (char *) A.data;
+
+  /* Iterate over each vertex we're drawing.
+     We just skip the ones < start, but the tex array has
+     left room for zeroes there anyway.
+   */
+  for (i = first; i < first + count; i++)
+    {
+      GLfloat vert[4] = { 0, };
+      int j, k;
+
+      /* Extract this vertex into `vert' as a float, whatever its type was. */
+      for (j = 0; j < A.size; j++)
+        {
+          switch (A.type) {
+          case GL_SHORT:  vert[j] = ((GLshort *)  verts_in)[j]; break;
+          case GL_INT:    vert[j] = ((GLint *)    verts_in)[j]; break;
+          case GL_FLOAT:  vert[j] = ((GLfloat *)  verts_in)[j]; break;
+          case GL_DOUBLE: vert[j] = ((GLdouble *) verts_in)[j]; break;
+          default: Assert (0, "unknown vertex type"); break;
+          }
+        }
+
+      /* Compute the texture coordinate for this vertex.
+         For GL_OBJECT_LINEAR, these coordinates are static, and can go
+         into the display list.  But for GL_EYE_LINEAR, GL_SPHERE_MAP and
+         GL_REFLECTION_MAP, they depend on the prevailing ModelView matrix,
+         and so need to be computed afresh each time glDrawArrays is called.
+         Unfortunately, our verts and norms are gone by then, dumped down
+         into the VBO and discarded from CPU RAM.  Bleh.
+       */
+      for (j = 0, k = 0; j < countof(tg); j++)
+        {
+          if (! ((state->compiling_list ? state->list_enabled : state->enabled)
+                 & tg[j].flag))
+            continue;
+          switch (tg[j].mode) {
+          case GL_OBJECT_LINEAR:
+            tex_out[k] = dot_product (4, vert, tg[j].plane);
+            break;
+          default:
+            Assert (0, "unimplemented texture mode");
+            break;
+          }
+          k++;
+        }
+
+      /* fprintf (stderr, "%4d: V %-5.1f %-5.1f %-5.1f  T %-5.1f %-5.1f\n",
+               i, vert[0], vert[1], vert[2], tex_out[0], tex_out[1]); */
+
+      /* Move verts_in and tex_out forward to the next vertex by stride. */
+      verts_in += A.stride;
+      tex_out = (GLfloat *) (((char *) tex_out) + tex_stride);
+    }
+
+  jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+  jwzgles_glTexCoordPointer (tcoords, GL_FLOAT, tex_stride,
+                             (GLvoid *) tex_array);
+  free (tex_array);
+}
+
 
 int
 jwzgles_gluBuild2DMipmaps (GLenum target,
@@ -2802,119 +3342,158 @@ jwzgles_glClearDepth (GLfloat d)
 }
 
 
-void
-jwzgles_glEnable (GLuint bit)
+/* When in immediate mode, we store a bit into state->enabled, and also
+   call the real glEnable() / glDisable().
+
+   When recording a list, we store a bit into state->list_enabled instead,
+   so that we can see what the prevailing enablement state will be when
+   the list is run.
+
+   set: 1 = set, -1 = clear, 0 = query.
+*/
+static int
+enable_disable (GLuint bit, int set)
 {
-  Assert (!state->compiling_verts, "glEnable not allowed inside glBegin");
-  if (state->compiling_list)
+  int result = (set > 0);
+  int omitp = 0;
+  int csp = 0;
+  unsigned long flag = 0;
+
+  switch (bit) {
+  case GL_TEXTURE_1D:     /* We implement 1D textures as 2D textures. */
+  case GL_TEXTURE_2D:     flag = ISENABLED_TEXTURE_2D;              break;
+  case GL_TEXTURE_GEN_S:  flag = ISENABLED_TEXTURE_GEN_S; omitp = 1; break;
+  case GL_TEXTURE_GEN_T:  flag = ISENABLED_TEXTURE_GEN_T; omitp = 1; break;
+  case GL_TEXTURE_GEN_R:  flag = ISENABLED_TEXTURE_GEN_R; omitp = 1; break;
+  case GL_TEXTURE_GEN_Q:  flag = ISENABLED_TEXTURE_GEN_Q; omitp = 1; break;
+  case GL_LIGHTING:       flag = ISENABLED_LIGHTING;                break;
+  case GL_BLEND:          flag = ISENABLED_BLEND;                   break;
+  case GL_DEPTH_TEST:     flag = ISENABLED_DEPTH_TEST;              break;
+  case GL_CULL_FACE:      flag = ISENABLED_CULL_FACE;               break;
+  case GL_NORMALIZE:      flag = ISENABLED_NORMALIZE;               break;
+  case GL_FOG:            flag = ISENABLED_FOG;                             break;
+  case GL_COLOR_MATERIAL: flag = ISENABLED_COLMAT;                  break;
+
+  /* Maybe technically these only work with glEnableClientState,
+     but we treat that as synonymous with glEnable. */
+  case GL_VERTEX_ARRAY:   flag = ISENABLED_VERT_ARRAY;     csp = 1;  break;
+  case GL_NORMAL_ARRAY:   flag = ISENABLED_NORM_ARRAY;     csp = 1;  break;
+  case GL_COLOR_ARRAY:    flag = ISENABLED_COLOR_ARRAY;    csp = 1;  break;
+  case GL_TEXTURE_COORD_ARRAY: flag = ISENABLED_TEX_ARRAY; csp = 1;  break;
+
+  default:
+    Assert (set != 0, "glIsEnabled unimplemented bit");
+    break;
+  }
+
+  if (set)  /* setting or unsetting, not querying */
     {
-      void_int vv[1];
-      vv[0].i = bit;
-      list_push ("glEnable", (list_fn_cb) &jwzgles_glEnable, PROTO_I, vv);
+      const char *fns[4] = { "glEnable", "glDisable",
+                             "glEnableClientState", "glDisableClientState" };
+      list_fn_cb fs[4] = { (list_fn_cb) &jwzgles_glEnable,
+                           (list_fn_cb) &jwzgles_glDisable,
+                           (list_fn_cb) &jwzgles_glEnableClientState,
+                           (list_fn_cb) &jwzgles_glDisableClientState };
+      const char *fn = fns[(csp ? 2 : 0) + (set < 0 ? 1 : 0)];
+      list_fn_cb  f  =  fs[(csp ? 2 : 0) + (set < 0 ? 1 : 0)];
+
+      Assert (!state->compiling_verts,
+              "glEnable/glDisable not allowed inside glBegin");
+
+      if (state->compiling_list)
+        {
+          void_int vv[1];
+          vv[0].i = bit;
+          list_push (fn, f,PROTO_I, vv);
+        }
+
+      if (! state->replaying_list &&
+          ! state->compiling_list)
+        LOG2 ("direct %-12s %s", fn, mode_desc(bit));
+
+      if (csp && !state->compiling_verts)
+        {
+          if (set > 0)
+            switch (bit) {
+            case GL_NORMAL_ARRAY: state->set.ncount        += 2; break;
+            case GL_TEXTURE_COORD_ARRAY: state->set.tcount += 2; break;
+            case GL_COLOR_ARRAY: state->set.ccount         += 2; break;
+            default: break;
+            }
+          else
+            switch (bit) {
+            case GL_NORMAL_ARRAY: state->set.ncount        = 0; break;
+            case GL_TEXTURE_COORD_ARRAY: state->set.tcount = 0; break;
+            case GL_COLOR_ARRAY: state->set.ccount         = 0; break;
+            default: break;
+            }
+        }
+
+      if (omitp || state->compiling_list)
+        ;
+      else if (set > 0 && csp)
+        glEnableClientState (bit);     /* the real one */
+      else if (set < 0 && csp)
+        glDisableClientState (bit);    /* the real one */
+      else if (set > 0)
+        glEnable (bit);                        /* the real one */
+      else
+        glDisable (bit);               /* the real one */
+
+      CHECK(fn);
     }
-  else
+
+  /* Store the bit in our state as well, or query it.
+   */
+  if (flag)
     {
-      if (! state->replaying_list)
-        LOG2 ("direct %-12s %s", "glEnable", mode_desc(bit));
-      glEnable (bit);  /* the real one */
-      CHECK("glEnable");
-
-      switch (bit) {
-      case GL_TEXTURE_2D: state->enabled |= ISENABLED_TEXTURE_2D; break;
-      case GL_TEXTURE_GEN_S: state->enabled |= ISENABLED_TEXTURE_GEN_S; break;
-      case GL_TEXTURE_GEN_T: state->enabled |= ISENABLED_TEXTURE_GEN_T; break;
-      case GL_LIGHTING: state->enabled |= ISENABLED_LIGHTING; break;
-      case GL_BLEND: state->enabled |= ISENABLED_BLEND; break;
-      case GL_DEPTH_TEST: state->enabled |= ISENABLED_DEPTH_TEST; break;
-      case GL_CULL_FACE: state->enabled |= ISENABLED_CULL_FACE; break;
-      case GL_NORMALIZE: state->enabled |= ISENABLED_NORMALIZE; break;
-      case GL_FOG: state->enabled |= ISENABLED_FOG; break;
-      case GL_COLOR_MATERIAL: state->enabled |= ISENABLED_COLMAT; break;
-
-      /* Do these work with glEnable or only with glEnableClientState? */
-      case GL_VERTEX_ARRAY: state->enabled |= ISENABLED_VERT_ARRAY; break;
-      case GL_NORMAL_ARRAY: state->enabled |= ISENABLED_NORM_ARRAY; break;
-      case GL_TEXTURE_COORD_ARRAY: state->enabled |= ISENABLED_TEX_ARRAY;break;
-      case GL_COLOR_ARRAY:  state->enabled |= ISENABLED_COLOR_ARRAY; break;
-
-      default: break;
-      }
+      unsigned long *enabled = (state->compiling_list
+                                ? &state->list_enabled
+                                : &state->enabled);
+      if (set > 0)
+        *enabled |= flag;
+      else if (set < 0)
+        *enabled &= ~flag;
+      else
+        result = !!(*enabled & flag);
     }
+
+  return result;
 }
 
 
 void
-jwzgles_glDisable (GLuint bit)
+jwzgles_glEnable (GLuint bit)
 {
-  Assert (!state->compiling_verts, "glDisable not allowed inside glBegin");
-  if (state->compiling_list)
-    {
-      void_int vv[1];
-      vv[0].i = bit;
-      list_push ("glDisable", (list_fn_cb) &jwzgles_glDisable, PROTO_I, vv);
-    }
-  else
-    {
-      if (! state->replaying_list)
-        LOG2 ("direct %-12s %s", "glDisable", mode_desc(bit));
-      glDisable (bit);  /* the real one */
-      CHECK("glDisable");
-
-      switch (bit) {
-      case GL_TEXTURE_2D: state->enabled &= ~ISENABLED_TEXTURE_2D; break;
-      case GL_TEXTURE_GEN_S: state->enabled &= ~ISENABLED_TEXTURE_GEN_S; break;
-      case GL_TEXTURE_GEN_T: state->enabled &= ~ISENABLED_TEXTURE_GEN_T; break;
-      case GL_LIGHTING: state->enabled &= ~ISENABLED_LIGHTING; break;
-      case GL_BLEND: state->enabled &= ~ISENABLED_BLEND; break;
-      case GL_DEPTH_TEST: state->enabled &= ~ISENABLED_DEPTH_TEST; break;
-      case GL_CULL_FACE: state->enabled &= ~ISENABLED_CULL_FACE; break;
-      case GL_NORMALIZE: state->enabled &= ~ISENABLED_NORMALIZE; break;
-      case GL_FOG: state->enabled &= ~ISENABLED_FOG; break;
-      case GL_COLOR_MATERIAL: state->enabled &= ~ISENABLED_COLMAT; break;
-
-      /* Do these work with glEnable or only with glEnableClientState? */
-      case GL_VERTEX_ARRAY: state->enabled &= ~ISENABLED_VERT_ARRAY; break;
-      case GL_NORMAL_ARRAY: state->enabled &= ~ISENABLED_NORM_ARRAY; break;
-      case GL_TEXTURE_COORD_ARRAY: state->enabled &= ~ISENABLED_TEX_ARRAY;break;
-      case GL_COLOR_ARRAY:  state->enabled &= ~ISENABLED_COLOR_ARRAY; break;
-
-      default: break;
-      }
-    }
+  enable_disable (bit, 1);
 }
 
+void
+jwzgles_glDisable (GLuint bit)
+{
+  enable_disable (bit, -1);
+}
 
 GLboolean
 jwzgles_glIsEnabled (GLuint bit)
 {
- /*
-  Assert (!state->compiling_verts, "glIsEnabled not allowed inside glBegin");
-  Assert (!state->compiling_list,  "glIsEnabled not allowed inside glNewList");
-  */
-  switch (bit) {
-  case GL_TEXTURE_2D: return !!(state->enabled & ISENABLED_TEXTURE_2D);
-  case GL_TEXTURE_GEN_S: return !!(state->enabled & ISENABLED_TEXTURE_GEN_S);
-  case GL_TEXTURE_GEN_T: return !!(state->enabled & ISENABLED_TEXTURE_GEN_T);
-  case GL_LIGHTING: return !!(state->enabled & ISENABLED_LIGHTING);
-  case GL_BLEND: return !!(state->enabled & ISENABLED_BLEND);
-  case GL_DEPTH_TEST: return !!(state->enabled & ISENABLED_DEPTH_TEST);
-  case GL_CULL_FACE: return !!(state->enabled & ISENABLED_CULL_FACE);
-  case GL_NORMALIZE: return !!(state->enabled & ISENABLED_NORMALIZE);
-  case GL_FOG: return !!(state->enabled & ISENABLED_FOG);
-  case GL_COLOR_MATERIAL: return !!(state->enabled & ISENABLED_COLMAT);
-
-  /* Do these work with glEnable or only with glEnableClientState?
-     We need to query them, and there is no glIsClientStateEnabled.
-   */
-  case GL_VERTEX_ARRAY: return !!(state->enabled & ISENABLED_VERT_ARRAY);
-  case GL_NORMAL_ARRAY: return !!(state->enabled & ISENABLED_NORM_ARRAY);
-  case GL_TEXTURE_COORD_ARRAY: return !!(state->enabled & ISENABLED_TEX_ARRAY);
-  case GL_COLOR_ARRAY: return !!(state->enabled & ISENABLED_COLOR_ARRAY);
-  default: Assert (0, "glIsEnabled unimplemented bit"); break;
-  }
+  return enable_disable (bit, 0);
+}
+
+void
+jwzgles_glEnableClientState (GLuint cap)
+{
+  enable_disable (cap, 1);
+}
+
+void
+jwzgles_glDisableClientState (GLuint cap)
+{
+  enable_disable (cap, -1);
 }
 
 
+
 /* The spec says that OpenGLES 1.x doesn't implement glGetFloatv.
    Were this true, it would suck, for it would mean that there was no
    way to retrieve the prevailing matrixes.  To implement this, we'd
@@ -3041,8 +3620,10 @@ jwzgles_gluErrorString (GLenum error)
 }
 
 
-/* These can be included inside glNewList, but they actually execute
-   immediately anyway. 
+/* These four *Pointer calls (plus glBindBuffer and glBufferData) can
+   be included inside glNewList, but they actually execute immediately
+   anyway, because their data is recorded in the list by the
+   subsequently-recorded call to glDrawArrays.  This is a little weird.
  */
 void
 jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride, 
@@ -3055,6 +3636,7 @@ jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride,
   CHECK("glVertexPointer");
 }
 
+
 void
 jwzgles_glNormalPointer (GLuint type, GLuint stride, const GLvoid *ptr)
 {
@@ -3087,6 +3669,26 @@ jwzgles_glTexCoordPointer (GLuint size, GLuint type, GLuint stride,
   CHECK("glTexCoordPointer");
 }
 
+void
+jwzgles_glBindBuffer (GLuint target, GLuint buffer)
+{
+  if (! state->replaying_list)
+    LOG3 ("direct %-12s %s %d", "glBindBuffer", mode_desc(target), buffer);
+  glBindBuffer (target, buffer);  /* the real one */
+  CHECK("glBindBuffer");
+}
+
+void
+jwzgles_glBufferData (GLenum target, GLsizeiptr size, const void *data,
+                      GLenum usage)
+{
+  if (! state->replaying_list)
+    LOG5 ("direct %-12s %s %ld 0x%lX %s", "glBufferData",
+          mode_desc(target), size, (unsigned long) data, mode_desc(usage));
+  glBufferData (target, size, data, usage);  /* the real one */
+  CHECK("glBufferData");
+}
+
 
 void
 jwzgles_glTexParameterf (GLuint target, GLuint pname, GLfloat param)
@@ -3094,13 +3696,20 @@ jwzgles_glTexParameterf (GLuint target, GLuint pname, GLfloat param)
   Assert (!state->compiling_verts,
           "glTexParameterf not allowed inside glBegin");
 
-  /* We don't *really* implement mipmaps, so just turn this off.
-   */
+  /* We don't *really* implement mipmaps, so just turn this off. */
   if (param == GL_LINEAR_MIPMAP_LINEAR)   param = GL_LINEAR;
   if (param == GL_NEAREST_MIPMAP_LINEAR)  param = GL_LINEAR;
   if (param == GL_LINEAR_MIPMAP_NEAREST)  param = GL_NEAREST;
   if (param == GL_NEAREST_MIPMAP_NEAREST) param = GL_NEAREST;
 
+  /* We implement 1D textures as 2D textures. */
+  if (target == GL_TEXTURE_1D) target = GL_TEXTURE_2D;
+
+  /* Apparently this is another invalid enum. Just ignore it. */
+  if ((pname == GL_TEXTURE_WRAP_S || pname == GL_TEXTURE_WRAP_T) &&
+      param == GL_CLAMP)
+    return;
+
   if (state->compiling_list)
     {
       void_int vv[3];
@@ -3127,6 +3736,37 @@ jwzgles_glTexParameteri (GLuint target, GLuint pname, GLuint param)
 }
 
 
+void
+jwzgles_glBindTexture (GLuint target, GLuint texture)
+{
+  Assert (!state->compiling_verts,
+          "glBindTexture not allowed inside glBegin");
+
+  /* We implement 1D textures as 2D textures. */
+  if (target == GL_TEXTURE_1D) target = GL_TEXTURE_2D;
+
+  if (state->compiling_list)
+    {
+      void_int vv[2];
+      vv[0].i = target;
+      vv[1].i = texture;
+      list_push ("glBindTexture", (list_fn_cb) &jwzgles_glBindTexture,
+                 PROTO_II, vv);
+    }
+
+  /* Do it immediately as well, for generate_texture_coords */
+  /* else */
+    {
+      if (! state->replaying_list)
+        LOG3 ("direct %-12s %s %d", "glBindTexture", 
+              mode_desc(target), texture);
+      glBindTexture (target, texture);  /* the real one */
+      CHECK("glBindTexture");
+    }
+}
+
+
+
 /* Matrix functions, mostly cribbed from Mesa.
  */
 
@@ -3463,7 +4103,6 @@ void jwzgles_##NAME (ARGS_##SIG)                                  \
 
 WRAP (glActiveTexture, I)
 WRAP (glAlphaFunc,     IF)
-WRAP (glBindTexture,   II)
 WRAP (glBlendFunc,     II)
 WRAP (glClear,         I)
 WRAP (glClearColor,    FFFF)