From http://www.jwz.org/xscreensaver/xscreensaver-5.32.tar.gz
[xscreensaver] / hacks / glx / jwzgles.c
index e10d2e6ed623328868b6b2dcb1cb2ccdc09b2ec7..ff915a021064510f2f10bbf5ce3d507fb11e363a 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
     - gluNewQuadric, gluCylinder, etc: rewrite your code to use tube.c, etc.
 
     - Putting verts in a display list without a wrapping glBegin.
-      (I didn't realize that even worked!)
+      I didn't realize that even worked!  Lockward used to do that,
+      before I fixed it to not.
 
     - Not every function is implemented; just the ones that I needed for
       xscreensaver.  However, the trivial ones are trivial to enable
 
    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
+#elif defined(HAVE_ANDROID)
+# include <GLES/gl.h>
+#else /* real 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 +236,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 +274,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 +294,26 @@ 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)
+#define ISENABLED_ALPHA_TEST   (1<<16)
+
+
+typedef struct {
+  GLuint mode;
+  GLfloat obj[4], eye[4];
+} texgen_state;
 
 
 typedef struct {       /* global state */
@@ -299,13 +325,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 +378,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 +396,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 +407,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 +421,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 +454,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 +478,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 +494,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 +514,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 +543,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 +576,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 +636,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 +674,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 +694,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 +714,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 +749,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 +906,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 +953,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 +1003,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 +1076,7 @@ jwzgles_glTexCoord1f (GLfloat s)
 }
 
 
+/* glColor: GLfloat */
 
 void
 jwzgles_glColor4fv (const GLfloat *v)
@@ -1018,15 +1099,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,77 +1129,230 @@ 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 +1792,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 +1812,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 +1830,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 +1859,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 +1904,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 +1927,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 +2097,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 +2265,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 +2278,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 +2289,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 +2301,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 +2325,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 +2402,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 +2417,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 +2426,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 +2436,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 +2459,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 +2512,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 +2531,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 +2566,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 +2815,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 +2932,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 +2980,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 +3035,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.
-
-     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!
+  texgen_state *s;
 
-     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 +3079,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,118 +3344,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_ALPHA_TEST:     flag = ISENABLED_ALPHA_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
@@ -3041,8 +3623,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 +3639,7 @@ jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride,
   CHECK("glVertexPointer");
 }
 
+
 void
 jwzgles_glNormalPointer (GLuint type, GLuint stride, const GLvoid *ptr)
 {
@@ -3087,6 +3672,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 +3699,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 +3739,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.
  */
 
@@ -3334,6 +3977,15 @@ jwzgles_gluProject (GLdouble objx, GLdouble objy, GLdouble objz,
 }
 
 
+void jwzgles_glViewport (GLuint x, GLuint y, GLuint w, GLuint h)
+{
+# if TARGET_IPHONE_SIMULATOR
+/*  fprintf (stderr, "glViewport %dx%d\n", w, h); */
+# endif
+  glViewport (x, y, w, h);  /* the real one */
+}
+
+
 /* The following functions are present in both OpenGL 1.1 and in OpenGLES 1,
    but are allowed within glNewList/glEndList, so we must wrap them to allow
    them to either be recorded in lists, or run directly.
@@ -3463,7 +4115,6 @@ void jwzgles_##NAME (ARGS_##SIG)                                  \
 
 WRAP (glActiveTexture, I)
 WRAP (glAlphaFunc,     IF)
-WRAP (glBindTexture,   II)
 WRAP (glBlendFunc,     II)
 WRAP (glClear,         I)
 WRAP (glClearColor,    FFFF)
@@ -3501,7 +4152,6 @@ WRAP (glStencilOp,        III)
 WRAP (glTexEnvf,       IIF)
 WRAP (glTexEnvi,       III)
 WRAP (glTranslatef,    FFF)
-WRAP (glViewport,      IIII)
 #undef  TYPE_IV
 #define TYPE_IV GLuint
 WRAP (glDeleteTextures,        IIV)