-/* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2012-2014 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
As a result of that, these savers look wrong:
+ atlantis Uses EYE_PLANE.
blocktube Uses SPHERE_MAP.
- bouncingcow Uses OBJECT_LINEAR (when run with -texture)
dnalogo Uses GLUtesselator.
extrusion Uses all kinds of GLUT crap.
- flyingtoasters Uses SPHERE_MAP and OBJECT_LINEAR.
+ flyingtoasters Uses SPHERE_MAP.
+ winduprobot Uses SPHERE_MAP.
jigglypuff Uses SPHERE_MAP (in chrome mode), GL_LINE (in wireframe)
jigsaw Uses GLUtesselator.
lockward Puts verts in lists without glBegin!
pinion Uses glSelectBuffer and gluPickMatrix for mouse-clicks.
pipes Uses glMap2f for the Utah Teapot.
- polyhedra Uses GLUtesselator; also Utah Teapot.
+ polyhedra Uses GLUtesselator (concave objects); also Utah Teapot.
skytentacles Uses GL_LINE in -cel mode.
timetunnel Uses GL_CONSTANT_ALPHA and all kinds of other stuff.
#elif defined(HAVE_COCOA)
# include <OpenGL/gl.h>
# include <OpenGL/glu.h>
-#else
+#else /* X11 */
+# ifndef GL_GLEXT_PROTOTYPES
+# define GL_GLEXT_PROTOTYPES /* for glBindBuffer */
+# endif
# include <GL/glx.h>
# include <GL/glu.h>
#endif
#define countof(x) (sizeof((x))/sizeof((*x)))
#undef Assert
-#define Assert(C,S) do { \
- if (!(C)) { \
- fprintf (stderr, "jwzgles: %s\n", S); \
- abort(); \
- }} while(0)
+
+#ifdef HAVE_COCOA
+ extern void jwxyz_abort (const char *fmt, ...) __dead2;
+# define Assert(C,S) do { if (!(C)) { jwxyz_abort ("%s",S); }} while(0)
+#else
+# define Assert(C,S) do { \
+ if (!(C)) { \
+ fprintf (stderr, "jwzgles: %s\n", S); \
+ abort(); \
+ }} while(0)
+#endif
typedef struct { GLfloat x, y, z; } XYZ;
or vice versa. They tend to be passed in different registers,
and you need to know about that because it's still 1972 here.
*/
-typedef union { const void *v; GLfloat f; GLuint i; } void_int;
+typedef union {
+ const void *v; GLfloat f; GLuint i; GLshort s; GLdouble d;
+} void_int;
typedef struct { /* saved args for glDrawArrays */
- int size, type, stride, bytes;
+ int binding, size, type, stride, bytes;
void *data;
} draw_array;
} list_fn;
-typedef struct { /* saved activity within glNewList */
+typedef struct { /* a display list: saved activity within glNewList */
int id;
int size, count;
list_fn *fns;
+
+ /* Named buffer that should be freed when this display list is deleted. */
+ GLuint buffer;
+
} list;
-typedef struct { /* A display list */
+typedef struct { /* All display lists */
list *lists;
int count, size;
} list_set;
#define ISENABLED_TEXTURE_2D (1<<0)
#define ISENABLED_TEXTURE_GEN_S (1<<1)
#define ISENABLED_TEXTURE_GEN_T (1<<2)
-#define ISENABLED_LIGHTING (1<<3)
-#define ISENABLED_BLEND (1<<4)
-#define ISENABLED_DEPTH_TEST (1<<5)
-#define ISENABLED_CULL_FACE (1<<6)
-#define ISENABLED_NORMALIZE (1<<7)
-#define ISENABLED_FOG (1<<8)
-#define ISENABLED_COLMAT (1<<9)
-#define ISENABLED_VERT_ARRAY (1<<10)
-#define ISENABLED_NORM_ARRAY (1<<11)
-#define ISENABLED_TEX_ARRAY (1<<12)
-#define ISENABLED_COLOR_ARRAY (1<<13)
+#define ISENABLED_TEXTURE_GEN_R (1<<3)
+#define ISENABLED_TEXTURE_GEN_Q (1<<4)
+#define ISENABLED_LIGHTING (1<<5)
+#define ISENABLED_BLEND (1<<6)
+#define ISENABLED_DEPTH_TEST (1<<7)
+#define ISENABLED_CULL_FACE (1<<8)
+#define ISENABLED_NORMALIZE (1<<9)
+#define ISENABLED_FOG (1<<10)
+#define ISENABLED_COLMAT (1<<11)
+#define ISENABLED_VERT_ARRAY (1<<12)
+#define ISENABLED_NORM_ARRAY (1<<13)
+#define ISENABLED_TEX_ARRAY (1<<14)
+#define ISENABLED_COLOR_ARRAY (1<<15)
+
+
+typedef struct {
+ GLuint mode;
+ GLfloat obj[4], eye[4];
+} texgen_state;
typedef struct { /* global state */
int compiling_verts; /* inside glBegin */
list_set lists; /* saved lists */
- unsigned long enabled;
+
+ unsigned long enabled; /* enabled flags, immediate mode */
+ unsigned long list_enabled; /* and for the list-in-progress */
+
+ texgen_state s, t, r, q;
} jwzgles_state;
-static jwzgles_state global_state = { { 0, }, 0, 0, 0, { 0, }, 0, };
-static jwzgles_state *state = &global_state;
+static jwzgles_state *state = 0;
#ifdef DEBUG
switch (mode) {
# define SS(X) case GL_##X: return STRINGIFY(X);
SS(ALPHA)
+ SS(ALPHA_TEST)
SS(AMBIENT)
SS(AMBIENT_AND_DIFFUSE)
+ SS(ARRAY_BUFFER)
SS(AUTO_NORMAL)
SS(BACK)
SS(BLEND)
SS(BLEND_DST)
SS(BLEND_SRC)
SS(BLEND_SRC_ALPHA)
- SS(RGBA_MODE)
- SS(DOUBLEBUFFER)
- SS(GREATER)
- SS(ALPHA_TEST)
- SS(LESS)
SS(BYTE)
SS(C3F_V3F)
SS(C4F_N3F_V3F)
SS(CCW)
SS(CLAMP)
SS(COLOR_ARRAY)
+ SS(COLOR_ARRAY_BUFFER_BINDING);
SS(COLOR_MATERIAL)
SS(COLOR_MATERIAL_FACE)
SS(COLOR_MATERIAL_PARAMETER)
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(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(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(REPEAT)
SS(RGB)
SS(RGBA)
+ SS(RGBA_MODE)
SS(S)
SS(SELECT)
SS(SEPARATE_SPECULAR_COLOR)
SS(SRC_COLOR)
SS(STACK_OVERFLOW)
SS(STACK_UNDERFLOW)
+ SS(STATIC_DRAW)
SS(STENCIL_BUFFER_BIT)
SS(T)
SS(T2F_C3F_V3F)
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(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):
#endif /* DEBUG */
+static void
+make_room (const char *name, void **array, int span, int *count, int *size)
+{
+ if (*count + 1 >= *size)
+ {
+ int new_size = (*count + 20) * 1.2; /* mildly exponential */
+ *array = realloc (*array, new_size * span);
+ Assert (*array, "out of memory");
+ /* LOG3("%s: grew %d -> %d", name, *size, new_size); */
+ *size = new_size;
+ }
+}
+
+
+void
+jwzgles_reset (void)
+{
+ if (! state)
+ state = (jwzgles_state *) calloc (1, sizeof (*state));
+
+ if (state->lists.lists)
+ {
+ state->compiling_list = 0;
+ if (state->lists.count)
+ jwzgles_glDeleteLists (1, state->lists.count);
+ free (state->lists.lists);
+ }
+
+ if (state->set.verts) free (state->set.verts);
+ if (state->set.norms) free (state->set.norms);
+ if (state->set.tex) free (state->set.tex);
+ if (state->set.color) free (state->set.color);
+
+ memset (state, 0, sizeof(*state));
+
+ state->s.mode = state->t.mode = state->r.mode = state->q.mode =
+ GL_EYE_LINEAR;
+ state->s.obj[0] = state->s.eye[0] = 1; /* s = 1 0 0 0 */
+ state->t.obj[1] = state->t.eye[1] = 1; /* t = 0 1 0 0 */
+}
+
+
int
jwzgles_glGenLists (int n)
{
{
list *L;
int id = 0;
-
- /* Adding a new list at the end. Make room for it.
- */
- if (state->lists.count >= state->lists.size - 1)
- {
- int new_size = 20 + (state->lists.size * 1.2);
- state->lists.lists = (list *)
- realloc (state->lists.lists,
- new_size * sizeof (*state->lists.lists));
- Assert (state->lists.lists, "out of memory");
- state->lists.size = new_size;
- LOG1("glGenLists grew -> %d", new_size);
- }
+ make_room ("glGenLists",
+ (void **) &state->lists.lists,
+ sizeof (*state->lists.lists),
+ &state->lists.count, &state->lists.size);
state->lists.count++;
id = state->lists.count;
L = &state->lists.lists[id-1];
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->set.count == 0, "missing glEnd");
Assert (!state->compiling_verts, "glEndList not allowed inside glBegin");
LOG1("glEndList %d", state->compiling_list);
+ optimize_arrays();
state->compiling_list = 0;
+ state->list_enabled = state->enabled;
}
-static void save_arrays (list_fn *, int);
-static void restore_arrays (list_fn *, int);
-static void copy_array_data (draw_array *, int, const char *);
-
static void
list_push (const char * const name,
list_fn_cb fn, fn_proto proto, void_int *av)
L = &state->lists.lists[state->compiling_list-1];
Assert (L, "glNewList: no list");
- if (L->count >= L->size - 1)
- {
- int new_size = 20 + (L->size * 1.2);
- L->fns = (list_fn *) realloc (L->fns, new_size * sizeof (*L->fns));
- Assert (L->fns, "glNewList: no functions");
- L->size = new_size;
- }
-
+ make_room ("glNewLists",
+ (void **) &L->fns, sizeof (*L->fns),
+ &L->count, &L->size);
memset (&L->fns[L->count], 0, sizeof (*L->fns));
-
F = L->fns + L->count;
F->name = name;
LOG2 (" push %-12s %7.3f", name, av[0].f);
break;
case PROTO_II:
- if (fn == (list_fn_cb) &jwzgles_glBindTexture)
+ if (fn == (list_fn_cb) &jwzgles_glBindTexture ||
+ fn == (list_fn_cb) &jwzgles_glBindBuffer)
LOG3 (" push %-12s %s %d", name, mode_desc (av[0].i), av[1].i);
else
LOG3 (" push %-12s %d %d", name, av[0].i, av[1].i);
{
int j;
for (j = 0; j < 4; j++)
- {
- if (lf->arrays[j].data)
- free (lf->arrays[j].data);
- }
+ /* If there's a binding, 'data' is an index, not a ptr. */
+ if (!lf->arrays[j].binding &&
+ lf->arrays[j].data)
+ free (lf->arrays[j].data);
+ free (lf->arrays);
}
}
if (L->fns)
free (L->fns);
+ if (L->buffer)
+ glDeleteBuffers (1, &L->buffer);
memset (L, 0, sizeof (*L));
L->id = id;
(state->compiling_verts ? " rec " : ""),
v[0], v[1], v[2]);
- state->set.cnorm.x = v[0];
- state->set.cnorm.y = v[1];
- state->set.cnorm.z = v[2];
- state->set.ncount++;
- if (state->set.count > 0 && state->set.ncount == 1) /* not first! */
- state->set.ncount++;
-
- if (! state->compiling_verts) /* outside glBegin */
+ if (state->compiling_verts) /* inside glBegin */
+ {
+ state->set.cnorm.x = v[0];
+ state->set.cnorm.y = v[1];
+ state->set.cnorm.z = v[2];
+ state->set.ncount++;
+ if (state->set.count > 0 && state->set.ncount == 1) /* not first! */
+ state->set.ncount++;
+ }
+ else /* outside glBegin */
{
glNormal3f (v[0], v[1], v[2]);
CHECK("glNormal3f");
(state->compiling_verts ? " rec " : ""),
v[0], v[1], v[2], v[3]);
- state->set.ctex.s = v[0];
- state->set.ctex.t = v[1];
- state->set.ctex.r = v[2];
- state->set.ctex.q = v[3];
- state->set.tcount++;
- if (state->set.count > 0 && state->set.tcount == 1) /* not first! */
- state->set.tcount++;
-
Assert (state->compiling_verts, "glTexCoord4fv outside glBegin");
+
+ if (state->compiling_verts) /* inside glBegin */
+ {
+ state->set.ctex.s = v[0];
+ state->set.ctex.t = v[1];
+ state->set.ctex.r = v[2];
+ state->set.ctex.q = v[3];
+ state->set.tcount++;
+ if (state->set.count > 0 && state->set.tcount == 1) /* not first! */
+ state->set.tcount++;
+ }
}
}
}
+/* glColor: GLfloat */
void
jwzgles_glColor4fv (const GLfloat *v)
(state->compiling_verts ? " rec " : ""),
v[0], v[1], v[2], v[3]);
- state->set.ccolor.r = v[0];
- state->set.ccolor.g = v[1];
- state->set.ccolor.b = v[2];
- state->set.ccolor.a = v[3];
- state->set.ccount++;
- if (state->set.count > 0 && state->set.ccount == 1) /* not first! */
- state->set.ccount++;
-
- if (! state->compiling_verts) /* outside glBegin */
+ if (state->compiling_verts) /* inside glBegin */
+ {
+ state->set.ccolor.r = v[0];
+ state->set.ccolor.g = v[1];
+ state->set.ccolor.b = v[2];
+ state->set.ccolor.a = v[3];
+ state->set.ccount++;
+ if (state->set.count > 0 && state->set.ccount == 1) /* not first! */
+ state->set.ccount++;
+ }
+ else /* outside glBegin */
{
glColor4f (v[0], v[1], v[2], v[3]);
CHECK("glColor4");
jwzgles_glColor4fv (v);
}
-
void
jwzgles_glColor3f (GLfloat r, GLfloat g, GLfloat b)
{
jwzgles_glColor4f (r, g, b, 1);
}
-
void
jwzgles_glColor3fv (const GLfloat *v)
{
- GLfloat vv[4];
- vv[0] = v[0];
- vv[1] = v[1];
- vv[2] = v[2];
- vv[3] = 1;
- jwzgles_glColor4fv (vv);
+ jwzgles_glColor3f (v[0], v[1], v[2]);
}
+/* glColor: GLdouble */
+
void
-jwzgles_glColor4i (GLuint r, GLuint g, GLuint b, GLuint a)
+jwzgles_glColor4d (GLdouble r, GLdouble g, GLdouble b, GLdouble a)
{
jwzgles_glColor4f (r, g, b, a);
}
+void
+jwzgles_glColor4dv (const GLdouble *v)
+{
+ jwzgles_glColor4d (v[0], v[1], v[2], v[3]);
+}
void
-jwzgles_glColor3i (GLuint r, GLuint g, GLuint b)
+jwzgles_glColor3d (GLdouble r, GLdouble g, GLdouble b)
{
- jwzgles_glColor4f (r, g, b, 1);
+ jwzgles_glColor4d (r, g, b, 1.0);
+}
+
+void
+jwzgles_glColor3dv (const GLdouble *v)
+{
+ jwzgles_glColor3d (v[0], v[1], v[2]);
}
+/* glColor: GLint (INT_MIN - INT_MAX) */
+
+void
+jwzgles_glColor4i (GLint r, GLint g, GLint b, GLint a)
+{
+ /* -0x8000000 - 0x7FFFFFFF => 0.0 - 1.0 */
+ jwzgles_glColor4f (0.5 + (GLfloat) r / 0xFFFFFFFF,
+ 0.5 + (GLfloat) g / 0xFFFFFFFF,
+ 0.5 + (GLfloat) b / 0xFFFFFFFF,
+ 0.5 + (GLfloat) a / 0xFFFFFFFF);
+}
+
void
jwzgles_glColor4iv (const GLint *v)
{
- GLfloat vv[4];
- vv[0] = v[0];
- vv[1] = v[1];
- vv[2] = v[2];
- vv[3] = v[3];
- jwzgles_glColor4fv (vv);
+ jwzgles_glColor4i (v[0], v[1], v[2], v[3]);
}
+void
+jwzgles_glColor3i (GLint r, GLint g, GLint b)
+{
+ jwzgles_glColor4i (r, g, b, 0x7FFFFFFF);
+}
+
void
jwzgles_glColor3iv (const GLint *v)
{
- GLfloat vv[4];
- vv[0] = v[0];
- vv[1] = v[1];
- vv[2] = v[2];
- vv[3] = 1;
- jwzgles_glColor4fv (vv);
+ jwzgles_glColor3i (v[0], v[1], v[2]);
}
+/* glColor: GLuint (0 - UINT_MAX) */
+
+void
+jwzgles_glColor4ui (GLuint r, GLuint g, GLuint b, GLuint a)
+{
+ /* 0 - 0xFFFFFFFF => 0.0 - 1.0 */
+ jwzgles_glColor4f ((GLfloat) r / 0xFFFFFFFF,
+ (GLfloat) g / 0xFFFFFFFF,
+ (GLfloat) b / 0xFFFFFFFF,
+ (GLfloat) a / 0xFFFFFFFF);
+}
+
+void
+jwzgles_glColor4uiv (const GLuint *v)
+{
+ jwzgles_glColor4ui (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3ui (GLuint r, GLuint g, GLuint b)
+{
+ jwzgles_glColor4ui (r, g, b, 0xFFFFFFFF);
+}
+
+void
+jwzgles_glColor3uiv (const GLuint *v)
+{
+ jwzgles_glColor3ui (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLshort (SHRT_MIN - SHRT_MAX) */
+
+void
+jwzgles_glColor4s (GLshort r, GLshort g, GLshort b, GLshort a)
+{
+ /* -0x8000 - 0x7FFF => 0.0 - 1.0 */
+ jwzgles_glColor4f (0.5 + (GLfloat) r / 0xFFFF,
+ 0.5 + (GLfloat) g / 0xFFFF,
+ 0.5 + (GLfloat) b / 0xFFFF,
+ 0.5 + (GLfloat) a / 0xFFFF);
+}
+
+void
+jwzgles_glColor4sv (const GLshort *v)
+{
+ jwzgles_glColor4s (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3s (GLshort r, GLshort g, GLshort b)
+{
+ jwzgles_glColor4s (r, g, b, 0x7FFF);
+}
+
+void
+jwzgles_glColor3sv (const GLshort *v)
+{
+ jwzgles_glColor3s (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLushort (0 - USHRT_MAX) */
+
+void
+jwzgles_glColor4us (GLushort r, GLushort g, GLushort b, GLushort a)
+{
+ /* 0 - 0xFFFF => 0.0 - 1.0 */
+ jwzgles_glColor4f ((GLfloat) r / 0xFFFF,
+ (GLfloat) g / 0xFFFF,
+ (GLfloat) b / 0xFFFF,
+ (GLfloat) a / 0xFFFF);
+}
+
+void
+jwzgles_glColor4usv (const GLushort *v)
+{
+ jwzgles_glColor4us (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3us (GLushort r, GLushort g, GLushort b)
+{
+ jwzgles_glColor4us (r, g, b, 0xFFFF);
+}
+
+void
+jwzgles_glColor3usv (const GLushort *v)
+{
+ jwzgles_glColor3us (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLbyte (-128 - 127) */
+
+void
+jwzgles_glColor4b (GLbyte r, GLbyte g, GLbyte b, GLbyte a)
+{
+ /* -128 - 127 => 0.0 - 1.0 */
+ jwzgles_glColor4f (0.5 + (GLfloat) r / 255,
+ 0.5 + (GLfloat) g / 255,
+ 0.5 + (GLfloat) b / 255,
+ 0.5 + (GLfloat) a / 255);
+}
+
+void
+jwzgles_glColor4bv (const GLbyte *v)
+{
+ jwzgles_glColor4b (v[0], v[1], v[2], v[3]);
+}
+
+void
+jwzgles_glColor3b (GLbyte r, GLbyte g, GLbyte b)
+{
+ jwzgles_glColor4b (r, g, b, 127);
+}
+
+void
+jwzgles_glColor3bv (const GLbyte *v)
+{
+ jwzgles_glColor3b (v[0], v[1], v[2]);
+}
+
+
+/* glColor: GLubyte (0 - 255) */
+
void
jwzgles_glColor4ub (GLubyte r, GLubyte g, GLubyte b, GLubyte a)
{
- jwzgles_glColor4f (r, g, b, a);
+ /* 0 - 255 => 0.0 - 1.0 */
+ jwzgles_glColor4f (r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}
+void
+jwzgles_glColor4ubv (const GLubyte *v)
+{
+ jwzgles_glColor4ub (v[0], v[1], v[2], v[3]);
+}
void
jwzgles_glColor3ub (GLubyte r, GLubyte g, GLubyte b)
{
- jwzgles_glColor4f (r, g, b, 1);
+ jwzgles_glColor4ub (r, g, b, 255);
}
+void
+jwzgles_glColor3ubv (const GLubyte *v)
+{
+ jwzgles_glColor3ub (v[0], v[1], v[2]);
+}
+
+
void
jwzgles_glMaterialfv (GLenum face, GLenum pname, const GLfloat *color)
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]",
else if (s->mode == GL_POLYGON)
s->mode = GL_TRIANGLE_FAN; /* They do the same thing! */
- glVertexPointer (4, GL_FLOAT, sizeof(*s->verts), s->verts); /* XYZW */
- glNormalPointer ( GL_FLOAT, sizeof(*s->norms), s->norms); /* XYZ */
- glTexCoordPointer (4, GL_FLOAT, sizeof(*s->tex), s->tex); /* STRQ */
- glColorPointer (4, GL_FLOAT, sizeof(*s->color), s->color); /* RGBA */
- CHECK("glColorPointer");
+ jwzgles_glColorPointer (4,GL_FLOAT, sizeof(*s->color),s->color); /* RGBA */
+ jwzgles_glNormalPointer ( GL_FLOAT, sizeof(*s->norms),s->norms); /* XYZ */
+ jwzgles_glTexCoordPointer(4,GL_FLOAT, sizeof(*s->tex), s->tex); /* STRQ */
+ jwzgles_glVertexPointer (4,GL_FLOAT, sizeof(*s->verts),s->verts); /* XYZW */
+ /* glVertexPointer must come after glTexCoordPointer */
/* If there were no calls to glNormal3f inside of glBegin/glEnd,
don't bother enabling the normals array.
Be careful to leave the arrays' enabled/disabled state the same as
before, or a later caller might end up using one of our arrays by
- mistake. #### Actually this isn't quite right: if glEnd is in a
- list, it saves the trailing enable/disable calls in the list, instead
- if restoring them to what their state was before the list was run.
+ mistake. (Remember that jwzgles_glIsEnabled() tracks the enablement
+ of the list-in-progress as well as the global state.)
*/
was_norm = jwzgles_glIsEnabled (GL_NORMAL_ARRAY);
was_tex = jwzgles_glIsEnabled (GL_TEXTURE_COORD_ARRAY);
was_color = jwzgles_glIsEnabled (GL_COLOR_ARRAY);
was_mat = jwzgles_glIsEnabled (GL_COLOR_MATERIAL);
+ /* If we're executing glEnd in immediate mode, not from inside a display
+ list (which is the only way it happens, because glEnd doesn't go into
+ display lists), make sure we're not stomping on a saved buffer list:
+ in immediate mode, vertexes are client-side only.
+ */
+ if (! state->compiling_list)
+ jwzgles_glBindBuffer (GL_ARRAY_BUFFER, 0);
+
if (s->ncount > 1)
{
is_norm = 1;
jwzgles_glDisableClientState (GL_NORMAL_ARRAY);
}
- if (s->tcount > 1)
+ if (s->tcount > 1 ||
+ ((state->compiling_list ? state->list_enabled : state->enabled)
+ & (ISENABLED_TEXTURE_GEN_S | ISENABLED_TEXTURE_GEN_T |
+ ISENABLED_TEXTURE_GEN_R | ISENABLED_TEXTURE_GEN_Q)))
{
+ /* Enable texture coords if any were specified; or if generation
+ is on in immediate mode; or if this list turned on generation. */
is_tex = 1;
jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY);
}
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) { \
}
+/* 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)
{
break;
case PROTO_II:
- if (fn == (list_fn_cb) &jwzgles_glBindTexture)
+ if (fn == (list_fn_cb) &jwzgles_glBindTexture ||
+ fn == (list_fn_cb) &jwzgles_glBindBuffer)
LOG3 (" call %-12s %s %d", F->name,
mode_desc (av[0].i), av[1].i);
else
/* if (state->set.count > 0) */
{
+ glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A[i].binding);
glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A[i].size);
glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A[i].type);
glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A[i].stride);
if (state->set.ncount > 1)
{
A[i].size = 3;
+ glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A[i].binding);
glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A[i].type);
glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A[i].stride);
glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A[i].data);
i++;
if (state->set.tcount > 1)
{
+ glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A[i].binding);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE, &A[i].size);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE, &A[i].type);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE, &A[i].stride);
i++;
if (state->set.ccount > 1)
{
+ glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A[i].binding);
glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A[i].size);
glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A[i].type);
glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A[i].stride);
{
int bytes = count * A->stride;
- Assert (bytes == A->bytes, "array data corrupted");
+ if (A->binding)
+ {
+ fprintf (stderr,
+ "jwzgles: %s %s %d %s %2d, %4d = %5d bind %d @ %d\n",
+ action, name,
+ A->size, mode_desc(A->type), A->stride,
+ count, bytes, A->binding, (int) A->data);
+ }
+ else
+ {
+ Assert (bytes == A->bytes, "array data corrupted");
+
+ fprintf (stderr, "jwzgles: %s %s %d %s %2d, %4d = %5d @ %lX",
+ action, name,
+ A->size, mode_desc(A->type), A->stride,
+ count, bytes, (unsigned long) A->data);
+ if (old)
+ fprintf (stderr, " / %lX", (unsigned long) old);
+ fprintf (stderr, "\n");
+ }
- fprintf (stderr, "jwzgles: %s %s %d %s %2d, %4d = %5d @ %lX",
- action, name,
- A->size, mode_desc(A->type), A->stride,
- count, bytes, (unsigned long) A->data);
- if (old)
- fprintf (stderr, " / %lX", (unsigned long) old);
- fprintf (stderr, "\n");
+ if (A->binding)
+ {
+ Assert (((unsigned long) A->data < 0xFFFF),
+ "buffer binding should be a numeric index,"
+ " but looks like a pointer");
# if 0
- {
+ /* glGetBufferSubData doesn't actually exist in OpenGLES, but this
+ was helpful for debugging on real OpenGL... */
+ GLfloat *d;
+ int i;
+ fprintf (stderr, "jwzgles: read back:\n");
+ d = (GLfloat *) malloc (A->bytes);
+ glGetBufferSubData (GL_ARRAY_BUFFER, (int) A->data,
+ count * A->stride, (void *) d);
+ CHECK("glGetBufferSubData");
+ for (i = 0; i < count * A->size; i++)
+ {
+ if (i % 4 == 0)
+ fprintf (stderr, "\njwzgles: %4d: ",
+ i + (int) A->data / sizeof(GLfloat));
+ fprintf (stderr, " %7.3f", d[i]);
+ }
+ fprintf (stderr, "\n");
+ free (d);
+# endif
+ }
+# if 0
+ else
+ {
unsigned char *b = (unsigned char *) A->data;
int i;
+ if ((unsigned long) A->data < 0xFFFF)
+ {
+ Assert (0, "buffer data not a pointer");
+ return;
+ }
for (i = 0; i < count; i++)
{
int j;
static void
dump_direct_array_data (int count)
{
- draw_array A;
+ draw_array A = { 0, };
if (jwzgles_glIsEnabled (GL_VERTEX_ARRAY))
{
+ glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding);
glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A.size);
glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A.type);
glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A.stride);
if (jwzgles_glIsEnabled (GL_NORMAL_ARRAY))
{
A.size = 0;
+ glGetIntegerv (GL_NORMAL_ARRAY_BUFFER_BINDING, &A.binding);
glGetIntegerv (GL_NORMAL_ARRAY_TYPE, &A.type);
glGetIntegerv (GL_NORMAL_ARRAY_STRIDE, &A.stride);
glGetPointerv (GL_NORMAL_ARRAY_POINTER, &A.data);
}
if (jwzgles_glIsEnabled (GL_TEXTURE_COORD_ARRAY))
{
+ glGetIntegerv (GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &A.binding);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_SIZE, &A.size);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_TYPE, &A.type);
glGetIntegerv (GL_TEXTURE_COORD_ARRAY_STRIDE, &A.stride);
}
if (jwzgles_glIsEnabled (GL_COLOR_ARRAY))
{
+ glGetIntegerv (GL_COLOR_ARRAY_BUFFER_BINDING, &A.binding);
glGetIntegerv (GL_COLOR_ARRAY_SIZE, &A.size);
glGetIntegerv (GL_COLOR_ARRAY_TYPE, &A.type);
glGetIntegerv (GL_COLOR_ARRAY_STRIDE, &A.stride);
data multiple times.
*/
int stride2, bytes, i, j;
- const void *old;
void *data2;
const GLfloat *IF;
GLfloat *OF;
const unsigned char *IB;
unsigned char *OB;
- if (! A->data) return;
+ if (((unsigned long) A->data) < 0xFFFF)
+ {
+ Assert (0, "buffer data not a pointer");
+ return;
+ }
Assert (A->size >= 2 && A->size <= 4, "bogus array size");
break;
}
- old = A->data;
A->data = data2;
A->bytes = bytes;
A->stride = stride2;
# ifdef DEBUG
- dump_array_data (A, count, "saved", name, old);
+ dump_array_data (A, count, "saved", name, 0);
# endif
}
for (i = 0; i < 4; i++)
{
- const char *name;
- if (! A[i].data) continue;
+ const char *name = 0;
+
+ if (!A[i].size)
+ continue;
+
+ Assert ((A[i].binding || A[i].data),
+ "array has neither buffer binding nor data");
+
+ glBindBuffer (GL_ARRAY_BUFFER, A[i].binding);
+ CHECK("glBindBuffer");
+
switch (i) {
case 0: glVertexPointer (A[i].size, A[i].type, A[i].stride, A[i].data);
name = "vertex ";
dump_array_data (&A[i], count, "restored", name, 0);
# endif
}
+
+ glBindBuffer (GL_ARRAY_BUFFER, 0); /* Keep out of others' hands */
}
void
jwzgles_glDrawArrays (GLuint mode, GLuint first, GLuint count)
{
+ /* If we are auto-generating texture coordinates, do that now, after
+ the vertex array was installed, but before drawing, This happens
+ when recording into a list, or in direct mode. It must happen
+ before calling optimize_arrays() from glEndList().
+ */
+ if (! state->replaying_list &&
+ ((state->compiling_list ? state->list_enabled : state->enabled)
+ & (ISENABLED_TEXTURE_GEN_S | ISENABLED_TEXTURE_GEN_T |
+ ISENABLED_TEXTURE_GEN_R | ISENABLED_TEXTURE_GEN_Q)))
+ generate_texture_coords (first, count);
+
if (state->compiling_list)
{
void_int vv[3];
-void
-jwzgles_glEnableClientState (GLuint cap)
-{
- if (state->compiling_list)
- {
- void_int vv[1];
- vv[0].i = cap;
- list_push ("glEnableClientState",
- (list_fn_cb) &jwzgles_glEnableClientState,
- PROTO_I, vv);
- }
- else
- {
- if (! state->replaying_list)
- LOG2 ("direct %-12s %s", "glEnableClientState", mode_desc(cap));
- glEnableClientState (cap); /* the real one */
- CHECK("glEnableClientState");
- }
-
- switch (cap) {
- case GL_NORMAL_ARRAY:
- state->set.ncount += 2;
- state->enabled |= ISENABLED_NORM_ARRAY;
- break;
- case GL_TEXTURE_COORD_ARRAY:
- state->set.tcount += 2;
- state->enabled |= ISENABLED_TEX_ARRAY;
- break;
- case GL_COLOR_ARRAY:
- state->set.ccount += 2;
- state->enabled |= ISENABLED_COLOR_ARRAY;
- break;
- default: break;
- }
-}
-
-
-void
-jwzgles_glDisableClientState (GLuint cap)
-{
- if (state->compiling_list)
- {
- void_int vv[1];
- vv[0].i = cap;
- list_push ("glDisableClientState",
- (list_fn_cb) &jwzgles_glDisableClientState,
- PROTO_I, vv);
- }
- else
- {
- if (! state->replaying_list)
- LOG2 ("direct %-12s %s", "glDisableClientState", mode_desc(cap));
- glDisableClientState (cap); /* the real one */
- CHECK("glDisableClientState");
- }
-
- switch (cap) {
- case GL_NORMAL_ARRAY:
- state->set.ncount = 0;
- state->enabled &= ~ISENABLED_NORM_ARRAY;
- break;
- case GL_TEXTURE_COORD_ARRAY:
- state->set.tcount = 0;
- state->enabled &= ~ISENABLED_TEX_ARRAY;
- break;
- case GL_COLOR_ARRAY:
- state->set.ccount = 0;
- state->enabled &= ~ISENABLED_COLOR_ARRAY;
- break;
- default:
- break;
- }
-}
-
-
void
jwzgles_glMultMatrixf (const GLfloat *m)
{
}
+/* OpenGLES doesn't have auto texture-generation at all!
+ "Oh, just rewrite that code to use GPU shaders", they say.
+ How fucking convenient.
+ */
void
jwzgles_glTexGenfv (GLenum coord, GLenum pname, const GLfloat *params)
{
- /* OpenGLES doesn't have this at all!
- "Oh, just rewrite that code to use GPU shaders", they say.
- How fucking convenient.
+ texgen_state *s;
- So, when this is enabled, we could emit a GL_TEXTURE_COORD_ARRAY
- and compute coords for each vertex in the current GL_VERTEX_ARRAY
- as per http://www.opengl.org/wiki/Mathematics_of_glTexGen
- but holy shit, what a pain in the ass!
-
- For GL_OBJECT_LINEAR, we can just re-use the vertex array as
- the texture array, using a proper stride. That's hardly worth
- the effort, though, because bouncingcow is the only hack that
- uses that, and not even by default.
- */
- Assert (coord == GL_S || coord == GL_T, "glTexGenfv: unimplemented coord");
-
- /* This is probably default-ish, so do nothing. */
- if (pname == GL_EYE_PLANE) return;
+ if (pname == GL_TEXTURE_GEN_MODE)
+ LOG5 ("%sdirect %-12s %s %s %s",
+ (state->compiling_list || state->replaying_list ? " " : ""),
+ "glTexGenfv",
+ mode_desc(coord), mode_desc(pname), mode_desc(params[0]));
+ else
+ LOG8 ("%sdirect %-12s %s %s %3.1f %3.1f %3.1f %3.1f",
+ (state->compiling_list || state->replaying_list ? " " : ""),
+ "glTexGenfv",
+ mode_desc(coord), mode_desc(pname),
+ params[0], params[1], params[2], params[3]);
+
+ switch (coord) {
+ case GL_S: s = &state->s; break;
+ case GL_T: s = &state->t; break;
+ case GL_R: s = &state->r; break;
+ case GL_Q: s = &state->q; break;
+ default: Assert (0, "glGetTexGenfv: unknown coord"); break;
+ }
- Assert (pname == GL_TEXTURE_GEN_MODE, "glTexGenfv: unimplemented name");
- Assert (params[0] == GL_EYE_LINEAR, "glTexGenfv: unimplemented mode");
+ switch (pname) {
+ case GL_TEXTURE_GEN_MODE: s->mode = params[0]; break;
+ case GL_OBJECT_PLANE: memcpy (s->obj, params, sizeof(s->obj)); break;
+ case GL_EYE_PLANE: memcpy (s->eye, params, sizeof(s->eye)); break;
+ default: Assert (0, "glTexGenfv: unknown pname"); break;
+ }
}
void
jwzgles_glTexGenfv (coord, pname, &v);
}
+void
+jwzgles_glGetTexGenfv (GLenum coord, GLenum pname, GLfloat *params)
+{
+ texgen_state *s;
+
+ switch (coord) {
+ case GL_S: s = &state->s; break;
+ case GL_T: s = &state->t; break;
+ case GL_R: s = &state->r; break;
+ case GL_Q: s = &state->q; break;
+ default: Assert (0, "glGetTexGenfv: unknown coord"); break;
+ }
+
+ switch (pname) {
+ case GL_TEXTURE_GEN_MODE: params[0] = s->mode; break;
+ case GL_OBJECT_PLANE: memcpy (params, s->obj, sizeof(s->obj)); break;
+ case GL_EYE_PLANE: memcpy (params, s->eye, sizeof(s->eye)); break;
+ default: Assert (0, "glGetTexGenfv: unknown pname"); break;
+ }
+
+ if (pname == GL_TEXTURE_GEN_MODE)
+ LOG5 ("%sdirect %-12s %s %s -> %s",
+ (state->compiling_list || state->replaying_list ? " " : ""),
+ "glGetTexGenfv",
+ mode_desc(coord), mode_desc(pname), mode_desc(params[0]));
+ else
+ LOG8 ("%sdirect %-12s %s %s -> %3.1f %3.1f %3.1f %3.1f",
+ (state->compiling_list || state->replaying_list ? " " : ""),
+ "glGetTexGenfv",
+ mode_desc(coord), mode_desc(pname),
+ params[0], params[1], params[2], params[3]);
+}
+
+
+static GLfloat
+dot_product (int rank, GLfloat *a, GLfloat *b)
+{
+ /* A dot B => (A[1] * B[1]) + ... + (A[n] * B[n]) */
+ GLfloat ret = 0;
+ int i;
+ for (i = 0; i < rank; i++)
+ ret += a[i] * b[i];
+ return ret;
+}
+
+
+
+/* Compute the texture coordinates of the prevailing list of verts as per
+ http://www.opengl.org/wiki/Mathematics_of_glTexGen
+ */
+static void
+generate_texture_coords (GLuint first, GLuint count)
+{
+ GLfloat *tex_out, *tex_array;
+ GLsizei tex_stride;
+ GLuint i;
+ draw_array A = { 0, };
+ char *verts_in;
+
+ struct { GLuint which, flag, mode; GLfloat plane[4]; } tg[4] = {
+ { GL_S, ISENABLED_TEXTURE_GEN_S, 0, { 0, } },
+ { GL_T, ISENABLED_TEXTURE_GEN_T, 0, { 0, } },
+ { GL_R, ISENABLED_TEXTURE_GEN_R, 0, { 0, } },
+ { GL_Q, ISENABLED_TEXTURE_GEN_Q, 0, { 0, }}};
+
+ int tcoords = 0;
+
+ /* Read the texture plane configs that were stored with glTexGen.
+ */
+ for (i = 0; i < countof(tg); i++)
+ {
+ GLfloat mode = 0;
+ if (! ((state->compiling_list ? state->list_enabled : state->enabled)
+ & tg[i].flag))
+ continue;
+ jwzgles_glGetTexGenfv (tg[i].which, GL_TEXTURE_GEN_MODE, &mode);
+ jwzgles_glGetTexGenfv (tg[i].which, GL_OBJECT_PLANE, tg[i].plane);
+ tg[i].mode = mode;
+ tcoords++;
+ }
+
+ if (tcoords == 0) return; /* Nothing to do! */
+
+
+ /* Make the array to store our texture coords in. */
+
+ tex_stride = tcoords * sizeof(GLfloat);
+ tex_array = (GLfloat *) calloc (first + count, tex_stride);
+ tex_out = tex_array;
+
+
+ /* Read the prevailing vertex array, that was stored with
+ glVertexPointer or glInterleavedArrays.
+ */
+ glGetIntegerv (GL_VERTEX_ARRAY_BUFFER_BINDING, &A.binding);
+ glGetIntegerv (GL_VERTEX_ARRAY_SIZE, &A.size);
+ glGetIntegerv (GL_VERTEX_ARRAY_TYPE, &A.type);
+ glGetIntegerv (GL_VERTEX_ARRAY_STRIDE, &A.stride);
+ glGetPointerv (GL_VERTEX_ARRAY_POINTER, &A.data);
+ A.bytes = count * A.stride;
+
+ verts_in = (char *) A.data;
+
+ /* Iterate over each vertex we're drawing.
+ We just skip the ones < start, but the tex array has
+ left room for zeroes there anyway.
+ */
+ for (i = first; i < first + count; i++)
+ {
+ GLfloat vert[4] = { 0, };
+ int j, k;
+
+ /* Extract this vertex into `vert' as a float, whatever its type was. */
+ for (j = 0; j < A.size; j++)
+ {
+ switch (A.type) {
+ case GL_SHORT: vert[j] = ((GLshort *) verts_in)[j]; break;
+ case GL_INT: vert[j] = ((GLint *) verts_in)[j]; break;
+ case GL_FLOAT: vert[j] = ((GLfloat *) verts_in)[j]; break;
+ case GL_DOUBLE: vert[j] = ((GLdouble *) verts_in)[j]; break;
+ default: Assert (0, "unknown vertex type"); break;
+ }
+ }
+
+ /* Compute the texture coordinate for this vertex.
+ For GL_OBJECT_LINEAR, these coordinates are static, and can go
+ into the display list. But for GL_EYE_LINEAR, GL_SPHERE_MAP and
+ GL_REFLECTION_MAP, they depend on the prevailing ModelView matrix,
+ and so need to be computed afresh each time glDrawArrays is called.
+ Unfortunately, our verts and norms are gone by then, dumped down
+ into the VBO and discarded from CPU RAM. Bleh.
+ */
+ for (j = 0, k = 0; j < countof(tg); j++)
+ {
+ if (! ((state->compiling_list ? state->list_enabled : state->enabled)
+ & tg[j].flag))
+ continue;
+ switch (tg[j].mode) {
+ case GL_OBJECT_LINEAR:
+ tex_out[k] = dot_product (4, vert, tg[j].plane);
+ break;
+ default:
+ Assert (0, "unimplemented texture mode");
+ break;
+ }
+ k++;
+ }
+
+ /* fprintf (stderr, "%4d: V %-5.1f %-5.1f %-5.1f T %-5.1f %-5.1f\n",
+ i, vert[0], vert[1], vert[2], tex_out[0], tex_out[1]); */
+
+ /* Move verts_in and tex_out forward to the next vertex by stride. */
+ verts_in += A.stride;
+ tex_out = (GLfloat *) (((char *) tex_out) + tex_stride);
+ }
+
+ jwzgles_glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ jwzgles_glTexCoordPointer (tcoords, GL_FLOAT, tex_stride,
+ (GLvoid *) tex_array);
+ free (tex_array);
+}
+
int
jwzgles_gluBuild2DMipmaps (GLenum target,
}
-void
-jwzgles_glEnable (GLuint bit)
+/* When in immediate mode, we store a bit into state->enabled, and also
+ call the real glEnable() / glDisable().
+
+ When recording a list, we store a bit into state->list_enabled instead,
+ so that we can see what the prevailing enablement state will be when
+ the list is run.
+
+ set: 1 = set, -1 = clear, 0 = query.
+*/
+static int
+enable_disable (GLuint bit, int set)
{
- Assert (!state->compiling_verts, "glEnable not allowed inside glBegin");
- if (state->compiling_list)
+ int result = (set > 0);
+ int omitp = 0;
+ int csp = 0;
+ unsigned long flag = 0;
+
+ switch (bit) {
+ case GL_TEXTURE_1D: /* We implement 1D textures as 2D textures. */
+ case GL_TEXTURE_2D: flag = ISENABLED_TEXTURE_2D; break;
+ case GL_TEXTURE_GEN_S: flag = ISENABLED_TEXTURE_GEN_S; omitp = 1; break;
+ case GL_TEXTURE_GEN_T: flag = ISENABLED_TEXTURE_GEN_T; omitp = 1; break;
+ case GL_TEXTURE_GEN_R: flag = ISENABLED_TEXTURE_GEN_R; omitp = 1; break;
+ case GL_TEXTURE_GEN_Q: flag = ISENABLED_TEXTURE_GEN_Q; omitp = 1; break;
+ case GL_LIGHTING: flag = ISENABLED_LIGHTING; break;
+ case GL_BLEND: flag = ISENABLED_BLEND; break;
+ case GL_DEPTH_TEST: flag = ISENABLED_DEPTH_TEST; break;
+ case GL_CULL_FACE: flag = ISENABLED_CULL_FACE; break;
+ case GL_NORMALIZE: flag = ISENABLED_NORMALIZE; break;
+ case GL_FOG: flag = ISENABLED_FOG; break;
+ case GL_COLOR_MATERIAL: flag = ISENABLED_COLMAT; break;
+
+ /* Maybe technically these only work with glEnableClientState,
+ but we treat that as synonymous with glEnable. */
+ case GL_VERTEX_ARRAY: flag = ISENABLED_VERT_ARRAY; csp = 1; break;
+ case GL_NORMAL_ARRAY: flag = ISENABLED_NORM_ARRAY; csp = 1; break;
+ case GL_COLOR_ARRAY: flag = ISENABLED_COLOR_ARRAY; csp = 1; break;
+ case GL_TEXTURE_COORD_ARRAY: flag = ISENABLED_TEX_ARRAY; csp = 1; break;
+
+ default:
+ Assert (set != 0, "glIsEnabled unimplemented bit");
+ break;
+ }
+
+ if (set) /* setting or unsetting, not querying */
{
- void_int vv[1];
- vv[0].i = bit;
- list_push ("glEnable", (list_fn_cb) &jwzgles_glEnable, PROTO_I, vv);
+ const char *fns[4] = { "glEnable", "glDisable",
+ "glEnableClientState", "glDisableClientState" };
+ list_fn_cb fs[4] = { (list_fn_cb) &jwzgles_glEnable,
+ (list_fn_cb) &jwzgles_glDisable,
+ (list_fn_cb) &jwzgles_glEnableClientState,
+ (list_fn_cb) &jwzgles_glDisableClientState };
+ const char *fn = fns[(csp ? 2 : 0) + (set < 0 ? 1 : 0)];
+ list_fn_cb f = fs[(csp ? 2 : 0) + (set < 0 ? 1 : 0)];
+
+ Assert (!state->compiling_verts,
+ "glEnable/glDisable not allowed inside glBegin");
+
+ if (state->compiling_list)
+ {
+ void_int vv[1];
+ vv[0].i = bit;
+ list_push (fn, f,PROTO_I, vv);
+ }
+
+ if (! state->replaying_list &&
+ ! state->compiling_list)
+ LOG2 ("direct %-12s %s", fn, mode_desc(bit));
+
+ if (csp && !state->compiling_verts)
+ {
+ if (set > 0)
+ switch (bit) {
+ case GL_NORMAL_ARRAY: state->set.ncount += 2; break;
+ case GL_TEXTURE_COORD_ARRAY: state->set.tcount += 2; break;
+ case GL_COLOR_ARRAY: state->set.ccount += 2; break;
+ default: break;
+ }
+ else
+ switch (bit) {
+ case GL_NORMAL_ARRAY: state->set.ncount = 0; break;
+ case GL_TEXTURE_COORD_ARRAY: state->set.tcount = 0; break;
+ case GL_COLOR_ARRAY: state->set.ccount = 0; break;
+ default: break;
+ }
+ }
+
+ if (omitp || state->compiling_list)
+ ;
+ else if (set > 0 && csp)
+ glEnableClientState (bit); /* the real one */
+ else if (set < 0 && csp)
+ glDisableClientState (bit); /* the real one */
+ else if (set > 0)
+ glEnable (bit); /* the real one */
+ else
+ glDisable (bit); /* the real one */
+
+ CHECK(fn);
}
- else
+
+ /* Store the bit in our state as well, or query it.
+ */
+ if (flag)
{
- if (! state->replaying_list)
- LOG2 ("direct %-12s %s", "glEnable", mode_desc(bit));
- glEnable (bit); /* the real one */
- CHECK("glEnable");
-
- switch (bit) {
- case GL_TEXTURE_2D: state->enabled |= ISENABLED_TEXTURE_2D; break;
- case GL_TEXTURE_GEN_S: state->enabled |= ISENABLED_TEXTURE_GEN_S; break;
- case GL_TEXTURE_GEN_T: state->enabled |= ISENABLED_TEXTURE_GEN_T; break;
- case GL_LIGHTING: state->enabled |= ISENABLED_LIGHTING; break;
- case GL_BLEND: state->enabled |= ISENABLED_BLEND; break;
- case GL_DEPTH_TEST: state->enabled |= ISENABLED_DEPTH_TEST; break;
- case GL_CULL_FACE: state->enabled |= ISENABLED_CULL_FACE; break;
- case GL_NORMALIZE: state->enabled |= ISENABLED_NORMALIZE; break;
- case GL_FOG: state->enabled |= ISENABLED_FOG; break;
- case GL_COLOR_MATERIAL: state->enabled |= ISENABLED_COLMAT; break;
-
- /* Do these work with glEnable or only with glEnableClientState? */
- case GL_VERTEX_ARRAY: state->enabled |= ISENABLED_VERT_ARRAY; break;
- case GL_NORMAL_ARRAY: state->enabled |= ISENABLED_NORM_ARRAY; break;
- case GL_TEXTURE_COORD_ARRAY: state->enabled |= ISENABLED_TEX_ARRAY;break;
- case GL_COLOR_ARRAY: state->enabled |= ISENABLED_COLOR_ARRAY; break;
-
- default: break;
- }
+ unsigned long *enabled = (state->compiling_list
+ ? &state->list_enabled
+ : &state->enabled);
+ if (set > 0)
+ *enabled |= flag;
+ else if (set < 0)
+ *enabled &= ~flag;
+ else
+ result = !!(*enabled & flag);
}
+
+ return result;
}
void
-jwzgles_glDisable (GLuint bit)
+jwzgles_glEnable (GLuint bit)
{
- Assert (!state->compiling_verts, "glDisable not allowed inside glBegin");
- if (state->compiling_list)
- {
- void_int vv[1];
- vv[0].i = bit;
- list_push ("glDisable", (list_fn_cb) &jwzgles_glDisable, PROTO_I, vv);
- }
- else
- {
- if (! state->replaying_list)
- LOG2 ("direct %-12s %s", "glDisable", mode_desc(bit));
- glDisable (bit); /* the real one */
- CHECK("glDisable");
-
- switch (bit) {
- case GL_TEXTURE_2D: state->enabled &= ~ISENABLED_TEXTURE_2D; break;
- case GL_TEXTURE_GEN_S: state->enabled &= ~ISENABLED_TEXTURE_GEN_S; break;
- case GL_TEXTURE_GEN_T: state->enabled &= ~ISENABLED_TEXTURE_GEN_T; break;
- case GL_LIGHTING: state->enabled &= ~ISENABLED_LIGHTING; break;
- case GL_BLEND: state->enabled &= ~ISENABLED_BLEND; break;
- case GL_DEPTH_TEST: state->enabled &= ~ISENABLED_DEPTH_TEST; break;
- case GL_CULL_FACE: state->enabled &= ~ISENABLED_CULL_FACE; break;
- case GL_NORMALIZE: state->enabled &= ~ISENABLED_NORMALIZE; break;
- case GL_FOG: state->enabled &= ~ISENABLED_FOG; break;
- case GL_COLOR_MATERIAL: state->enabled &= ~ISENABLED_COLMAT; break;
-
- /* Do these work with glEnable or only with glEnableClientState? */
- case GL_VERTEX_ARRAY: state->enabled &= ~ISENABLED_VERT_ARRAY; break;
- case GL_NORMAL_ARRAY: state->enabled &= ~ISENABLED_NORM_ARRAY; break;
- case GL_TEXTURE_COORD_ARRAY: state->enabled &= ~ISENABLED_TEX_ARRAY;break;
- case GL_COLOR_ARRAY: state->enabled &= ~ISENABLED_COLOR_ARRAY; break;
-
- default: break;
- }
- }
+ enable_disable (bit, 1);
}
+void
+jwzgles_glDisable (GLuint bit)
+{
+ enable_disable (bit, -1);
+}
GLboolean
jwzgles_glIsEnabled (GLuint bit)
{
- /*
- Assert (!state->compiling_verts, "glIsEnabled not allowed inside glBegin");
- Assert (!state->compiling_list, "glIsEnabled not allowed inside glNewList");
- */
- switch (bit) {
- case GL_TEXTURE_2D: return !!(state->enabled & ISENABLED_TEXTURE_2D);
- case GL_TEXTURE_GEN_S: return !!(state->enabled & ISENABLED_TEXTURE_GEN_S);
- case GL_TEXTURE_GEN_T: return !!(state->enabled & ISENABLED_TEXTURE_GEN_T);
- case GL_LIGHTING: return !!(state->enabled & ISENABLED_LIGHTING);
- case GL_BLEND: return !!(state->enabled & ISENABLED_BLEND);
- case GL_DEPTH_TEST: return !!(state->enabled & ISENABLED_DEPTH_TEST);
- case GL_CULL_FACE: return !!(state->enabled & ISENABLED_CULL_FACE);
- case GL_NORMALIZE: return !!(state->enabled & ISENABLED_NORMALIZE);
- case GL_FOG: return !!(state->enabled & ISENABLED_FOG);
- case GL_COLOR_MATERIAL: return !!(state->enabled & ISENABLED_COLMAT);
-
- /* Do these work with glEnable or only with glEnableClientState?
- We need to query them, and there is no glIsClientStateEnabled.
- */
- case GL_VERTEX_ARRAY: return !!(state->enabled & ISENABLED_VERT_ARRAY);
- case GL_NORMAL_ARRAY: return !!(state->enabled & ISENABLED_NORM_ARRAY);
- case GL_TEXTURE_COORD_ARRAY: return !!(state->enabled & ISENABLED_TEX_ARRAY);
- case GL_COLOR_ARRAY: return !!(state->enabled & ISENABLED_COLOR_ARRAY);
- default: Assert (0, "glIsEnabled unimplemented bit"); break;
- }
+ return enable_disable (bit, 0);
}
+void
+jwzgles_glEnableClientState (GLuint cap)
+{
+ enable_disable (cap, 1);
+}
+
+void
+jwzgles_glDisableClientState (GLuint cap)
+{
+ enable_disable (cap, -1);
+}
+
+
/* The spec says that OpenGLES 1.x doesn't implement glGetFloatv.
Were this true, it would suck, for it would mean that there was no
}
-/* These can be included inside glNewList, but they actually execute
- immediately anyway.
+/* These four *Pointer calls (plus glBindBuffer and glBufferData) can
+ be included inside glNewList, but they actually execute immediately
+ anyway, because their data is recorded in the list by the
+ subsequently-recorded call to glDrawArrays. This is a little weird.
*/
void
jwzgles_glVertexPointer (GLuint size, GLuint type, GLuint stride,
CHECK("glVertexPointer");
}
+
void
jwzgles_glNormalPointer (GLuint type, GLuint stride, const GLvoid *ptr)
{
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)
/* 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];
list_push ("glBindTexture", (list_fn_cb) &jwzgles_glBindTexture,
PROTO_II, vv);
}
- else
+
+ /* Do it immediately as well, for generate_texture_coords */
+ /* else */
{
if (! state->replaying_list)
LOG3 ("direct %-12s %s %d", "glBindTexture",