X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fjwzgles.c;fp=hacks%2Fglx%2Fjwzgles.c;h=e10d2e6ed623328868b6b2dcb1cb2ccdc09b2ec7;hb=f8cf5ac7b2f53510f80a0eaf286a25298be17bfe;hp=0000000000000000000000000000000000000000;hpb=ec8d2b32b63649e6d32bdfb306eda062769af823;p=xscreensaver diff --git a/hacks/glx/jwzgles.c b/hacks/glx/jwzgles.c new file mode 100644 index 00000000..e10d2e6e --- /dev/null +++ b/hacks/glx/jwzgles.c @@ -0,0 +1,3510 @@ +/* xscreensaver, Copyright (c) 2012 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 + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* A compatibility shim to allow OpenGL 1.3 source code to work in an + OpenGLES environment, where almost every OpenGL 1.3 function has + been "deprecated". + + There are two major operations going on here: + + - Converting calls to glBegin + glVertex3f + glEnd to glDrawArrays + - Implementing display lists. + + + From an API point of view, OpenGL 1.3 and earlier code looks like this: + + glLightfv (GL_LIGHT0, GL_POSITION, ...); + glLightfv (GL_LIGHT0, GL_AMBIENT, ...); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + gluPerspective (...); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + gluLookAt (...); + + glPushMatrix (); + + glRotatef (...); + + glColor3f (...); + + glBegin (GL_TRIANGLES); + glNormal3f (...); + glVertex3f (...); + glVertex3f (...); + glVertex3f (...); + glEnd (); + + glPopMatrix (); + + glFinish (); + + + OpenGLES broke that model by eliminating glBegin(). Instead of + iterating a sequence of vertexes, you need to pack your points into + an array first, e.g.: + + GLfloat coords[] = { + 0, 0, 0, + 0, 1, 0, + ... + }; + + glDrawArrays (GL_TRIANGLES, 0, 3); + + The projection model (glRotatef, etc.) works the same, but glColor() + is missing. You're expected to encode that into your arrays. + + Also, OpenGLES doesn't support display lists at all. + + + So this code shadows all of the functions that are allowed within + glBegin, builds up an array, and calls glDrawArrays at the end. + + Likewise, it shadows all of the functions that are allowed within + glNewList and records those calls for later playback. + + + This code only handles OpenGLES 1.x, not 2.x. + + OpenGLES 2.0 broke things further by eliminating the whole OpenGL + lighting model. Instead of specifying the positions and properties + of your lights using the glLight* API, now you are expected to + implement it all yourself by downloading C-like code into the GPU + directly. This is more flexible, in that if you wanted a completely + different lighting model than what OpenGL provides, you could do + that, but it leaves you needing to download boilerplate to reproduce + what used to be built in. + + + Incidentally, the OpenGL numbering scheme goes something like this: + + OpenGL 1.0 1992 + OpenGL 1.1 1997 (improved texture support) + OpenGL 1.2 1998 (nothing interesting) + OpenGL 1.3 2001 (multisampling, cubemaps) + OpenGL 1.4 2002 (added auto-mipmapping) + OpenGLES 1.0 2003 (deprecated 80% of the language; fork of OpenGL 1.3) + OpenGL 1.5 2003 (added VBOs) + OpenGLES 1.1 2004 (fork of OpenGL 1.5) + OpenGL 2.0 2004 (a political quagmire) + OpenGLES 2.0 2007 (deprecated 95% of the language; fork of OpenGL 2.0) + OpenGL 3.0 2008 (added FBOs, VAOs, deprecated 60% of the language) + + + Some things that are missing: + + - glTexGeni, meaning no spherical environment-mapping textures. + + - gluNewTess, meaning no tesselation of complex objects. + + - glMap2f mesh evaluators, meaning no Utah Teapot. + + - glPolygonMode with GL_LINE or GL_POINT, meaning no wireframe modes + that do hidden-surface removal. + + - glSelectBuffer, meaning no mouse-hit detection on rendered objects. + + - 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!) + + - Not every function is implemented; just the ones that I needed for + xscreensaver. However, the trivial ones are trivial to enable + as they come up. Harder ones will be harder. + + As a result of that, these savers look wrong: + + 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. + 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. + timetunnel Uses GL_CONSTANT_ALPHA and all kinds of other stuff. + +*/ + + +#undef DEBUG + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_JWZGLES /* whole file */ + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if defined(USE_IPHONE) +# include +# include +#elif defined(HAVE_COCOA) +# include +# include +#else +# include +# include +#endif + +#include "jwzglesI.h" + +#define STRINGIFY(X) #X + +#undef countof +#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) + + +typedef struct { GLfloat x, y, z; } XYZ; +typedef struct { GLfloat x, y, z, w; } XYZW; +typedef struct { GLfloat s, t, r, q; } STRQ; +typedef struct { GLfloat r, g, b, a; } RGBA; + + +/* Used to record all calls to glVertex3f, glNormal3f, etc. + while inside glBegin / glEnd so that we can convert that + to a single call to glDrawArrays. + */ +typedef struct { + int mode; + int count, size; /* size of each array */ + + XYZW *verts; /* Arrays being built */ + XYZ *norms; + STRQ *tex; + RGBA *color; + + int ncount; /* How many normals, tex coords and colors were */ + int tcount; /* used. We optimize based on "0, 1, or many". */ + int ccount; + int materialistic; /* Whether glMaterial was called inside glBegin */ + + XYZ cnorm; /* Prevailing normal/texture/color while building */ + STRQ ctex; + RGBA ccolor; + +} vert_set; + + +typedef void (*list_fn_cb) (void); + + +/* We need this nonsense because you can't cast a double to a 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 struct { /* saved args for glDrawArrays */ + int size, type, stride, bytes; + void *data; +} draw_array; + +typedef enum { /* shorthand describing arglist signature */ + PROTO_VOID, /* no args */ + PROTO_I, /* 1 int arg */ + PROTO_F, /* 1 float arg */ + PROTO_II, /* int, int */ + PROTO_FF, /* float, float */ + PROTO_IF, /* int, float */ + PROTO_III, /* int, int, int */ + PROTO_FFF, /* float, float, float */ + PROTO_IIF, /* int, int, float */ + PROTO_IIII, /* int, int, int, int */ + PROTO_FFFF, /* float, float, float, float */ + PROTO_IIV, /* int, int[4] */ + PROTO_IFV, /* int, float[4] */ + PROTO_IIIV, /* int, int, int[4] */ + PROTO_IIFV, /* int, int, float[4] */ + PROTO_FV16, /* float[16] */ + PROTO_ARRAYS /* glDrawArrays */ +} fn_proto; + +typedef struct { /* A single element of a display list */ + const char *name; + list_fn_cb fn; /* saved function pointer */ + fn_proto proto; /* arglist prototype */ + draw_array *arrays; /* args for glDrawArrays */ + void_int argv[16]; /* args for everything else */ +} list_fn; + + +typedef struct { /* saved activity within glNewList */ + int id; + int size, count; + list_fn *fns; +} list; + + +typedef struct { /* A display list */ + list *lists; + int count, size; +} list_set; + + +#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) + + +typedef struct { /* global state */ + + vert_set set; /* set being built */ + + int compiling_list; /* list id if inside glNewList; 0 means immediate */ + int replaying_list; /* depth of call stack to glCallList */ + int compiling_verts; /* inside glBegin */ + + list_set lists; /* saved lists */ + unsigned long enabled; + +} jwzgles_state; + + +static jwzgles_state global_state = { { 0, }, 0, 0, 0, { 0, }, 0, }; +static jwzgles_state *state = &global_state; + + +#ifdef DEBUG +# define LOG(A) fprintf(stderr,"jwzgles: " A "\n") +# define LOG1(A,B) fprintf(stderr,"jwzgles: " A "\n",B) +# define LOG2(A,B,C) fprintf(stderr,"jwzgles: " A "\n",B,C) +# define LOG3(A,B,C,D) fprintf(stderr,"jwzgles: " A "\n",B,C,D) +# define LOG4(A,B,C,D,E) fprintf(stderr,"jwzgles: " A "\n",B,C,D,E) +# define LOG5(A,B,C,D,E,F) fprintf(stderr,"jwzgles: " A "\n",B,C,D,E,F) +# define LOG6(A,B,C,D,E,F,G) fprintf(stderr,"jwzgles: " A "\n",B,C,D,E,F,G) +# define LOG7(A,B,C,D,E,F,G,H) fprintf(stderr,"jwzgles: " A "\n",B,C,D,E,F,G,H) +# define LOG8(A,B,C,D,E,F,G,H,I)\ + fprintf(stderr,"jwzgles: "A "\n",B,C,D,E,F,G,H,I) +# define LOG9(A,B,C,D,E,F,G,H,I,J)\ + fprintf(stderr,"jwzgles: "A "\n",B,C,D,E,F,G,H,I,J) +# define LOG10(A,B,C,D,E,F,G,H,I,J,K)\ + fprintf(stderr,"jwzgles: "A "\n",B,C,D,E,F,G,H,I,J,K) +# define LOG17(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R)\ + fprintf(stderr,"jwzgles: "A "\n",B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) +# define CHECK(S) check_gl_error(S) +#else +# define LOG(A) /* */ +# define LOG1(A,B) /* */ +# define LOG2(A,B,C) /* */ +# define LOG3(A,B,C,D) /* */ +# define LOG4(A,B,C,D,E) /* */ +# define LOG5(A,B,C,D,E,F) /* */ +# define LOG6(A,B,C,D,E,F,G) /* */ +# define LOG7(A,B,C,D,E,F,G,H) /* */ +# define LOG8(A,B,C,D,E,F,G,H,I) /* */ +# define LOG9(A,B,C,D,E,F,G,H,I,J) /* */ +# define LOG10(A,B,C,D,E,F,G,H,I,J,K) /* */ +# define LOG17(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) /* */ +# define CHECK(S) /* */ +#endif + +#ifdef DEBUG +static const char * +mode_desc (int mode) /* for debugging messages */ +{ + switch (mode) { +# define SS(X) case GL_##X: return STRINGIFY(X); + SS(ALPHA) + SS(AMBIENT) + SS(AMBIENT_AND_DIFFUSE) + 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) + SS(C4UB_V2F) + SS(C4UB_V3F) + SS(CCW) + SS(CLAMP) + SS(COLOR_ARRAY) + SS(COLOR_MATERIAL) + SS(COLOR_MATERIAL_FACE) + SS(COLOR_MATERIAL_PARAMETER) + SS(COMPILE) + SS(CULL_FACE) + SS(CW) + SS(DECAL) + SS(DEPTH_BUFFER_BIT) + SS(DEPTH_TEST) + SS(DIFFUSE) + SS(DST_ALPHA) + SS(DST_COLOR) + SS(EYE_LINEAR) + SS(EYE_PLANE) + SS(FEEDBACK) + SS(FILL) + SS(FLAT) + SS(FLOAT) + SS(FOG) + SS(FRONT) + SS(FRONT_AND_BACK) + SS(INTENSITY) + SS(INVALID_ENUM) + SS(INVALID_OPERATION) + SS(INVALID_VALUE) + SS(LIGHT0) + SS(LIGHT1) + SS(LIGHT2) + SS(LIGHT3) + SS(LIGHTING) + SS(LIGHT_MODEL_AMBIENT) + SS(LIGHT_MODEL_COLOR_CONTROL) + SS(LIGHT_MODEL_LOCAL_VIEWER) + SS(LIGHT_MODEL_TWO_SIDE) + SS(LINE) + SS(LINEAR) + SS(LINEAR_MIPMAP_LINEAR) + SS(LINEAR_MIPMAP_NEAREST) + SS(LINES) + SS(LINE_LOOP) + SS(LINE_STRIP) + SS(LUMINANCE) + SS(LUMINANCE_ALPHA) + SS(MATRIX_MODE) + SS(MODELVIEW) + SS(MODULATE) + SS(N3F_V3F) + SS(NEAREST) + SS(NEAREST_MIPMAP_LINEAR) + SS(NEAREST_MIPMAP_NEAREST) + SS(NORMALIZE) + SS(NORMAL_ARRAY) + SS(OBJECT_LINEAR) + SS(OBJECT_PLANE) + SS(ONE_MINUS_DST_ALPHA) + SS(ONE_MINUS_DST_COLOR) + SS(ONE_MINUS_SRC_ALPHA) + SS(ONE_MINUS_SRC_COLOR) + SS(OUT_OF_MEMORY) + SS(PACK_ALIGNMENT) + SS(POINTS) + SS(POLYGON) + SS(POLYGON_OFFSET_FILL) + SS(POLYGON_SMOOTH) + SS(POLYGON_STIPPLE) + SS(POSITION) + SS(PROJECTION) + SS(Q) + SS(QUADS) + SS(QUAD_STRIP) + SS(R) + SS(RENDER) + SS(REPEAT) + SS(RGB) + SS(RGBA) + SS(S) + SS(SELECT) + SS(SEPARATE_SPECULAR_COLOR) + SS(SHADE_MODEL) + SS(SHININESS) + SS(SHORT) + SS(SINGLE_COLOR) + SS(SMOOTH) + SS(SPECULAR) + SS(SPHERE_MAP) + SS(SRC_ALPHA) + SS(SRC_ALPHA_SATURATE) + SS(SRC_COLOR) + SS(STACK_OVERFLOW) + SS(STACK_UNDERFLOW) + SS(STENCIL_BUFFER_BIT) + SS(T) + SS(T2F_C3F_V3F) + SS(T2F_C4F_N3F_V3F) + SS(T2F_C4UB_V3F) + SS(T2F_N3F_V3F) + SS(T2F_V3F) + SS(T4F_C4F_N3F_V4F) + SS(T4F_V4F) + SS(TEXTURE) + SS(TEXTURE_1D) + SS(TEXTURE_2D) + SS(TEXTURE_ALPHA_SIZE) + SS(TEXTURE_BINDING_2D) + SS(TEXTURE_BLUE_SIZE) + SS(TEXTURE_BORDER) + SS(TEXTURE_BORDER_COLOR) + SS(TEXTURE_COMPONENTS) + SS(TEXTURE_COORD_ARRAY) + SS(TEXTURE_ENV) + SS(TEXTURE_ENV_COLOR) + SS(TEXTURE_ENV_MODE) + SS(TEXTURE_GEN_MODE) + SS(TEXTURE_GEN_Q) + SS(TEXTURE_GEN_R) + SS(TEXTURE_GEN_S) + SS(TEXTURE_GEN_T) + SS(TEXTURE_GREEN_SIZE) + SS(TEXTURE_HEIGHT) + SS(TEXTURE_INTENSITY_SIZE) + SS(TEXTURE_LUMINANCE_SIZE) + SS(TEXTURE_MAG_FILTER) + SS(TEXTURE_MIN_FILTER) + SS(TEXTURE_RED_SIZE) + SS(TEXTURE_WRAP_S) + SS(TEXTURE_WRAP_T) + SS(TRIANGLES) + SS(TRIANGLE_FAN) + SS(TRIANGLE_STRIP) + SS(UNPACK_ALIGNMENT) + SS(UNPACK_ROW_LENGTH) + SS(UNSIGNED_BYTE) + SS(UNSIGNED_INT_8_8_8_8_REV) + SS(UNSIGNED_SHORT) + SS(V2F) + SS(V3F) + SS(VERTEX_ARRAY) +/*SS(COLOR_BUFFER_BIT) -- same value as GL_LIGHT0 */ +# undef SS + case (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT): + return "DEPTH_BUFFER_BIT | COLOR_BUFFER_BIT"; +/* Oops, same as INVALID_ENUM. + case (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT): + return "DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT"; +*/ + case (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT): + return "COLOR_BUFFER_BIT | STENCIL_BUFFER_BIT"; + case (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT): + return "DEPTH_BUFFER_BIT | COLOR_BUFFER_BIT | STENCIL_BUFFER_BIT"; + default: + { + static char buf[255]; + sprintf (buf, "0x%04X", mode); + return buf; + } + } +} + +static void +check_gl_error (const char *s) +{ + GLenum i = glGetError(); + if (i == GL_NO_ERROR) return; + fprintf (stderr, "jwzgles: GL ERROR: %s: %s\n", s, mode_desc(i)); +} + +#endif /* DEBUG */ + + +int +jwzgles_glGenLists (int n) +{ + int i; + int ret = 0; + + Assert (!state->compiling_verts, "glGenLists not allowed inside glBegin"); + + /* Ensure space in state->lists, clear the one at the end, and tick counter + Note that lists are never really deleted, and we can never re-use elements + of this array. glDeleteLists zeroes out the contents of the list, but + the list ID is still valid for use with glNewList forever. + #### So maybe this should be a linked list instead of an array. + */ + for (i = 0; i < n; i++) + { + 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); + } + state->lists.count++; + id = state->lists.count; + L = &state->lists.lists[id-1]; + + memset (L, 0, sizeof (*L)); + L->id = id; + if (ret == 0) ret = id; + LOG1("glGenLists -> %d", L->id); + } + + /* Return the ID of the first list allocated */ + + return ret; +} + + +void +jwzgles_glNewList (int id, int mode) +{ + list *L; + Assert (id > 0 && id <= state->lists.count, "glNewList: bogus ID"); + Assert (mode == GL_COMPILE, "glNewList: bad mode"); + Assert (!state->compiling_verts, "glNewList not allowed inside glBegin"); + Assert (!state->compiling_list, "nested glNewList"); + Assert (state->set.count == 0, "missing glEnd"); + + L = &state->lists.lists[id-1]; + Assert (L->id == id, "glNewList corrupted"); + + if (L->count != 0) jwzgles_glDeleteLists (L->id, 1); /* Overwriting */ + Assert (L->count == 0, "glNewList corrupted"); + + state->compiling_list = id; + + LOG1("glNewList -> %d", id); +} + + +void +jwzgles_glEndList (void) +{ + Assert (state->compiling_list, "extra glEndList"); + Assert (state->set.count == 0, "missing glEnd"); + Assert (!state->compiling_verts, "glEndList not allowed inside glBegin"); + LOG1("glEndList %d", state->compiling_list); + state->compiling_list = 0; +} + + +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) +{ + list *L; + list_fn *F; + int i; + + Assert (state->compiling_list > 0, "not inside glNewList"); + Assert (state->compiling_list <= state->lists.count, "glNewList corrupted"); + + 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; + } + + memset (&L->fns[L->count], 0, sizeof (*L->fns)); + + F = L->fns + L->count; + + F->name = name; + F->fn = fn; + F->proto = proto; + if (proto != PROTO_VOID) + for (i = 0; i < countof(F->argv); i++) + F->argv[i] = av[i]; + +# ifdef DEBUG + switch (proto) { + case PROTO_VOID: + LOG1 (" push %-12s", name); + break; + case PROTO_I: + if (fn == (list_fn_cb) &jwzgles_glBegin || + fn == (list_fn_cb) &jwzgles_glFrontFace || + fn == (list_fn_cb) &jwzgles_glEnable || + fn == (list_fn_cb) &jwzgles_glDisable || + fn == (list_fn_cb) &jwzgles_glEnableClientState || + fn == (list_fn_cb) &jwzgles_glDisableClientState || + fn == (list_fn_cb) &jwzgles_glShadeModel || + fn == (list_fn_cb) &jwzgles_glMatrixMode) + LOG2 (" push %-12s %s", name, mode_desc (av[0].i)); + else + LOG2 (" push %-12s %d", name, av[0].i); + break; + case PROTO_F: + LOG2 (" push %-12s %7.3f", name, av[0].f); + break; + case PROTO_II: + if (fn == (list_fn_cb) &jwzgles_glBindTexture) + 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); + break; + case PROTO_FF: + LOG3 (" push %-12s %7.3f %7.3f", name, av[0].f, av[1].f); + break; + case PROTO_IF: + LOG3 (" push %-12s %s %7.3f", name, mode_desc (av[0].i), av[1].f); + break; + case PROTO_III: + case PROTO_ARRAYS: + if (fn == (list_fn_cb) &jwzgles_glDrawArrays || + fn == (list_fn_cb) &jwzgles_glTexParameteri) + LOG4 (" push %-12s %s %d %d", name, mode_desc (av[0].i), + av[1].i, av[2].i); + else + LOG4 (" push %-12s %d %d %d", name, av[0].i, av[1].i, av[2].i); + break; + case PROTO_FFF: + LOG4 (" push %-12s %7.3f %7.3f %7.3f", name, av[0].f, av[1].f, av[2].f); + break; + case PROTO_IIF: + LOG4 (" push %-12s %s %s %7.3f", name, + mode_desc(av[0].i), mode_desc(av[1].i), av[2].f); + break; + case PROTO_IIII: + LOG5 (" push %-12s %d %d %d %d", name, + av[0].i, av[1].i, av[2].i, av[3].i); + break; + case PROTO_FFFF: + LOG5 (" push %-12s %7.3f %7.3f %7.3f %7.3f", name, + av[0].f, av[1].f, av[2].f, av[3].f); + break; + case PROTO_IFV: + LOG6 (" push %-12s %s %3.1f %3.1f %3.1f %3.1f", name, mode_desc (av[0].i), + av[1].f, av[2].f, av[3].f, av[4].f); + break; + case PROTO_IIV: + LOG6 (" push %-12s %s %d %d %d %d", name, mode_desc (av[0].i), + av[1].i, av[2].i, av[3].i, av[4].i); + break; + case PROTO_IIFV: + LOG7 (" push %-12s %s %-8s %3.1f %3.1f %3.1f %3.1f", name, + mode_desc (av[0].i), mode_desc (av[1].i), + av[2].f, av[3].f, av[4].f, av[5].f); + break; + case PROTO_IIIV: + LOG7 (" push %-12s %s %-8s %3d %3d %3d %3d", name, + mode_desc (av[0].i), mode_desc (av[1].i), + av[2].i, av[3].i, av[4].i, av[5].i); + break; + case PROTO_FV16: + LOG17 (" push %-12s [" + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f ]", + name, + av[0].f, av[1].f, av[2].f, av[3].f, + av[4].f, av[5].f, av[6].f, av[7].f, + av[8].f, av[9].f, av[10].f, av[11].f, + av[12].f, av[13].f, av[14].f, av[15].f); + break; + default: + Assert (0, "bogus prototype"); + break; + } +# endif /* DEBUG */ + + if (proto == PROTO_ARRAYS) /* glDrawArrays */ + save_arrays (F, av[1].i + av[2].i); + + L->count++; +} + + +void +jwzgles_glBegin (int mode) +{ + Assert (!state->compiling_verts, "nested glBegin"); + state->compiling_verts++; + + /* Only these commands are allowed inside glBegin: + + glVertex -- not allowed outside + glColor + glSecondaryColor + glIndex + glNormal + glFogCoord + glTexCoord + glMultiTexCoord + glVertexAttrib + glEvalCoord + glEvalPoint + glArrayElement -- not allowed outside + glMaterial + glEdgeFlag + glCallList + glCallLists + */ + + if (!state->replaying_list) + LOG2 ("%sglBegin %s", + (state->compiling_list || state->replaying_list ? " " : ""), + mode_desc (mode)); + + Assert (state->set.count == 0, "glBegin corrupted"); + state->set.mode = mode; + state->set.count = 0; + state->set.ncount = 0; + state->set.tcount = 0; + state->set.ccount = 0; +} + + +void +jwzgles_glDeleteLists (int id0, int range) +{ + Assert (!state->compiling_verts, "glDeleteLists not allowed inside glBegin"); + + if (state->compiling_list) + { + void_int vv[2]; + vv[0].i = id0; + vv[1].i = range; + list_push ("glDeleteLists", (list_fn_cb) &jwzgles_glDeleteLists, + PROTO_II, vv); + } + else + { + int id; + + if (!state->replaying_list) + LOG2 ("glDeleteLists %d %d", id0, range); + + for (id = id0 + range - 1; id >= id0; id--) + { + int i; + list *L; + if (id == 0) continue; /* People do this stupid thing */ + if (id > state->lists.count) break; /* this too */ + Assert (id > 0 && id <= state->lists.count, + "glDeleteLists: bogus ID"); + L = &state->lists.lists[id-1]; + Assert (L->id == id, "glDeleteLists corrupted"); + + for (i = 0; i < L->count; i++) + { + list_fn *lf = &L->fns[i]; + if (lf->arrays) + { + int j; + for (j = 0; j < 4; j++) + { + if (lf->arrays[j].data) + free (lf->arrays[j].data); + } + } + } + if (L->fns) + free (L->fns); + + memset (L, 0, sizeof (*L)); + L->id = id; + } + } +} + + +extern GLboolean +jwzgles_glIsList (GLuint id) +{ + return (id > 0 && id < state->lists.count); +} + + + +void +jwzgles_glNormal3fv (const GLfloat *v) +{ + if (state->compiling_list && !state->compiling_verts) + { + void_int vv[3]; + vv[0].f = v[0]; + vv[1].f = v[1]; + vv[2].f = v[2]; + list_push ("glNormal3f", (list_fn_cb) &jwzgles_glNormal3f, + PROTO_FFF, vv); + } + else + { + if (!state->replaying_list) + LOG5 ("%s%sglNormal3f %7.3f %7.3f %7.3f", + (state->compiling_list || state->replaying_list ? " " : ""), + (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 */ + { + glNormal3f (v[0], v[1], v[2]); + CHECK("glNormal3f"); + } + } +} + + +void +jwzgles_glNormal3f (GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat v[3]; + v[0] = x; + v[1] = y; + v[2] = z; + jwzgles_glNormal3fv (v); +} + + +void +jwzgles_glTexCoord4fv (const GLfloat *v) +{ + if (state->compiling_list && !state->compiling_verts) + { + void_int vv[4]; + vv[0].f = v[0]; + vv[1].f = v[1]; + vv[2].f = v[2]; + vv[3].f = v[3]; + list_push ("glTexCoord4f", (list_fn_cb) &jwzgles_glTexCoord4f, + PROTO_FFFF, vv); + } + else + { + if (!state->replaying_list) + LOG6 ("%s%sglTexCoord4f %7.3f %7.3f %7.3f %7.3f", + (state->compiling_list || state->replaying_list ? " " : ""), + (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"); + } +} + + +void +jwzgles_glTexCoord4f (GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + GLfloat v[4]; + v[0] = s; + v[1] = t; + v[2] = r; + v[3] = q; + jwzgles_glTexCoord4fv (v); +} + + +void +jwzgles_glTexCoord3fv (const GLfloat *v) +{ + GLfloat vv[4]; + vv[0] = v[0]; + vv[1] = v[1]; + vv[2] = v[2]; + vv[3] = 1; + jwzgles_glTexCoord4fv (vv); +} + + +void +jwzgles_glTexCoord2fv (const GLfloat *v) +{ + GLfloat vv[4]; + vv[0] = v[0]; + vv[1] = v[1]; + vv[2] = 0; + vv[3] = 1; + jwzgles_glTexCoord4fv (vv); +} + + +void +jwzgles_glTexCoord3f (GLfloat s, GLfloat t, GLfloat r) +{ + jwzgles_glTexCoord4f (s, t, r, 1); +} + + +void +jwzgles_glTexCoord2f (GLfloat s, GLfloat t) +{ + jwzgles_glTexCoord4f (s, t, 0, 1); +} + + +void +jwzgles_glTexCoord1f (GLfloat s) +{ + jwzgles_glTexCoord4f (s, 0, 0, 1); +} + + + +void +jwzgles_glColor4fv (const GLfloat *v) +{ + if (state->compiling_list && !state->compiling_verts) + { + void_int vv[4]; + vv[0].f = v[0]; + vv[1].f = v[1]; + vv[2].f = v[2]; + vv[3].f = v[3]; + list_push ("glColor4f", (list_fn_cb) &jwzgles_glColor4f, + PROTO_FFFF, vv); + } + else + { + if (!state->replaying_list) + LOG6 ("%s%sglColor4f %7.3f %7.3f %7.3f %7.3f", + (state->compiling_list || state->replaying_list ? " " : ""), + (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 */ + { + glColor4f (v[0], v[1], v[2], v[3]); + CHECK("glColor4"); + } + } +} + + +void +jwzgles_glColor4f (GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + GLfloat v[4]; + v[0] = r; + v[1] = g; + v[2] = b; + v[3] = 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); +} + + +void +jwzgles_glColor4i (GLuint r, GLuint g, GLuint b, GLuint a) +{ + jwzgles_glColor4f (r, g, b, a); +} + + +void +jwzgles_glColor3i (GLuint r, GLuint g, GLuint b) +{ + jwzgles_glColor4f (r, g, b, 1); +} + + +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); +} + + +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); +} + + +void +jwzgles_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a) +{ + jwzgles_glColor4f (r, g, b, a); +} + + +void +jwzgles_glColor3ub (GLubyte r, GLubyte g, GLubyte b) +{ + jwzgles_glColor4f (r, g, b, 1); +} + + +void +jwzgles_glMaterialfv (GLenum face, GLenum pname, const GLfloat *color) +{ + /* If this is called inside glBegin/glEnd with a front ambient color, + then treat it the same as glColor: set the color of the upcoming + vertex. + + Other faces or lighting types within glBegin are ignored. + */ + + if (state->compiling_verts) + { + if ((face == GL_FRONT || + face == GL_FRONT_AND_BACK) && + (pname == GL_AMBIENT || + pname == GL_DIFFUSE || + pname == GL_AMBIENT_AND_DIFFUSE)) + { + jwzgles_glColor4f (color[0], color[1], color[2], color[3]); + state->set.materialistic++; + } + else + LOG2 (" IGNORING glMaterialfv %s %s", + mode_desc(face), mode_desc(pname)); + } + else if (state->compiling_list) + { + void_int vv[6]; + vv[0].i = face; + vv[1].i = pname; + vv[2].f = color[0]; + vv[3].f = color[1]; + vv[4].f = color[2]; + vv[5].f = color[3]; + list_push ("glMaterialfv", (list_fn_cb) &jwzgles_glMaterialfv, + PROTO_IIFV, vv); + } + else + { + /* If this is called outside of glBegin/glEnd with a front + ambient color, then the intent is presumably for that color + to apply to the upcoming vertexes (which may be played back + from a list that does not have vertex colors in it). In that + case, the only way to make the colors show up is to call + glColor() with GL_COLOR_MATERIAL enabled. + + I'm not sure if this will have other inappropriate side effects... + */ + if ((face == GL_FRONT || + face == GL_FRONT_AND_BACK) && + (pname == GL_AMBIENT || + pname == GL_DIFFUSE || + pname == GL_AMBIENT_AND_DIFFUSE)) + { + jwzgles_glEnable (GL_COLOR_MATERIAL); + jwzgles_glColor4f (color[0], color[1], color[2], color[3]); + } + + /* OpenGLES seems to throw "invalid enum" for GL_FRONT -- but it + goes ahead and sets the material anyway! No error if we just + always use GL_FRONT_AND_BACK. + */ + if (face == GL_FRONT) + face = GL_FRONT_AND_BACK; + if (! state->replaying_list) + LOG7 ("direct %-12s %s %s %7.3f %7.3f %7.3f %7.3f", "glMaterialfv", + mode_desc(face), mode_desc(pname), + color[0], color[1], color[2], color[3]); + glMaterialfv (face, pname, color); /* the real one */ + CHECK("glMaterialfv"); + } +} + + +void +jwzgles_glMaterialiv (GLenum face, GLenum pname, const GLint *v) +{ + GLfloat vv[4]; + vv[0] = v[0]; + vv[1] = v[1]; + vv[2] = v[2]; + vv[3] = 1; + jwzgles_glMaterialfv (face, pname, vv); +} + +void +jwzgles_glMaterialf (GLenum face, GLenum pname, const GLfloat c) +{ + GLfloat vv[4]; + vv[0] = c; + vv[1] = c; + vv[2] = c; + vv[3] = 1; + jwzgles_glMaterialfv (face, pname, vv); +} + + +void +jwzgles_glMateriali (GLenum face, GLenum pname, const GLuint c) +{ + jwzgles_glMaterialf (face, pname, c); +} + + +void +jwzgles_glColorMaterial (GLenum face, GLenum mode) +{ + Assert (!state->compiling_verts, + "glColorMaterial not allowed inside glBegin"); +#if 0 + if (state->compiling_list) + { + void_int vv[2]; + vv[0].i = face; + vv[1].i = mode; + list_push ("glColorMaterial", (list_fn_cb) &jwzgles_glColorMaterial, + PROTO_II, vv); + } + else + { + /* No real analog to this distinction in OpenGLES, since color + arrays don't distinguish between "color" and "material", */ + Assert (0, "glColorMaterial: unimplemented mode"); + } +#endif +} + + + + +void +jwzgles_glVertex4fv (const GLfloat *v) +{ + vert_set *s = &state->set; + int count = s->count; + + Assert (state->compiling_verts, "glVertex4fv not inside glBegin"); + + LOG5("%s rec glVertex4f %7.3f %7.3f %7.3f %7.3f", + (state->compiling_list || state->replaying_list ? " " : ""), + v[0], v[1], v[2], v[3]); + + if (count >= s->size - 1) + { + int new_size = 20 + (s->size * 1.2); + + /* 4 arrays, different element sizes... + We allocate all 4 arrays just in case we need them, + but we might not end up using them all at the end. + */ + + s->verts = (XYZW *) realloc (s->verts, new_size * sizeof (*s->verts)); + Assert (s->verts, "out of memory"); + + s->norms = (XYZ *) realloc (s->norms, new_size * sizeof (*s->norms)); + Assert (s->norms, "out of memory"); + + s->tex = (STRQ *) realloc (s->tex, new_size * sizeof (*s->tex)); + Assert (s->tex, "out of memory"); + + s->color = (RGBA *) realloc (s->color, new_size * sizeof (*s->color)); + Assert (s->color, "out of memory"); + + s->size = new_size; + } + + s->verts [count].x = v[0]; + s->verts [count].y = v[1]; + s->verts [count].z = v[2]; + s->verts [count].w = v[3]; + s->norms [count] = s->cnorm; + s->tex [count] = s->ctex; + s->color [count] = s->ccolor; + s->count++; +} + + +void +jwzgles_glVertex4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLfloat v[4]; + v[0] = x; + v[1] = y; + v[2] = z; + v[3] = w; + jwzgles_glVertex4fv (v); +} + +void +jwzgles_glVertex4i (GLint x, GLint y, GLint z, GLint w) +{ + jwzgles_glVertex4f (x, y, z, w); +} + +void +jwzgles_glVertex3f (GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat v[4]; + v[0] = x; + v[1] = y; + v[2] = z; + v[3] = 1; + jwzgles_glVertex4fv (v); +} + +void +jwzgles_glVertex3i (GLint x, GLint y, GLint z) +{ + jwzgles_glVertex3f (x, y, z); +} + +void +jwzgles_glVertex3fv (const GLfloat *v) +{ + jwzgles_glVertex3f (v[0], v[1], v[2]); +} + +void +jwzgles_glVertex3dv (const GLdouble *v) +{ + jwzgles_glVertex3f (v[0], v[1], v[2]); +} + + +void +jwzgles_glVertex2f (GLfloat x, GLfloat y) +{ + GLfloat v[3]; + v[0] = x; + v[1] = y; + v[2] = 0; + jwzgles_glVertex3fv (v); +} + +void +jwzgles_glVertex2fv (const GLfloat *v) +{ + jwzgles_glVertex2f (v[0], v[1]); +} + +void +jwzgles_glVertex2i (GLint x, GLint y) +{ + jwzgles_glVertex2f (x, y); +} + + +void +jwzgles_glLightiv (GLenum light, GLenum pname, const GLint *params) +{ + GLfloat v[4]; + v[0] = params[0]; + v[1] = params[1]; + v[2] = params[2]; + v[3] = params[3]; + jwzgles_glLightfv (light, pname, v); +} + +void +jwzgles_glLightModeliv (GLenum pname, const GLint *params) +{ + GLfloat v[4]; + v[0] = params[0]; + v[1] = params[1]; + v[2] = params[2]; + v[3] = params[3]; + jwzgles_glLightModelfv (pname, v); +} + +void +jwzgles_glFogiv (GLenum pname, const GLint *params) +{ + GLfloat v[4]; + v[0] = params[0]; + v[1] = params[1]; + v[2] = params[2]; + v[3] = params[3]; + jwzgles_glFogfv (pname, v); +} + +void +jwzgles_glLighti (GLenum light, GLenum pname, GLint param) +{ + jwzgles_glLightf (light, pname, param); +} + +void +jwzgles_glLightModeli (GLenum pname, GLint param) +{ + jwzgles_glLightModelf (pname, param); +} + +void +jwzgles_glFogi (GLenum pname, GLint param) +{ + jwzgles_glFogf (pname, param); +} + + +void +jwzgles_glRotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z) +{ + jwzgles_glRotatef (angle, x, y, z); +} + + +void +jwzgles_glClipPlane (GLenum plane, const GLdouble *equation) +{ + Assert (state->compiling_verts, "glClipPlane not inside glBegin"); + Assert (0, "glClipPlane unimplemented"); /* no GLES equivalent... */ +} + + +void +jwzgles_glPolygonMode (GLenum face, GLenum mode) +{ + Assert (!state->compiling_verts, "not inside glBegin"); + if (state->compiling_list) + { + void_int vv[2]; + vv[0].i = face; + vv[1].i = mode; + list_push ("glPolygonMode", (list_fn_cb) &jwzgles_glPolygonMode, + PROTO_II, vv); + } + else + { + /* POINT and LINE don't exist in GLES */ + Assert (mode == GL_FILL, "glPolygonMode: unimplemented mode"); + } +} + + +void +jwzgles_glDrawBuffer (GLenum buf) +{ + Assert (!state->compiling_verts, "not inside glBegin"); + if (state->compiling_list) + { + void_int vv[1]; + vv[0].i = buf; + list_push ("glDrawBuffer", (list_fn_cb) &jwzgles_glDrawBuffer, + PROTO_I, vv); + } + else + { +/* Assert (buf == GL_BACK, "glDrawBuffer: back buffer only"); */ +# ifndef GL_VERSION_ES_CM_1_0 /* not compiling against OpenGLES 1.x */ + if (! state->replaying_list) + LOG1 ("direct %-12s", "glDrawBuffer"); + glDrawBuffer (buf); /* the real one */ + CHECK("glDrawBuffer"); +# endif + } +} + + +/* Given an array of sets of 4 elements of arbitrary size, convert it + to an array of sets of 6 elements instead: ABCD becomes ABC BCD. + */ +static int +cq2t (unsigned char **arrayP, int stride, int count) +{ + int count2 = count * 6 / 4; + int size = stride * count; + int size2 = stride * count2; + const unsigned char *oarray, *in; + unsigned char *array2, *oarray2, *out; + int i; + + oarray = *arrayP; + if (!oarray || count == 0) + return count2; + + array2 = (unsigned char *) malloc (size2); + Assert (array2, "out of memory"); + oarray2 = array2; + + in = oarray; + out = oarray2; + for (i = 0; i < count / 4; i++) + { + const unsigned char *a, *b, *c, *d; /* the 4 corners */ + a = in; in += stride; + b = in; in += stride; + c = in; in += stride; + d = in; in += stride; + +# define PUSH(IN) do { \ + const unsigned char *ii = IN; \ + int j; \ + for (j = 0; j < stride; j++) { \ + *out++ = *ii++; \ + }} while(0) + + PUSH (a); PUSH (b); PUSH (d); /* the 2 triangles */ + PUSH (b); PUSH (c); PUSH (d); +# undef PUSH + } + + Assert (in == oarray + size, "convert_quads corrupted"); + Assert (out == oarray2 + size2, "convert_quads corrupted"); + + free (*arrayP); + *arrayP = oarray2; + return count2; +} + + +/* Convert all coordinates in a GL_QUADS vert_set to GL_TRIANGLES. + */ +static void +convert_quads_to_triangles (vert_set *s) +{ + int count2; + Assert (s->mode == GL_QUADS, "convert_quads bad mode"); + count2 = + cq2t ((unsigned char **) &s->verts, sizeof(*s->verts), s->count); + cq2t ((unsigned char **) &s->norms, sizeof(*s->norms), s->count); + cq2t ((unsigned char **) &s->tex, sizeof(*s->tex), s->count); + cq2t ((unsigned char **) &s->color, sizeof(*s->color), s->count); + s->count = count2; + s->size = count2; + s->mode = GL_TRIANGLES; +} + + +void +jwzgles_glEnd (void) +{ + vert_set *s = &state->set; + int was_norm, was_tex, was_color, was_mat; + int is_norm, is_tex, is_color, is_mat; + + Assert (state->compiling_verts == 1, "missing glBegin"); + state->compiling_verts--; + + if (!state->replaying_list) + { + LOG5 ("%s [V = %d, N = %d, T = %d, C = %d]", + (state->compiling_list || state->replaying_list ? " " : ""), + s->count, s->ncount, s->tcount, s->ccount); + LOG1 ("%sglEnd", + (state->compiling_list || state->replaying_list ? " " : "")); + } + + if (s->count == 0) return; + + if (s->mode == GL_QUADS) + convert_quads_to_triangles (s); + else if (s->mode == GL_QUAD_STRIP) + s->mode = GL_TRIANGLE_STRIP; /* They do the same thing! */ + 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"); + + /* If there were no calls to glNormal3f inside of glBegin/glEnd, + don't bother enabling the normals array. + + If there was exactly *one* call to glNormal3f inside of glBegin/glEnd, + and it was before the first glVertex3f, then also don't enable the + normals array, but do emit that call to glNormal3f before calling + glDrawArrays. + + Likewise for texture coordinates and colors. + + 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. + */ + 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 (s->ncount > 1) + { + is_norm = 1; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + } + else + { + is_norm = 0; + if (s->ncount == 1) + jwzgles_glNormal3f (s->cnorm.x, s->cnorm.y, s->cnorm.z); + jwzgles_glDisableClientState (GL_NORMAL_ARRAY); + } + + if (s->tcount > 1) + { + is_tex = 1; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + } + else + { + is_tex = 0; + if (s->tcount == 1) + jwzgles_glTexCoord4f (s->ctex.s, s->ctex.t, s->ctex.r, s->ctex.q); + jwzgles_glDisableClientState (GL_TEXTURE_COORD_ARRAY); + } + + if (s->ccount > 1) + { + is_color = 1; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + } + else + { + is_color = 0; + if (s->ccount == 1) + jwzgles_glColor4f (s->ccolor.r, s->ccolor.g, s->ccolor.b, s->ccolor.a); + jwzgles_glDisableClientState (GL_COLOR_ARRAY); + } + + jwzgles_glEnableClientState (GL_VERTEX_ARRAY); + + /* We translated the glMaterial calls to per-vertex colors, which are + of the glColor sort, not the glMaterial sort, so automatically + turn on material mapping. Maybe this is a bad idea. + */ + if (s->materialistic && !jwzgles_glIsEnabled (GL_COLOR_MATERIAL)) + { + is_mat = 1; + jwzgles_glEnable (GL_COLOR_MATERIAL); + } + else + is_mat = 0; + + jwzgles_glDrawArrays (s->mode, 0, s->count); + +# define RESET(VAR,FN,ARG) do { \ + if (is_##VAR != was_##VAR) { \ + if (was_##VAR) jwzgles_glEnable##FN (ARG); \ + else jwzgles_glDisable##FN (ARG); \ + }} while(0) + RESET (norm, ClientState, GL_NORMAL_ARRAY); + RESET (tex, ClientState, GL_TEXTURE_COORD_ARRAY); + RESET (color, ClientState, GL_COLOR_ARRAY); + RESET (mat, , GL_COLOR_MATERIAL); +# undef RESET + + s->count = 0; + s->ncount = 0; + s->tcount = 0; + s->ccount = 0; + s->materialistic = 0; +} + + +void +jwzgles_glCallList (int id) +{ + if (state->compiling_list) + { + /* Yes, you can call lists inside of lists. + Yes, recursion would be a mistake. */ + void_int vv[1]; + vv[0].i = id; + list_push ("glCallList", (list_fn_cb) &jwzgles_glCallList, PROTO_I, vv); + } + else + { + list *L; + int i; + + state->replaying_list++; + +# ifdef DEBUG + fprintf (stderr, "\n"); + LOG1 ("glCallList %d", id); +# endif + + Assert (id > 0 && id <= state->lists.count, "glCallList: bogus ID"); + L = &state->lists.lists[id-1]; + Assert (id == L->id, "glCallList corrupted"); + + for (i = 0; i < L->count; i++) + { + list_fn *F = &L->fns[i]; + list_fn_cb fn = F->fn; + void_int *av = F->argv; + + switch (F->proto) { + case PROTO_VOID: + LOG1 (" call %-12s", F->name); + ((void (*) (void)) fn) (); + break; + + case PROTO_I: + if (fn == (list_fn_cb) &jwzgles_glBegin || + fn == (list_fn_cb) &jwzgles_glFrontFace || + fn == (list_fn_cb) &jwzgles_glEnable || + fn == (list_fn_cb) &jwzgles_glDisable || + fn == (list_fn_cb) &jwzgles_glEnableClientState || + fn == (list_fn_cb) &jwzgles_glDisableClientState || + fn == (list_fn_cb) &jwzgles_glShadeModel || + fn == (list_fn_cb) &jwzgles_glMatrixMode) + LOG2 (" call %-12s %s", F->name, mode_desc (av[0].i)); + else + LOG2 (" call %-12s %d", F->name, av[0].i); + ((void (*) (int)) fn) (av[0].i); + break; + + case PROTO_F: + LOG2 (" call %-12s %7.3f", F->name, av[0].f); + ((void (*) (GLfloat)) fn) (av[0].f); + break; + + case PROTO_II: + if (fn == (list_fn_cb) &jwzgles_glBindTexture) + LOG3 (" call %-12s %s %d", F->name, + mode_desc (av[0].i), av[1].i); + else + LOG3 (" call %-12s %d %d", F->name, av[0].i, av[1].i); + ((void (*) (int, int)) fn) (av[0].i, av[1].i); + break; + + case PROTO_FF: + LOG3 (" call %-12s %7.3f %7.3f", F->name, av[0].f, av[1].f); + ((void (*) (GLfloat, GLfloat)) fn) (av[0].f, av[1].f); + break; + + case PROTO_IF: + LOG3 (" call %-12s %s %7.3f", F->name, + mode_desc (av[0].f), av[1].f); + ((void (*) (GLint, GLfloat)) fn) (av[0].i, av[1].f); + break; + + case PROTO_III: III: + if (fn == (list_fn_cb) &jwzgles_glDrawArrays || + fn == (list_fn_cb) &jwzgles_glTexParameteri) + LOG4 (" call %-12s %s %d %d", F->name, + mode_desc (av[0].i), av[1].i, av[2].i); + else + LOG4 (" call %-12s %d %d %d", F->name, + av[0].i, av[1].i, av[2].i); + ((void (*) (int, int, int)) fn) (av[0].i, av[1].i, av[2].i); + break; + + case PROTO_FFF: + LOG4 (" call %-12s %7.3f %7.3f %7.3f", F->name, + av[0].f, av[1].f, av[2].f); + ((void (*) (GLfloat, GLfloat, GLfloat)) fn) + (av[0].f, av[1].f, av[2].f); + break; + + case PROTO_IIF: + LOG4 (" call %-12s %s %s %7.3f", F->name, + mode_desc (av[0].i), mode_desc (av[1].i), av[2].f); + ((void (*) (int, int, GLfloat)) fn) (av[0].i, av[1].i, av[2].f); + break; + + case PROTO_IIII: + LOG5 (" call %-12s %d %d %d %d", F->name, + av[0].i, av[1].i, av[2].i, av[3].i); + ((void (*) (int, int, int, int)) fn) + (av[0].i, av[1].i, av[2].i, av[3].i); + break; + + case PROTO_FFFF: + LOG5 (" call %-12s %7.3f %7.3f %7.3f %7.3f", F->name, + av[0].f, av[1].f, av[2].f, av[3].f); + ((void (*) (GLfloat, GLfloat, GLfloat, GLfloat)) fn) + (av[0].f, av[1].f, av[2].f, av[3].f); + break; + + case PROTO_IFV: + { + GLfloat v[4]; + v[0] = av[1].f; + v[1] = av[2].f; + v[2] = av[3].f; + v[3] = av[4].f; + LOG6 (" call %-12s %s %3.1f %3.1f %3.1f %3.1f", F->name, + mode_desc (av[0].i), + av[1].f, av[2].f, av[3].f, av[4].f); + ((void (*) (int, const GLfloat *)) fn) (av[0].i, v); + } + break; + + case PROTO_IIFV: + { + GLfloat v[4]; + v[0] = av[2].f; + v[1] = av[3].f; + v[2] = av[4].f; + v[3] = av[5].f; + LOG7 (" call %-12s %s %-8s %3.1f %3.1f %3.1f %3.1f", F->name, + mode_desc (av[0].i), mode_desc (av[1].i), + av[2].f, av[3].f, av[4].f, av[5].f); + ((void (*) (int, int, const GLfloat *)) fn) + (av[0].i, av[1].i, v); + } + break; + + case PROTO_IIV: + { + int v[4]; + v[0] = av[1].i; + v[1] = av[2].i; + v[2] = av[3].i; + v[3] = av[4].i; + LOG6 (" call %-12s %s %3d %3d %3d %3d", F->name, + mode_desc (av[0].i), + av[1].i, av[2].i, av[3].i, av[4].i); + ((void (*) (int, const int *)) fn) (av[0].i, v); + } + break; + + case PROTO_IIIV: + { + int v[4]; + v[0] = av[2].i; + v[1] = av[3].i; + v[2] = av[4].i; + v[3] = av[5].i; + LOG7 (" call %-12s %s %-8s %3d %3d %3d %3d", F->name, + mode_desc (av[0].i), mode_desc (av[1].i), + av[2].i, av[3].i, av[4].i, av[5].i); + ((void (*) (int, int, const int *)) fn) + (av[0].i, av[1].i, v); + } + break; + + case PROTO_ARRAYS: + restore_arrays (F, av[1].i + av[2].i); + goto III; + break; + + case PROTO_FV16: + { + GLfloat m[16]; + int i; + for (i = 0; i < countof(m); i++) + m[i] = av[i].f; + LOG17 (" call %-12s [" + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f " "\n\t\t\t " + "%8.3f %8.3f %8.3f %8.3f ]", + F->name, + m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15]); + ((void (*) (GLfloat *)) fn) (m); + } + break; + + default: + Assert (0, "bogus prototype"); + break; + } + } + + LOG1 ("glCallList %d done\n", id); + + state->replaying_list--; + Assert (state->replaying_list >= 0, "glCallList corrupted"); + } +} + + +/* When we save a call to glDrawArrays into a display list, we also need to + save the prevailing copy of the arrays that it will use, and restore them + later. + */ +static void +save_arrays (list_fn *F, int count) +{ + int i = 0; + draw_array *A = (draw_array *) calloc (4, sizeof (*A)); + Assert (A, "out of memory"); + +/* if (state->set.count > 0) */ + { + glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A[i].size); + glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A[i].type); + glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A[i].stride); + glGetPointerv (GL_VERTEX_ARRAY_POINTER, &A[i].data); + CHECK("glGetPointerv"); + copy_array_data (&A[i], count, "vert"); + } + + i++; + if (state->set.ncount > 1) + { + A[i].size = 3; + glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A[i].type); + glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A[i].stride); + glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A[i].data); + CHECK("glGetPointerv"); + copy_array_data (&A[i], count, "norm"); + } + + i++; + if (state->set.tcount > 1) + { + 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); + glGetPointerv (GL_TEXTURE_COORD_ARRAY_POINTER, &A[i].data); + CHECK("glGetPointerv"); + copy_array_data (&A[i], count, "tex "); + } + + i++; + if (state->set.ccount > 1) + { + glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A[i].size); + glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A[i].type); + glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A[i].stride); + glGetPointerv (GL_COLOR_ARRAY_POINTER, &A[i].data); + CHECK("glGetPointerv"); + copy_array_data (&A[i], count, "col "); + } + + /* Freed by glDeleteLists. */ + + Assert (!F->arrays, "save_arrays corrupted"); + F->arrays = A; +} + + +#ifdef DEBUG + +static void +dump_array_data (draw_array *A, int count, + const char *action, const char *name, const void *old) +{ + int bytes = count * A->stride; + + 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"); + +# if 0 + { + unsigned char *b = (unsigned char *) A->data; + int i; + for (i = 0; i < count; i++) + { + int j; + GLfloat *f = (GLfloat *) b; + int s = A->size; + if (s == 0) s = 3; /* normals */ + fprintf (stderr, "jwzgles: "); + for (j = 0; j < s; j++) + fprintf (stderr, " %7.3f", f[j]); + fprintf (stderr, "\n"); + b += A->stride; + } + } +# endif +} + +static void +dump_direct_array_data (int count) +{ + draw_array A; + + if (jwzgles_glIsEnabled (GL_VERTEX_ARRAY)) + { + 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; + dump_array_data (&A, count, "direct", "vertex ", 0); + } + if (jwzgles_glIsEnabled (GL_NORMAL_ARRAY)) + { + A.size = 0; + glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A.type); + glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A.stride); + glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A.data); + A.bytes = count * A.stride; + dump_array_data (&A, count, "direct", "normal ", 0); + } + if (jwzgles_glIsEnabled (GL_TEXTURE_COORD_ARRAY)) + { + glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE, &A.size); + glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE, &A.type); + glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE, &A.stride); + glGetPointerv (GL_TEXTURE_COORD_ARRAY_POINTER, &A.data); + A.bytes = count * A.stride; + dump_array_data (&A, count, "direct", "texture", 0); + } + if (jwzgles_glIsEnabled (GL_COLOR_ARRAY)) + { + glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A.size); + glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A.type); + glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A.stride); + glGetPointerv (GL_COLOR_ARRAY_POINTER, &A.data); + A.bytes = count * A.stride; + dump_array_data (&A, count, "direct", "color ", 0); + } +} + +#endif /* DEBUG */ + + +static void +copy_array_data (draw_array *A, int count, const char *name) +{ + /* Instead of just memcopy'ing the whole array and obeying its previous + 'stride' value, we make up a more compact array. This is because if + the same array data is being used with multiple component types, + e.g. with glInterleavedArrays, we don't want to copy all of the + 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; + + Assert (A->size >= 2 && A->size <= 4, "bogus array size"); + + switch (A->type) { + case GL_FLOAT: stride2 = A->size * sizeof(GLfloat); break; + case GL_UNSIGNED_BYTE: stride2 = A->size; break; + default: Assert (0, "bogus array type"); break; + } + + bytes = count * stride2; + Assert (bytes > 0, "bogus array count or stride"); + Assert (A->data, "missing array data"); + data2 = (void *) malloc (bytes); + Assert (data2, "out of memory"); + + IB = (const unsigned char *) A->data; + OB = (unsigned char *) data2; + IF = (const GLfloat *) A->data; + OF = (GLfloat *) data2; + + switch (A->type) { + case GL_FLOAT: + for (i = 0; i < count; i++) + { + for (j = 0; j < A->size; j++) + *OF++ = IF[j]; + IF = (const GLfloat *) (((const unsigned char *) IF) + A->stride); + } + break; + case GL_UNSIGNED_BYTE: + for (i = 0; i < count; i++) + { + for (j = 0; j < A->size; j++) + *OB++ = IB[j]; + IB += A->stride; + } + break; + default: + Assert (0, "bogus array type"); + break; + } + + old = A->data; + A->data = data2; + A->bytes = bytes; + A->stride = stride2; + +# ifdef DEBUG + dump_array_data (A, count, "saved", name, old); +# endif +} + + +static void +restore_arrays (list_fn *F, int count) +{ + int i = 0; + draw_array *A = F->arrays; + Assert (A, "missing array"); + + for (i = 0; i < 4; i++) + { + const char *name; + if (! A[i].data) continue; + switch (i) { + case 0: glVertexPointer (A[i].size, A[i].type, A[i].stride, A[i].data); + name = "vertex "; + CHECK("glVertexPointer"); + break; + case 1: glNormalPointer ( A[i].type, A[i].stride, A[i].data); + name = "normal "; + CHECK("glNormalPointer"); + break; + case 2: glTexCoordPointer(A[i].size, A[i].type, A[i].stride, A[i].data); + name = "texture"; + CHECK("glTexCoordPointer"); + break; + case 3: glColorPointer (A[i].size, A[i].type, A[i].stride, A[i].data); + name = "color "; + CHECK("glColorPointer"); + break; + default: Assert (0, "wat"); break; + } + +# ifdef DEBUG + dump_array_data (&A[i], count, "restored", name, 0); +# endif + } +} + + +void +jwzgles_glDrawArrays (GLuint mode, GLuint first, GLuint count) +{ + if (state->compiling_list) + { + void_int vv[3]; + vv[0].i = mode; + vv[1].i = first; + vv[2].i = count; + list_push ("glDrawArrays", (list_fn_cb) &jwzgles_glDrawArrays, + PROTO_ARRAYS, vv); + } + else + { +# ifdef DEBUG + if (! state->replaying_list) { + LOG4("direct %-12s %d %d %d", "glDrawArrays", mode, first, count); + dump_direct_array_data (first + count); + } +# endif + glDrawArrays (mode, first, count); /* the real one */ + CHECK("glDrawArrays"); + } +} + + +void +jwzgles_glInterleavedArrays (GLenum format, GLsizei stride, const void *data) +{ + /* We can implement this by calling the various *Pointer functions + with offsets into the same data, taking advantage of stride. + */ + const unsigned char *c = (const unsigned char *) data; +# define B 1 +# define F sizeof(GLfloat) + + Assert (!state->compiling_verts, + "glInterleavedArrays not allowed inside glBegin"); + + jwzgles_glEnableClientState (GL_VERTEX_ARRAY); + + if (!state->replaying_list) + LOG4 ("%sglInterleavedArrays %s %d %lX", + (state->compiling_list || state->replaying_list ? " " : ""), + mode_desc (format), stride, (unsigned long) data); + + switch (format) { + case GL_V2F: + glVertexPointer (2, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + if (!state->replaying_list) + LOG3 ("%s -> glVertexPointer 2 FLOAT %d %lX", + (state->compiling_list || state->replaying_list ? " " : ""), + stride, (unsigned long) c); + break; + case GL_V3F: + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + if (!state->replaying_list) + LOG3 ("%s -> glVertexPointer 3 FLOAT %d %lX", + (state->compiling_list || state->replaying_list ? " " : ""), + stride, (unsigned long) c); + break; + case GL_C4UB_V2F: + if (stride == 0) + stride = 4*B + 2*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); + CHECK("glColorPointer"); + c += 4*B; /* #### might be incorrect float-aligned address */ + glVertexPointer (2, GL_FLOAT, stride, c); + break; + case GL_C4UB_V3F: + if (stride == 0) + stride = 4*B + 3*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); + CHECK("glColorPointer"); + c += 4*B; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_C3F_V3F: + if (stride == 0) + stride = 3*F + 3*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (3, GL_FLOAT, stride, c); + CHECK("glColorPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_N3F_V3F: + if (stride == 0) + stride = 3*F + 3*F; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + glNormalPointer (GL_FLOAT, stride, c); + CHECK("glNormalPointer"); + if (!state->replaying_list) + LOG3 ("%s -> glNormalPointer FLOAT %d %lX", + (state->compiling_list || state->replaying_list ? " " : ""), + stride, (unsigned long) c); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + if (!state->replaying_list) + LOG3 ("%s -> glVertexPointer 3 FLOAT %d %lX", + (state->compiling_list || state->replaying_list ? " " : ""), + stride, (unsigned long) c); + break; + case GL_C4F_N3F_V3F: + if (stride == 0) + stride = 4*F + 3*F + 3*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (4, GL_FLOAT, stride, c); + CHECK("glColorPointer"); + c += 4*F; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + glNormalPointer (GL_FLOAT, stride, c); + CHECK("glNormalPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T2F_V3F: + if (stride == 0) + stride = 2*F + 3*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 2*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T4F_V4F: + if (stride == 0) + stride = 4*F + 4*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (4, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 4*F; + glVertexPointer (4, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T2F_C4UB_V3F: + if (stride == 0) + stride = 2*F + 4*B + 3*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 2*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); + CHECK("glColorPointer"); + c += 4*B; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T2F_C3F_V3F: + if (stride == 0) + stride = 2*F + 3*F + 3*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 2*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (3, GL_FLOAT, stride, c); + CHECK("glColorPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T2F_N3F_V3F: + if (stride == 0) + stride = 2*F + 3*F + 3*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 2*F; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + glNormalPointer (GL_FLOAT, stride, c); + CHECK("glNormalPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T2F_C4F_N3F_V3F: + if (stride == 0) + stride = 2*F + 4*F + 3*F + 3*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 2*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (3, GL_FLOAT, stride, c); + CHECK("glColorPointer"); + c += 3*F; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + glNormalPointer (GL_FLOAT, stride, c); + CHECK("glNormalPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + case GL_T4F_C4F_N3F_V4F: + if (stride == 0) + stride = 4*F + 4*F + 3*F + 4*F; + jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (4, GL_FLOAT, stride, c); + CHECK("glTexCoordPointer"); + c += 4*F; + jwzgles_glEnableClientState (GL_COLOR_ARRAY); + glColorPointer (4, GL_FLOAT, stride, c); + CHECK("glColorPointer"); + c += 4*F; + jwzgles_glEnableClientState (GL_NORMAL_ARRAY); + glNormalPointer (GL_FLOAT, stride, c); + CHECK("glNormalPointer"); + c += 3*F; + glVertexPointer (3, GL_FLOAT, stride, c); + CHECK("glVertexPointer"); + break; + default: + Assert (0, "glInterleavedArrays: bogus format"); + break; + } + +# undef B +# undef F +} + + + +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) +{ + Assert (!state->compiling_verts, + "glMultMatrixf not allowed inside glBegin"); + if (state->compiling_list) + { + void_int vv[16]; + int i; + for (i = 0; i < countof(vv); i++) + vv[i].f = m[i]; + list_push ("glMultMatrixf", (list_fn_cb) &jwzgles_glMultMatrixf, + PROTO_FV16, vv); + } + else + { + if (! state->replaying_list) + LOG1 ("direct %-12s", "glMultMatrixf"); + glMultMatrixf (m); /* the real one */ + CHECK("glMultMatrixf"); + } +} + + +void +jwzgles_glClearIndex(GLfloat c) +{ + /* Does GLES even do indexed color? */ + Assert (0, "glClearIndex unimplemented"); +} + + +void +jwzgles_glBitmap (GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, + GLfloat xmove, GLfloat ymove, const GLubyte *bitmap) +{ + Assert (0, "glBitmap unimplemented"); +} + +void +jwzgles_glPushAttrib(int flags) +{ + Assert (0, "glPushAttrib unimplemented"); +} + +void +jwzgles_glPopAttrib(void) +{ + Assert (0, "glPopAttrib unimplemented"); +} + + +/* These are needed for object hit detection in pinion. + Might need to rewrite that code entirely. Punt for now. + */ +void +jwzgles_glInitNames (void) +{ +/* Assert (0, "glInitNames unimplemented");*/ +} + +void +jwzgles_glPushName (GLuint name) +{ +/* Assert (0, "glPushName unimplemented");*/ +} + +GLuint +jwzgles_glPopName (void) +{ +/* Assert (0, "glPopName unimplemented");*/ + return 0; +} + +GLuint +jwzgles_glRenderMode (GLuint mode) +{ +/* Assert (0, "glRenderMode unimplemented");*/ + return 0; +} + +void +jwzgles_glSelectBuffer (GLsizei size, GLuint *buf) +{ +/* Assert (0, "glSelectBuffer unimplemented");*/ +} + + +void +jwzgles_glGenTextures (GLuint n, GLuint *ret) +{ + Assert (!state->compiling_verts, + "glGenTextures not allowed inside glBegin"); + /* technically legal, but stupid! */ + Assert (!state->compiling_list, + "glGenTextures not allowed inside glNewList"); + if (! state->replaying_list) + LOG1 ("direct %-12s", "glGenTextures"); + glGenTextures (n, ret); /* the real one */ + CHECK("glGenTextures"); +} + + +/* return the next larger power of 2. */ +static int +to_pow2 (int value) +{ + int i = 1; + while (i < value) i <<= 1; + return i; +} + +void +jwzgles_glTexImage1D (GLenum target, GLint level, + GLint internalFormat, + GLsizei width, GLint border, + GLenum format, GLenum type, + const GLvoid *pixels) +{ + 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 */ +} + +void +jwzgles_glTexImage2D (GLenum target, + GLint level, + GLint internalFormat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const GLvoid *data) +{ + GLvoid *d2 = (GLvoid *) data; + Assert (!state->compiling_verts, "glTexImage2D not allowed inside glBegin"); + Assert (!state->compiling_list, /* technically legal, but stupid! */ + "glTexImage2D not allowed inside glNewList"); + + Assert (width == to_pow2(width), "width must be a power of 2"); + Assert (height == to_pow2(height), "height must be a power of 2"); + + /* OpenGLES no longer supports "4" as a synonym for "RGBA". */ + switch (internalFormat) { + case 1: internalFormat = GL_LUMINANCE; break; + case 2: internalFormat = GL_LUMINANCE_ALPHA; break; + case 3: internalFormat = GL_RGB; break; + case 4: internalFormat = GL_RGBA; break; + } + + /* GLES does not let us omit the data pointer to create a blank texture. */ + if (! data) + { + d2 = (GLvoid *) calloc (1, width * height * sizeof(GLfloat) * 4); + Assert (d2, "out of memory"); + } + + if (internalFormat == GL_RGB && format == GL_RGBA) + internalFormat = GL_RGBA; /* WTF */ + + if (! state->replaying_list) + LOG10 ("direct %-12s %s %d %s %d %d %d %s %s 0x%lX", "glTexImage2D", + mode_desc(target), level, mode_desc(internalFormat), + width, height, border, mode_desc(format), mode_desc(type), + (unsigned long) d2); + glTexImage2D (target, level, internalFormat, width, height, border, + format, type, d2); /* the real one */ + CHECK("glTexImage2D"); + + if (d2 != data) free (d2); +} + +void +jwzgles_glTexSubImage2D (GLenum target, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const GLvoid *pixels) +{ + Assert (!state->compiling_verts, + "glTexSubImage2D not allowed inside glBegin"); + Assert (!state->compiling_list, /* technically legal, but stupid! */ + "glTexSubImage2D not allowed inside glNewList"); + + if (! state->replaying_list) + LOG10 ("direct %-12s %s %d %d %d %d %d %s %s 0x%lX", "glTexSubImage2D", + mode_desc(target), level, xoffset, yoffset, width, height, + mode_desc (format), mode_desc (type), (unsigned long) pixels); + glTexSubImage2D (target, level, xoffset, yoffset, width, height, + format, type, pixels); /* the real one */ + CHECK("glTexSubImage2D"); +} + +void +jwzgles_glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) +{ + Assert (!state->compiling_verts, + "glCopyTexImage2D not allowed inside glBegin"); + Assert (!state->compiling_list, /* technically legal, but stupid! */ + "glCopyTexImage2D not allowed inside glNewList"); + if (! state->replaying_list) + LOG9 ("direct %-12s %s %d %s %d %d %d %d %d", "glCopyTexImage2D", + mode_desc(target), level, mode_desc(internalformat), + x, y, width, height, border); + glCopyTexImage2D (target, level, internalformat, x, y, width, height, + border); /* the real one */ + CHECK("glCopyTexImage2D"); +} + + +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! + + 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; + + Assert (pname == GL_TEXTURE_GEN_MODE, "glTexGenfv: unimplemented name"); + Assert (params[0] == GL_EYE_LINEAR, "glTexGenfv: unimplemented mode"); +} + +void +jwzgles_glTexGeni (GLenum coord, GLenum pname, GLint param) +{ + GLfloat v = param; + jwzgles_glTexGenfv (coord, pname, &v); +} + + +int +jwzgles_gluBuild2DMipmaps (GLenum target, + GLint internalFormat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const GLvoid *data) +{ + /* Not really bothering with mipmapping; only making one level. + Note that this required a corresponding hack in glTexParameterf(). + */ + + int w2 = to_pow2(width); + int h2 = to_pow2(height); + + void *d2 = (void *) data; + + /* OpenGLES no longer supports "4" as a synonym for "RGBA". */ + switch (internalFormat) { + case 1: internalFormat = GL_LUMINANCE; break; + case 2: internalFormat = GL_LUMINANCE_ALPHA; break; + case 3: internalFormat = GL_RGB; break; + case 4: internalFormat = GL_RGBA; break; + } + +/* if (w2 < h2) w2 = h2; + if (h2 < w2) h2 = w2;*/ + + if (w2 != width || h2 != height) + { + /* Scale up the image bits to fit the power-of-2 texture. + We have to do this because the mipmap API assumes that + the texture bits go to texture coordinates 1.0 x 1.0. + This could be more efficient, but it doesn't happen often. + */ + int istride = (format == GL_RGBA ? 4 : 3); + int ostride = 4; + int ibpl = istride * width; + int obpl = ostride * w2; + int oy; + const unsigned char *in = (unsigned char *) data; + unsigned char *out = (void *) malloc (h2 * obpl); + Assert (out, "out of memory"); + d2 = out; + + for (oy = 0; oy < h2; oy++) + { + int iy = oy * height / h2; + const unsigned char *iline = in + (iy * ibpl); + unsigned char *oline = out + (oy * obpl); + int ox; + for (ox = 0; ox < w2; ox++) + { + int ix = ox * width / w2; + const unsigned char *i = iline + (ix * istride); + unsigned char *o = oline + (ox * ostride); + *o++ = *i++; /* R */ + *o++ = *i++; /* G */ + *o++ = *i++; /* B */ + *o++ = (istride == 4 ? *i : 0xFF); /* A */ + } + } + width = w2; + height = h2; + internalFormat = GL_RGBA; + format = GL_RGBA; + } + + jwzgles_glTexImage2D (target, 0, internalFormat, w2, h2, 0, + format, type, d2); + if (d2 != data) free (d2); + + return 0; +} + + +void +jwzgles_glRectf (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) +{ + jwzgles_glBegin (GL_POLYGON); + jwzgles_glVertex2f (x1, y1); + jwzgles_glVertex2f (x2, y1); + jwzgles_glVertex2f (x2, y2); + jwzgles_glVertex2f (x1, y2); + jwzgles_glEnd (); +} + +void +jwzgles_glRecti (GLint x1, GLint y1, GLint x2, GLint y2) +{ + jwzgles_glRectf (x1, y1, x2, y2); +} + +void +jwzgles_glClearDepth (GLfloat d) +{ + /* Not sure what to do here */ + Assert (d == 1.0, "glClearDepth unimplemented"); +} + + +void +jwzgles_glEnable (GLuint bit) +{ + Assert (!state->compiling_verts, "glEnable not allowed inside glBegin"); + if (state->compiling_list) + { + void_int vv[1]; + vv[0].i = bit; + list_push ("glEnable", (list_fn_cb) &jwzgles_glEnable, PROTO_I, vv); + } + else + { + 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; + } + } +} + + +void +jwzgles_glDisable (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; + } + } +} + + +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; + } +} + + +/* The spec says that OpenGLES 1.x doesn't implement glGetFloatv. + Were this true, it would suck, for it would mean that there was no + way to retrieve the prevailing matrixes. To implement this, we'd + have to keep track of them all on the client side by combining in + all the actions of glMultMatrixf, glRotatef, etc. + + However, Apple's iOS OpenGLES *does* provide glGetFloatv! + */ +void +jwzgles_glGetFloatv (GLenum pname, GLfloat *params) +{ + if (! state->replaying_list) + LOG2 ("direct %-12s %s", "glGetFloatv", mode_desc(pname)); + glGetFloatv (pname, params); /* the real one */ + CHECK("glGetFloatv"); +} + + +/* Likewise: not supposed to be there, but it is. */ +void +jwzgles_glGetPointerv (GLenum pname, GLvoid *params) +{ + if (! state->replaying_list) + LOG2 ("direct %-12s %s", "glGetPointerv", mode_desc(pname)); + glGetPointerv (pname, params); /* the real one */ + CHECK("glGetPointerv"); +} + + +/* How many cells are written into the *params array. + We need to know this to avoid smashing the caller's stack + if they asked for a single-value parameter. + */ +static int +glGet_ret_count (GLenum pname) +{ + switch (pname) { +/*case GL_COLOR_MATRIX: */ + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: +/*case GL_TRANSPOSE_COLOR_MATRIX: */ +/*case GL_TRANSPOSE_MODELVIEW_MATRIX: */ +/*case GL_TRANSPOSE_PROJECTION_MATRIX: */ +/*case GL_TRANSPOSE_TEXTURE_MATRIX: */ + return 16; +/*case GL_ACCUM_CLEAR_VALUE: */ +/*case GL_BLEND_COLOR: */ + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_CURRENT_COLOR: +/*case GL_CURRENT_RASTER_COLOR: */ +/*case GL_CURRENT_RASTER_POSITION: */ +/*case GL_CURRENT_RASTER_SECONDARY_COLOR: */ +/*case GL_CURRENT_RASTER_TEXTURE_COORDS: */ +/*case GL_CURRENT_SECONDARY_COLOR: */ + case GL_CURRENT_TEXTURE_COORDS: + case GL_FOG_COLOR: + case GL_LIGHT_MODEL_AMBIENT: +/*case GL_MAP2_GRID_DOMAIN: */ + case GL_SCISSOR_BOX: + case GL_VIEWPORT: + return 4; + case GL_CURRENT_NORMAL: + case GL_POINT_DISTANCE_ATTENUATION: + return 3; + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_DEPTH_RANGE: +/*case GL_LINE_WIDTH_RANGE: */ +/*case GL_MAP1_GRID_DOMAIN: */ +/*case GL_MAP2_GRID_SEGMENTS: */ + case GL_MAX_VIEWPORT_DIMS: +/*case GL_POINT_SIZE_RANGE: */ + case GL_POLYGON_MODE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + case GL_SMOOTH_POINT_SIZE_RANGE: + return 2; + default: + return 1; + } +} + + +void +jwzgles_glGetDoublev (GLenum pname, GLdouble *params) +{ + GLfloat m[16]; + int i, j = glGet_ret_count (pname); + jwzgles_glGetFloatv (pname, m); + for (i = 0; i < j; i++) + params[i] = m[i]; +} + + +void +jwzgles_glGetIntegerv (GLenum pname, GLint *params) +{ + GLfloat m[16]; + int i, j = glGet_ret_count (pname); + jwzgles_glGetFloatv (pname, m); + for (i = 0; i < j; i++) + params[i] = m[i]; +} + + +void +jwzgles_glGetBooleanv (GLenum pname, GLboolean *params) +{ + GLfloat m[16]; + int i, j = glGet_ret_count (pname); + jwzgles_glGetFloatv (pname, m); + for (i = 0; i < j; i++) + params[i] = (m[i] != 0.0); +} + + +const char * +jwzgles_gluErrorString (GLenum error) +{ + static char s[20]; + sprintf (s, "0x%lX", (unsigned long) error); + return s; +} + + +/* These can be included inside glNewList, but they actually execute + immediately anyway. + */ +void +jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride, + const GLvoid *ptr) +{ + if (! state->replaying_list) + LOG5 ("direct %-12s %d %s %d 0x%lX", "glVertexPointer", + size, mode_desc(type), stride, (unsigned long) ptr); + glVertexPointer (size, type, stride, ptr); /* the real one */ + CHECK("glVertexPointer"); +} + +void +jwzgles_glNormalPointer (GLuint type, GLuint stride, const GLvoid *ptr) +{ + if (! state->replaying_list) + LOG4 ("direct %-12s %s %d 0x%lX", "glNormalPointer", + mode_desc(type), stride, (unsigned long) ptr); + glNormalPointer (type, stride, ptr); /* the real one */ + CHECK("glNormalPointer"); +} + +void +jwzgles_glColorPointer (GLuint size, GLuint type, GLuint stride, + const GLvoid *ptr) +{ + if (! state->replaying_list) + LOG5 ("direct %-12s %d %s %d 0x%lX", "glColorPointer", + size, mode_desc(type), stride, (unsigned long) ptr); + glColorPointer (size, type, stride, ptr); /* the real one */ + CHECK("glColorPointer"); +} + +void +jwzgles_glTexCoordPointer (GLuint size, GLuint type, GLuint stride, + const GLvoid *ptr) +{ + if (! state->replaying_list) + LOG5 ("direct %-12s %d %s %d 0x%lX", "glTexCoordPointer", + size, mode_desc(type), stride, (unsigned long) ptr); + glTexCoordPointer (size, type, stride, ptr); /* the real one */ + CHECK("glTexCoordPointer"); +} + + +void +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. + */ + 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; + + if (state->compiling_list) + { + void_int vv[3]; + vv[0].i = target; + vv[1].i = pname; + vv[2].f = param; + list_push ("glTexParameterf", (list_fn_cb) &jwzgles_glTexParameterf, + PROTO_IIF, vv); + } + else + { + if (! state->replaying_list) + LOG4 ("direct %-12s %s %s %7.3f", "glTexParameterf", + mode_desc(target), mode_desc(pname), param); + glTexParameterf (target, pname, param); /* the real one */ + CHECK("glTexParameterf"); + } +} + +void +jwzgles_glTexParameteri (GLuint target, GLuint pname, GLuint param) +{ + jwzgles_glTexParameterf (target, pname, param); +} + + +/* Matrix functions, mostly cribbed from Mesa. + */ + +void +jwzgles_glFrustum (GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat near, GLfloat far) +{ + GLfloat m[16]; + GLfloat x = (2 * near) / (right-left); + GLfloat y = (2 * near) / (top - bottom); + GLfloat a = (right + left) / (right - left); + GLfloat b = (top + bottom) / (top - bottom); + GLfloat c = -(far + near) / (far - near); + GLfloat d = -(2 * far * near) / (far - near); + +# define M(X,Y) m[Y * 4 + X] + M(0,0) = x; M(0,1) = 0; M(0,2) = a; M(0,3) = 0; + M(1,0) = 0; M(1,1) = y; M(1,2) = b; M(1,3) = 0; + M(2,0) = 0; M(2,1) = 0; M(2,2) = c; M(2,3) = d; + M(3,0) = 0; M(3,1) = 0; M(3,2) = -1; M(3,3) = 0; +# undef M + + jwzgles_glMultMatrixf (m); +} + + +void +jwzgles_glOrtho (GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat near, GLfloat far) +{ + GLfloat m[16]; + GLfloat a = 2 / (right - left); + GLfloat b = -(right + left) / (right - left); + GLfloat c = 2 / (top - bottom); + GLfloat d = -(top + bottom) / (top - bottom); + GLfloat e = -2 / (far - near); + GLfloat f = -(far + near) / (far - near); + +# define M(X,Y) m[Y * 4 + X] + M(0,0) = a; M(0,1) = 0; M(0,2) = 0; M(0,3) = b; + M(1,0) = 0; M(1,1) = c; M(1,2) = 0; M(1,3) = d; + M(2,0) = 0; M(2,1) = 0; M(2,2) = e; M(2,3) = f; + M(3,0) = 0; M(3,1) = 0; M(3,2) = 0; M(3,3) = 1; +# undef M + + jwzgles_glMultMatrixf (m); +} + + +void +jwzgles_gluPerspective (GLdouble fovy, GLdouble aspect, + GLdouble near, GLdouble far) +{ + GLfloat m[16]; + double si, co, dz; + double rad = fovy / 2 * M_PI / 180; + double a, b, c, d; + + dz = far - near; + si = sin(rad); + if (dz == 0 || si == 0 || aspect == 0) + return; + co = cos(rad) / si; + + a = co / aspect; + b = co; + c = -(far + near) / dz; + d = -2 * near * far / dz; + +# define M(X,Y) m[Y * 4 + X] + M(0,0) = a; M(0,1) = 0; M(0,2) = 0; M(0,3) = 0; + M(1,0) = 0; M(1,1) = b; M(1,2) = 0; M(1,3) = 0; + M(2,0) = 0; M(2,1) = 0; M(2,2) = c; M(2,3) = d; + M(3,0) = 0; M(3,1) = 0; M(3,2) = -1; M(3,3) = 0; +# undef M + + jwzgles_glMultMatrixf (m); +} + + +void +jwzgles_gluLookAt (GLfloat eyex, GLfloat eyey, GLfloat eyez, + GLfloat centerx, GLfloat centery, GLfloat centerz, + GLfloat upx, GLfloat upy, GLfloat upz) +{ + GLfloat m[16]; + GLfloat x[3], y[3], z[3]; + GLfloat mag; + + /* Make rotation matrix */ + + /* Z vector */ + z[0] = eyex - centerx; + z[1] = eyey - centery; + z[2] = eyez - centerz; + mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); + if (mag) { /* mpichler, 19950515 */ + z[0] /= mag; + z[1] /= mag; + z[2] /= mag; + } + + /* Y vector */ + y[0] = upx; + y[1] = upy; + y[2] = upz; + + /* X vector = Y cross Z */ + x[0] = y[1] * z[2] - y[2] * z[1]; + x[1] = -y[0] * z[2] + y[2] * z[0]; + x[2] = y[0] * z[1] - y[1] * z[0]; + + /* Recompute Y = Z cross X */ + y[0] = z[1] * x[2] - z[2] * x[1]; + y[1] = -z[0] * x[2] + z[2] * x[0]; + y[2] = z[0] * x[1] - z[1] * x[0]; + + /* mpichler, 19950515 */ + /* cross product gives area of parallelogram, which is < 1.0 for + * non-perpendicular unit-length vectors; so normalize x, y here + */ + + mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); + if (mag) { + x[0] /= mag; + x[1] /= mag; + x[2] /= mag; + } + + mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); + if (mag) { + y[0] /= mag; + y[1] /= mag; + y[2] /= mag; + } + +#define M(row,col) m[col*4+row] + M(0, 0) = x[0]; M(0, 1) = x[1]; M(0, 2) = x[2]; M(0, 3) = 0.0; + M(1, 0) = y[0]; M(1, 1) = y[1]; M(1, 2) = y[2]; M(1, 3) = 0.0; + M(2, 0) = z[0]; M(2, 1) = z[1]; M(2, 2) = z[2]; M(2, 3) = 0.0; + M(3, 0) = 0.0; M(3, 1) = 0.0; M(3, 2) = 0.0; M(3, 3) = 1.0; +#undef M + + jwzgles_glMultMatrixf(m); + + /* Translate Eye to Origin */ + jwzgles_glTranslatef(-eyex, -eyey, -eyez); +} + + +static void __gluMultMatrixVecd (const GLdouble matrix[16], + const GLdouble in[4], + GLdouble out[4]) +{ + int i; + + for (i=0; i<4; i++) { + out[i] = + in[0] * matrix[0*4+i] + + in[1] * matrix[1*4+i] + + in[2] * matrix[2*4+i] + + in[3] * matrix[3*4+i]; + } +} + +GLint +jwzgles_gluProject (GLdouble objx, GLdouble objy, GLdouble objz, + const GLdouble modelMatrix[16], + const GLdouble projMatrix[16], + const GLint viewport[4], + GLdouble *winx, GLdouble *winy, GLdouble *winz) +{ + GLdouble in[4]; + GLdouble out[4]; + + /* #### I suspect this is not working right. I was seeing crazy values + in lament.c. Maybe there's some float-vs-double confusion going on? + */ + + in[0]=objx; + in[1]=objy; + in[2]=objz; + in[3]=1.0; + __gluMultMatrixVecd(modelMatrix, in, out); + __gluMultMatrixVecd(projMatrix, out, in); + if (in[3] == 0.0) return(GL_FALSE); + in[0] /= in[3]; + in[1] /= in[3]; + in[2] /= in[3]; + /* Map x, y and z to range 0-1 */ + in[0] = in[0] * 0.5 + 0.5; + in[1] = in[1] * 0.5 + 0.5; + in[2] = in[2] * 0.5 + 0.5; + + /* Map x,y to viewport */ + in[0] = in[0] * viewport[2] + viewport[0]; + in[1] = in[1] * viewport[3] + viewport[1]; + + *winx=in[0]; + *winy=in[1]; + *winz=in[2]; + return(GL_TRUE); +} + + +/* 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. + + All this CPP obscenity is me screaming in rage at all the ways that C is + not Lisp, as all I want to do here is DEFADVICE. + */ + +#define PROTO_V PROTO_VOID +#define TYPE_V GLuint +#define ARGS_V void +#define VARS_V /* */ +#define LOGS_V "\n" +#define FILL_V /* */ + +#define TYPE_I GLuint +#define TYPE_II TYPE_I +#define TYPE_III TYPE_I +#define TYPE_IIII TYPE_I +#define ARGS_I TYPE_I a +#define ARGS_II TYPE_I a, TYPE_I b +#define ARGS_III TYPE_I a, TYPE_I b, TYPE_I c +#define ARGS_IIII TYPE_I a, TYPE_I b, TYPE_I c, TYPE_I d +#define LOGS_I "%s\n", mode_desc(a) +#define LOGS_II "%s %d\n", mode_desc(a), b +#define LOGS_III "%s %s %s\n", mode_desc(a), mode_desc(b), mode_desc(c) +#define LOGS_IIII "%d %d %d %d\n", a, b, c, d +#define VARS_I a +#define VARS_II a, b +#define VARS_III a, b, c +#define VARS_IIII a, b, c, d +#define FILL_I vv[0].i = a; +#define FILL_II vv[0].i = a; vv[1].i = b; +#define FILL_III vv[0].i = a; vv[1].i = b; vv[2].i = c; +#define FILL_IIII vv[0].i = a; vv[1].i = b; vv[2].i = c; vv[3].i = d; + +#define TYPE_F GLfloat +#define TYPE_FF TYPE_F +#define TYPE_FFF TYPE_F +#define TYPE_FFFF TYPE_F +#define ARGS_F TYPE_F a +#define ARGS_FF TYPE_F a, TYPE_F b +#define ARGS_FFF TYPE_F a, TYPE_F b, TYPE_F c +#define ARGS_FFFF TYPE_F a, TYPE_F b, TYPE_F c, TYPE_F d +#define LOGS_F "%7.3f\n", a +#define LOGS_FF "%7.3f %7.3f\n", a, b +#define LOGS_FFF "%7.3f %7.3f %7.3f\n", a, b, c +#define LOGS_FFFF "%7.3f %7.3f %7.3f %7.3f\n", a, b, c, d +#define VARS_F VARS_I +#define VARS_FF VARS_II +#define VARS_FFF VARS_III +#define VARS_FFFF VARS_IIII +#define FILL_F vv[0].f = a; +#define FILL_FF vv[0].f = a; vv[1].f = b; +#define FILL_FFF vv[0].f = a; vv[1].f = b; vv[2].f = c; +#define FILL_FFFF vv[0].f = a; vv[1].f = b; vv[2].f = c; vv[3].f = d; + +#define ARGS_IF TYPE_I a, TYPE_F b +#define VARS_IF VARS_II +#define LOGS_IF "%s %7.3f\n", mode_desc(a), b +#define FILL_IF vv[0].i = a; vv[1].f = b; + +#define ARGS_IIF TYPE_I a, TYPE_I b, TYPE_F c +#define VARS_IIF VARS_III +#define LOGS_IIF "%s %s %7.3f\n", mode_desc(a), mode_desc(b), c +#define FILL_IIF vv[0].i = a; vv[1].i = b; vv[2].f = c; + +#define TYPE_IV GLint +#define ARGS_IIV TYPE_I a, const TYPE_IV *b +#define VARS_IIV VARS_II +#define LOGS_IIV "%s %d %d %d %d\n", mode_desc(a), b[0], b[1], b[2], b[3] +#define FILL_IIV vv[0].i = a; \ + vv[1].i = b[0]; vv[2].i = b[1]; \ + vv[3].i = b[2]; vv[4].i = b[3]; + +#define ARGS_IFV TYPE_I a, const TYPE_F *b +#define VARS_IFV VARS_II +#define LOGS_IFV "%s %7.3f %7.3f %7.3f %7.3f\n", mode_desc(a), \ + b[0], b[1], b[2], b[3] +#define FILL_IFV vv[0].i = a; \ + vv[1].f = b[0]; vv[2].f = b[1]; \ + vv[3].f = b[2]; vv[4].f = b[3]; + +#define ARGS_IIIV TYPE_I a, TYPE_I b, const TYPE_IV *c +#define VARS_IIIV VARS_III +#define LOGS_IIIV "%s %-8s %3d %3d %3d %3d\n", mode_desc(a), mode_desc(b), \ + c[0], c[1], c[2], c[3] +#define FILL_IIIV vv[0].i = a; vv[1].i = b; \ + vv[2].i = c[0]; vv[3].i = c[1]; \ + vv[4].i = c[2]; vv[5].i = c[3]; + +#define ARGS_IIFV TYPE_I a, TYPE_I b, const TYPE_F *c +#define VARS_IIFV VARS_III +#define LOGS_IIFV "%s %-8s %7.3f %7.3f %7.3f %7.3f\n", \ + mode_desc(a), mode_desc(b), \ + c[0], c[1], c[2], c[3] +#define FILL_IIFV vv[0].i = a; vv[1].i = b; \ + vv[2].f = c[0]; vv[3].f = c[1]; \ + vv[4].f = c[2]; vv[5].f = c[3]; + +#ifdef DEBUG +# define WLOG(NAME,ARGS) \ + fprintf (stderr, "jwzgles: direct %-12s ", NAME); \ + fprintf (stderr, ARGS) +#else +# define WLOG(NAME,ARGS) /* */ +#endif + +#define WRAP(NAME,SIG) \ +void jwzgles_##NAME (ARGS_##SIG) \ +{ \ + Assert (!state->compiling_verts, \ + STRINGIFY(NAME) " not allowed inside glBegin"); \ + if (state->compiling_list) { \ + void_int vv[10]; \ + FILL_##SIG \ + list_push (STRINGIFY(NAME), (list_fn_cb) &jwzgles_##NAME, \ + PROTO_##SIG, vv); \ + } else { \ + if (! state->replaying_list) { \ + WLOG (STRINGIFY(NAME), LOGS_##SIG); \ + } \ + NAME (VARS_##SIG); \ + CHECK(STRINGIFY(NAME)); \ + } \ +} + +WRAP (glActiveTexture, I) +WRAP (glAlphaFunc, IF) +WRAP (glBindTexture, II) +WRAP (glBlendFunc, II) +WRAP (glClear, I) +WRAP (glClearColor, FFFF) +WRAP (glClearStencil, I) +WRAP (glColorMask, IIII) +WRAP (glCullFace, I) +WRAP (glDepthFunc, I) +WRAP (glDepthMask, I) +WRAP (glFinish, V) +WRAP (glFlush, V) +WRAP (glFogf, IF) +WRAP (glFogfv, IFV) +WRAP (glFrontFace, I) +WRAP (glHint, II) +WRAP (glLightModelf, IF) +WRAP (glLightModelfv, IFV) +WRAP (glLightf, IIF) +WRAP (glLightfv, IIFV) +WRAP (glLineWidth, F) +WRAP (glLoadIdentity, V) +WRAP (glLogicOp, I) +WRAP (glMatrixMode, I) +WRAP (glPixelStorei, II) +WRAP (glPointSize, F) +WRAP (glPolygonOffset, FF) +WRAP (glPopMatrix, V) +WRAP (glPushMatrix, V) +WRAP (glRotatef, FFFF) +WRAP (glScalef, FFF) +WRAP (glScissor, IIII) +WRAP (glShadeModel, I) +WRAP (glStencilFunc, III) +WRAP (glStencilMask, I) +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) + + +#endif /* HAVE_JWZGLES - whole file */