X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fjwzgles.c;h=325e4654b4b597527d1c222ccf849c2bfbd4a8d9;hb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e;hp=15284ca801a384ed2b019cff54b00309f9827031;hpb=c70f94f648d51bb4828193124f325fa52b0e57f3;p=xscreensaver diff --git a/hacks/glx/jwzgles.c b/hacks/glx/jwzgles.c index 15284ca8..325e4654 100644 --- a/hacks/glx/jwzgles.c +++ b/hacks/glx/jwzgles.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2012 Jamie Zawinski +/* xscreensaver, Copyright (c) 2012-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -126,17 +126,18 @@ 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. + 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. @@ -167,7 +168,10 @@ #elif defined(HAVE_COCOA) # include # include -#else +#else /* X11 */ +# ifndef GL_GLEXT_PROTOTYPES +# define GL_GLEXT_PROTOTYPES /* for glBindBuffer */ +# endif # include # include #endif @@ -180,11 +184,17 @@ #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,77 +1127,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_glColor3i (GLuint r, GLuint g, GLuint b) +jwzgles_glColor3d (GLdouble r, GLdouble g, GLdouble b) { - jwzgles_glColor4f (r, g, b, 1); + jwzgles_glColor4d (r, g, b, 1.0); +} + +void +jwzgles_glColor3dv (const GLdouble *v) +{ + 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) { @@ -2671,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 @@ -2704,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, @@ -2807,118 +3342,157 @@ 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 @@ -3046,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, @@ -3060,6 +3636,7 @@ jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride, CHECK("glVertexPointer"); } + void jwzgles_glNormalPointer (GLuint type, GLuint stride, const GLvoid *ptr) { @@ -3092,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) @@ -3108,6 +3705,11 @@ jwzgles_glTexParameterf (GLuint target, GLuint pname, GLfloat param) /* 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]; @@ -3151,7 +3753,9 @@ jwzgles_glBindTexture (GLuint target, GLuint texture) list_push ("glBindTexture", (list_fn_cb) &jwzgles_glBindTexture, PROTO_II, vv); } - else + + /* Do it immediately as well, for generate_texture_coords */ + /* else */ { if (! state->replaying_list) LOG3 ("direct %-12s %s %d", "glBindTexture",