/* xscreensaver, Copyright (c) 2012-2018 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.1, not 1.0 or 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! Lockward used to do that, before I fixed it to not. - Not every function is implemented; just the ones that I needed for xscreensaver. However, the trivial ones are trivial to enable as they come up. Harder ones will be harder. As a result of that, these savers look wrong: atlantis Uses EYE_PLANE. blocktube Uses SPHERE_MAP. dnalogo Uses GLUtesselator. extrusion Uses all kinds of GLUT crap. flyingtoasters Uses SPHERE_MAP. winduprobot Uses SPHERE_MAP. jigglypuff Uses SPHERE_MAP (in chrome mode), GL_LINE (in wireframe) jigsaw Uses GLUtesselator. pinion Uses glSelectBuffer and gluPickMatrix for mouse-clicks. pipes Uses glMap2f for the 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. */ #undef DEBUG #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef HAVE_JWZGLES /* whole file */ #include #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 #elif defined(HAVE_ANDROID) # include # include #else /* real X11 */ # ifndef GL_GLEXT_PROTOTYPES # define GL_GLEXT_PROTOTYPES /* for glBindBuffer */ # endif # include # include #endif #include "jwzglesI.h" #include "pow2.h" #define STRINGIFY(X) #X #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #undef Assert #ifdef HAVE_COCOA extern void jwxyz_abort (const char *fmt, ...) __dead2; # define Assert(C,S) do { if (!(C)) { jwxyz_abort ("%s",S); }} while(0) #elif defined HAVE_ANDROID # define Assert(C,S) do { \ if (!(C)) { \ __android_log_print (ANDROID_LOG_ERROR, "xscreensaver", "jwzgles: %s\n", S); \ abort(); \ }} 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; 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; GLshort s; GLdouble d; } void_int; typedef struct { /* saved args for glDrawArrays */ int binding, 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 { /* 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 { /* All display lists */ 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_TEXTURE_GEN_R (1<<3) #define ISENABLED_TEXTURE_GEN_Q (1<<4) #define ISENABLED_LIGHTING (1<<5) #define ISENABLED_BLEND (1<<6) #define ISENABLED_DEPTH_TEST (1<<7) #define ISENABLED_CULL_FACE (1<<8) #define ISENABLED_NORMALIZE (1<<9) #define ISENABLED_FOG (1<<10) #define ISENABLED_COLMAT (1<<11) #define ISENABLED_VERT_ARRAY (1<<12) #define ISENABLED_NORM_ARRAY (1<<13) #define ISENABLED_TEX_ARRAY (1<<14) #define ISENABLED_COLOR_ARRAY (1<<15) #define ISENABLED_ALPHA_TEST (1<<16) typedef struct { GLuint mode; GLfloat obj[4], eye[4]; } texgen_state; struct jwzgles_state { /* 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; /* enabled flags, immediate mode */ unsigned long list_enabled; /* and for the list-in-progress */ texgen_state s, t, r, q; /* Implementing glPushClientAttrib? Don't forget about these! */ draw_array varray, narray, carray, tarray; }; static jwzgles_state *state = 0; #ifdef DEBUG static void Log(const char *fmt, ...) { va_list args; va_start (args, fmt); #ifdef HAVE_ANDROID /* setprop log.redirect-stdio true is another possibility, but that apparently only works on rooted devices. */ __android_log_vprint(ANDROID_LOG_DEBUG, "xscreensaver", fmt, args); # else vfprintf(stderr, fmt, args); # endif va_end (args); } # define LOG(A) Log("jwzgles: " A "\n") # define LOG1(A,B) Log("jwzgles: " A "\n",B) # define LOG2(A,B,C) Log("jwzgles: " A "\n",B,C) # define LOG3(A,B,C,D) Log("jwzgles: " A "\n",B,C,D) # define LOG4(A,B,C,D,E) Log("jwzgles: " A "\n",B,C,D,E) # define LOG5(A,B,C,D,E,F) Log("jwzgles: " A "\n",B,C,D,E,F) # define LOG6(A,B,C,D,E,F,G) Log("jwzgles: " A "\n",B,C,D,E,F,G) # define LOG7(A,B,C,D,E,F,G,H) Log("jwzgles: " A "\n",B,C,D,E,F,G,H) # define LOG8(A,B,C,D,E,F,G,H,I)\ Log("jwzgles: "A "\n",B,C,D,E,F,G,H,I) # define LOG9(A,B,C,D,E,F,G,H,I,J)\ Log("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)\ Log("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)\ Log("jwzgles: "A "\n",B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) # define CHECK(S) jwzgles_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(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(BYTE) SS(C3F_V3F) SS(C4F_N3F_V3F) SS(C4UB_V2F) SS(C4UB_V3F) SS(CCW) SS(CLAMP) SS(COLOR_ARRAY) SS(COLOR_ARRAY_BUFFER_BINDING); 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(DOUBLEBUFFER) SS(DST_ALPHA) SS(DST_COLOR) SS(DYNAMIC_DRAW) SS(ELEMENT_ARRAY_BUFFER) SS(EYE_LINEAR) SS(EYE_PLANE) SS(FEEDBACK) SS(FILL) SS(FLAT) SS(FLOAT) 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) 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(NORMAL_ARRAY_BUFFER_BINDING); 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(RGBA_MODE) 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(STATIC_DRAW) 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_COORD_ARRAY_BUFFER_BINDING); 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(VERTEX_ARRAY_BUFFER_BINDING); /*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 jwzgles_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 */ 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_free_state (void) { /* Tricky: jwzgles_make_state doesn't require an active GLES context, but jwzgles_free_state does. */ LOG1("jwzgles_free_state %p", 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); free (state); state = NULL; } jwzgles_state * jwzgles_make_state (void) { jwzgles_state *s = (jwzgles_state *) calloc (1, sizeof (*s)); LOG1("jwzgles_make_state %p", s); s->s.mode = s->t.mode = s->r.mode = s->q.mode = GL_EYE_LINEAR; s->s.obj[0] = s->s.eye[0] = 1; /* s = 1 0 0 0 */ s->t.obj[1] = s->t.eye[1] = 1; /* t = 0 1 0 0 */ return s; } void jwzgles_make_current (jwzgles_state *s) { LOG1("jwzgles_make_current %p", s); state = s; } 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; 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]; 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; 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) { 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); optimize_arrays(); state->compiling_list = 0; state->list_enabled = state->enabled; } 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"); 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; 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 || 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); 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 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; } } } 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]); 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"); } } } 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]); 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++; } } } 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); } /* glColor: GLfloat */ 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]); 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"); } } } 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) { jwzgles_glColor3f (v[0], v[1], v[2]); } /* glColor: GLdouble */ void jwzgles_glColor4d (GLdouble r, GLdouble g, GLdouble b, GLdouble a) { jwzgles_glColor4f (r, g, b, a); } void jwzgles_glColor4dv (const GLdouble *v) { jwzgles_glColor4d (v[0], v[1], v[2], v[3]); } void jwzgles_glColor3d (GLdouble r, GLdouble g, GLdouble b) { jwzgles_glColor4d (r, g, b, 1.0); } void jwzgles_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) { 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) { 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) { /* 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_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) { /* 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_glVertex2dv (const GLdouble *v) { jwzgles_glVertex2f (v[0], v[1]); } 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--; 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]", (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! */ 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. 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. (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; 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 || ((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); } 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; 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) { \ 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; } /* 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) { 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 || fn == (list_fn_cb) &jwzgles_glBindBuffer) 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) */ { jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A[i].binding); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A[i].size); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A[i].type); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A[i].stride); jwzgles_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; jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A[i].binding); jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A[i].type); jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A[i].stride); jwzgles_glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A[i].data); CHECK("glGetPointerv"); copy_array_data (&A[i], count, "norm"); } i++; if (state->set.tcount > 1) { jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A[i].binding); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE, &A[i].size); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE, &A[i].type); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE, &A[i].stride); jwzgles_glGetPointerv (GL_TEXTURE_COORD_ARRAY_POINTER, &A[i].data); CHECK("glGetPointerv"); copy_array_data (&A[i], count, "tex "); } i++; if (state->set.ccount > 1) { jwzgles_glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A[i].binding); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A[i].size); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A[i].type); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A[i].stride); jwzgles_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; 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"); } if (A->binding) { Assert (((unsigned long) A->binding < 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; 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 = { 0, }; if (jwzgles_glIsEnabled (GL_VERTEX_ARRAY)) { jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A.size); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A.type); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A.stride); jwzgles_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; jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A.binding); jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A.type); jwzgles_glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A.stride); jwzgles_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)) { jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A.binding); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE, &A.size); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE, &A.type); jwzgles_glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE, &A.stride); jwzgles_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)) { jwzgles_glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A.binding); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A.size); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A.type); jwzgles_glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A.stride); jwzgles_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; void *data2; const GLfloat *IF; GLfloat *OF; const unsigned char *IB; unsigned char *OB; if (((unsigned long) A->data) < 0xFFFF) { Assert (0, "buffer data not a pointer"); 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; } A->data = data2; A->bytes = bytes; A->stride = stride2; # ifdef DEBUG dump_array_data (A, count, "saved", name, 0); # 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 = 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: jwzgles_glVertexPointer (A[i].size, A[i].type, A[i].stride, A[i].data); name = "vertex "; CHECK("glVertexPointer"); break; case 1: jwzgles_glNormalPointer ( A[i].type, A[i].stride, A[i].data); name = "normal "; CHECK("glNormalPointer"); break; case 2: jwzgles_glTexCoordPointer(A[i].size, A[i].type, A[i].stride, A[i].data); name = "texture"; CHECK("glTexCoordPointer"); break; case 3: jwzgles_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)name; } 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]; 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: jwzgles_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: jwzgles_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); jwzgles_glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); CHECK("glColorPointer"); c += 4*B; /* #### might be incorrect float-aligned address */ jwzgles_glVertexPointer (2, GL_FLOAT, stride, c); break; case GL_C4UB_V3F: if (stride == 0) stride = 4*B + 3*F; jwzgles_glEnableClientState (GL_COLOR_ARRAY); jwzgles_glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); CHECK("glColorPointer"); c += 4*B; jwzgles_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); jwzgles_glColorPointer (3, GL_FLOAT, stride, c); CHECK("glColorPointer"); c += 3*F; jwzgles_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); jwzgles_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; jwzgles_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); jwzgles_glColorPointer (4, GL_FLOAT, stride, c); CHECK("glColorPointer"); c += 4*F; jwzgles_glEnableClientState (GL_NORMAL_ARRAY); jwzgles_glNormalPointer (GL_FLOAT, stride, c); CHECK("glNormalPointer"); c += 3*F; jwzgles_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); jwzgles_glTexCoordPointer (2, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 2*F; jwzgles_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); jwzgles_glTexCoordPointer (4, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 4*F; jwzgles_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); jwzgles_glTexCoordPointer (2, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 2*F; jwzgles_glEnableClientState (GL_COLOR_ARRAY); jwzgles_glColorPointer (4, GL_UNSIGNED_BYTE, stride, c); CHECK("glColorPointer"); c += 4*B; jwzgles_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); jwzgles_glTexCoordPointer (2, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 2*F; jwzgles_glEnableClientState (GL_COLOR_ARRAY); jwzgles_glColorPointer (3, GL_FLOAT, stride, c); CHECK("glColorPointer"); c += 3*F; jwzgles_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); jwzgles_glTexCoordPointer (2, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 2*F; jwzgles_glEnableClientState (GL_NORMAL_ARRAY); jwzgles_glNormalPointer (GL_FLOAT, stride, c); CHECK("glNormalPointer"); c += 3*F; jwzgles_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); jwzgles_glTexCoordPointer (2, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 2*F; jwzgles_glEnableClientState (GL_COLOR_ARRAY); jwzgles_glColorPointer (3, GL_FLOAT, stride, c); CHECK("glColorPointer"); c += 3*F; jwzgles_glEnableClientState (GL_NORMAL_ARRAY); jwzgles_glNormalPointer (GL_FLOAT, stride, c); CHECK("glNormalPointer"); c += 3*F; jwzgles_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); jwzgles_glTexCoordPointer (4, GL_FLOAT, stride, c); CHECK("glTexCoordPointer"); c += 4*F; jwzgles_glEnableClientState (GL_COLOR_ARRAY); jwzgles_glColorPointer (4, GL_FLOAT, stride, c); CHECK("glColorPointer"); c += 4*F; jwzgles_glEnableClientState (GL_NORMAL_ARRAY); jwzgles_glNormalPointer (GL_FLOAT, stride, c); CHECK("glNormalPointer"); c += 3*F; jwzgles_glVertexPointer (3, GL_FLOAT, stride, c); CHECK("glVertexPointer"); break; default: Assert (0, "glInterleavedArrays: bogus format"); break; } # undef B # undef F } 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"); } void jwzgles_glTexImage1D (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *data) { Assert (!state->compiling_verts, "glTexImage1D not allowed inside glBegin"); /* technically legal, but stupid! */ Assert (!state->compiling_list, "glTexImage1D inside glNewList"); Assert (width == to_pow2(width), "width must be a power of 2"); if (target == GL_TEXTURE_1D) target = GL_TEXTURE_2D; jwzgles_glTexImage2D (target, level, internalFormat, width, 1, border, format, type, data); } 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 (type == GL_UNSIGNED_INT_8_8_8_8_REV) type = GL_UNSIGNED_BYTE; if (! state->replaying_list) LOG10 ("direct %-12s %s %d %s %d %d %d %s %s 0x%lX", "glTexImage2D", 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_glCopyTexSubImage2D (GLenum target, GLint level, GLint xoff, GLint yoff, GLint x, GLint y, GLsizei width, GLsizei height) { Assert (!state->compiling_verts, "glCopyTexSubImage2D not allowed inside glBegin"); Assert (!state->compiling_list, /* technically legal, but stupid! */ "glCopyTexSubImage2D not allowed inside glNewList"); if (! state->replaying_list) LOG9 ("direct %-12s %s %d %d %d %d %d %d %d", "glCopyTexSubImage2D", mode_desc(target), level, xoff, yoff, x, y, width, height); glCopyTexSubImage2D (target, level, /* the real one */ xoff, yoff, x, y, width, height); CHECK("glCopyTexSubImage2D"); } /* 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) { texgen_state *s; 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; } 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 jwzgles_glTexGeni (GLenum coord, GLenum pname, GLint param) { GLfloat v = 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. */ jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A.size); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A.type); jwzgles_glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A.stride); jwzgles_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, 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(). */ GLsizei w2 = (GLsizei)to_pow2(width); GLsizei h2 = (GLsizei)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"); } /* 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) { int result = (set > 0); int omitp = 0; int csp = 0; unsigned long flag = 0; switch (bit) { case GL_TEXTURE_1D: /* We implement 1D textures as 2D textures. */ case GL_TEXTURE_2D: flag = ISENABLED_TEXTURE_2D; break; case GL_TEXTURE_GEN_S: flag = ISENABLED_TEXTURE_GEN_S; omitp = 1; break; case GL_TEXTURE_GEN_T: flag = ISENABLED_TEXTURE_GEN_T; omitp = 1; break; case GL_TEXTURE_GEN_R: flag = ISENABLED_TEXTURE_GEN_R; omitp = 1; break; case GL_TEXTURE_GEN_Q: flag = ISENABLED_TEXTURE_GEN_Q; omitp = 1; break; case GL_LIGHTING: flag = ISENABLED_LIGHTING; break; case GL_BLEND: flag = ISENABLED_BLEND; break; case GL_DEPTH_TEST: flag = ISENABLED_DEPTH_TEST; break; case GL_ALPHA_TEST: flag = ISENABLED_ALPHA_TEST; break; case GL_CULL_FACE: flag = ISENABLED_CULL_FACE; break; case GL_NORMALIZE: flag = ISENABLED_NORMALIZE; break; case GL_FOG: flag = ISENABLED_FOG; break; case GL_COLOR_MATERIAL: flag = ISENABLED_COLMAT; break; /* Maybe technically these only work with glEnableClientState, but we treat that as synonymous with glEnable. */ case GL_VERTEX_ARRAY: flag = ISENABLED_VERT_ARRAY; csp = 1; break; case GL_NORMAL_ARRAY: flag = ISENABLED_NORM_ARRAY; csp = 1; break; case GL_COLOR_ARRAY: flag = ISENABLED_COLOR_ARRAY; csp = 1; break; case GL_TEXTURE_COORD_ARRAY: flag = ISENABLED_TEX_ARRAY; csp = 1; break; default: Assert (set != 0, "glIsEnabled unimplemented bit"); break; } if (set) /* setting or unsetting, not querying */ { 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); } /* Store the bit in our state as well, or query it. */ if (flag) { 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_glEnable (GLuint bit) { enable_disable (bit, 1); } void jwzgles_glDisable (GLuint bit) { enable_disable (bit, -1); } GLboolean jwzgles_glIsEnabled (GLuint bit) { return enable_disable (bit, 0); } void jwzgles_glEnableClientState (GLuint cap) { enable_disable (cap, 1); } void jwzgles_glDisableClientState (GLuint cap) { enable_disable (cap, -1); } #define GET(pname, value) \ case pname: \ *params = value; \ break; /* The spec says that OpenGLES 1.0 doesn't implement glGetFloatv. iOS provides 1.1 (and glGetFloatv by extension) at the very minimum. Android goes down to 1.0. In particular, this includes the emulator when running without GPU emulation. Actual devices that don't support 1.1 are extremely rare at this point. OpenGL ES 1.0 sucks because without glGetFloatv there is 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. Right now, we're only keeping track of the gl*Pointer functions. */ void jwzgles_glGetFloatv (GLenum pname, GLfloat *params) { if (! state->replaying_list) LOG2 ("direct %-12s %s", "glGetFloatv", mode_desc(pname)); switch (pname) { /* OpenGL ES 1.0 omits a few dozen properties that 1.1 supports. The following, however, is sufficient to get things basically working in the Android emulator. */ GET(GL_VERTEX_ARRAY_BUFFER_BINDING, state->varray.binding) GET(GL_VERTEX_ARRAY_SIZE, state->varray.size) GET(GL_VERTEX_ARRAY_TYPE, state->varray.type) GET(GL_VERTEX_ARRAY_STRIDE, state->varray.stride) GET(GL_NORMAL_ARRAY_BUFFER_BINDING, state->narray.binding) GET(GL_NORMAL_ARRAY_TYPE, state->narray.type) GET(GL_NORMAL_ARRAY_STRIDE, state->narray.stride) GET(GL_COLOR_ARRAY_BUFFER_BINDING, state->carray.binding) GET(GL_COLOR_ARRAY_SIZE, state->carray.size) GET(GL_COLOR_ARRAY_TYPE, state->carray.type) GET(GL_COLOR_ARRAY_STRIDE, state->carray.stride) GET(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, state->tarray.binding) GET(GL_TEXTURE_COORD_ARRAY_SIZE, state->tarray.size) GET(GL_TEXTURE_COORD_ARRAY_TYPE, state->tarray.type) GET(GL_TEXTURE_COORD_ARRAY_STRIDE, state->tarray.stride) default: glGetFloatv (pname, params); /* the real one */ break; } CHECK("glGetFloatv"); } void jwzgles_glGetPointerv (GLenum pname, GLvoid **params) { if (! state->replaying_list) LOG2 ("direct %-12s %s", "glGetPointerv", mode_desc(pname)); switch (pname) { GET(GL_VERTEX_ARRAY_POINTER, state->varray.data) GET(GL_NORMAL_ARRAY_POINTER, state->narray.data) GET(GL_COLOR_ARRAY_POINTER, state->carray.data) GET(GL_TEXTURE_COORD_ARRAY_POINTER, state->tarray.data) default: glGetPointerv (pname, params); /* the real one */ } CHECK("glGetPointerv"); } #undef GET /* 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 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, const GLvoid *ptr) { if (! state->replaying_list) LOG5 ("direct %-12s %d %s %d 0x%lX", "glVertexPointer", size, mode_desc(type), stride, (unsigned long) ptr); state->varray.size = size; state->varray.type = type; state->varray.stride = stride; state->varray.data = (GLvoid *)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); state->narray.type = type; state->narray.stride = stride; state->narray.data = (GLvoid *)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); state->carray.size = size; state->carray.type = type; state->carray.stride = stride; state->carray.data = (GLvoid *)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); state->tarray.size = size; state->tarray.type = type; state->tarray.stride = stride; state->tarray.data = (GLvoid *)ptr; glTexCoordPointer (size, type, stride, ptr); /* the real one */ 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) { 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; /* 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]; 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); } void jwzgles_glBindTexture (GLuint target, GLuint texture) { Assert (!state->compiling_verts, "glBindTexture not allowed inside glBegin"); /* We implement 1D textures as 2D textures. */ if (target == GL_TEXTURE_1D) target = GL_TEXTURE_2D; if (state->compiling_list) { void_int vv[2]; vv[0].i = target; vv[1].i = texture; list_push ("glBindTexture", (list_fn_cb) &jwzgles_glBindTexture, PROTO_II, vv); } /* Do it immediately as well, for generate_texture_coords */ /* else */ { if (! state->replaying_list) LOG3 ("direct %-12s %s %d", "glBindTexture", mode_desc(target), texture); glBindTexture (target, texture); /* the real one */ CHECK("glBindTexture"); } } /* Matrix functions, mostly cribbed from Mesa. */ 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); } /* OpenGL ES has different extensions vs. regular OpenGL, but the basic principle for checking for extensions is the same. */ GLboolean jwzgles_gluCheckExtension (const GLubyte *ext_name, const GLubyte *ext_string) { size_t ext_len = strlen ((const char *)ext_name); for (;;) { const GLubyte *found = (const GLubyte *)strstr ((const char *)ext_string, (const char *)ext_name); if (!found) break; char last_ch = found[ext_len]; if ((found == ext_string || found[-1] == ' ') && (last_ch == ' ' || !last_ch)) { return GL_TRUE; } ext_string = found + ext_len; } return GL_FALSE; } void jwzgles_glViewport (GLuint x, GLuint y, GLuint w, GLuint h) { # if TARGET_IPHONE_SIMULATOR /* fprintf (stderr, "glViewport %dx%d\n", w, h); */ # endif glViewport (x, y, w, h); /* the real one */ } /* The following functions are present in both OpenGL 1.1 and in OpenGLES 1, but are allowed within glNewList/glEndList, so we must wrap them to allow them to either be recorded in lists, or run directly. 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 (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) #undef TYPE_IV #define TYPE_IV GLuint WRAP (glDeleteTextures, IIV) #endif /* HAVE_JWZGLES - whole file */