/* ************************************************************************* * NAME: videosample.c * * DESCRIPTION: * * code to make an opengl window, put a poly in it, and change the * texture on it to try to simulate video. this doesn't use the * glut library. (adapted from man glxintro) * * this code uses luminance textures to do greyscale * * PROCESS: * * Linux compile: * debugging: * cc -g -o videosample -I/usr/X11R6/include -I. videosample.c \ -lGL -lX11 -lGLU -L/usr/X11R6/lib * * full optimizations: * cc -O3 -ffast-math -mpentiumpro -o videosample \ -I/usr/X11R6/include -I. videosample.c \ -lGL -lX11 -lGLU -L/usr/X11R6/lib * * if you're using the nVidia graphics graphics cards and drivers then * * setenv __GL_SYNC_TO_VBLANK 1 * * will enable the "sync to vertical retrace" feature. (glXSwapBuffers * will swap in time for the monitor to do its vertical refresh.) * * to run: * * ./videosample * * GLOBALS: * * attributeListSgl, attributeListDbl * * REFERENCES: * * LIMITATIONS: * * * * REVISION HISTORY: * * STR Description Author * * 8-Jun-02 initial coding gpk * 9-Jun-02 switch to luminance texture, fix gpk * coordinate mapping to handle video updates * whose dimensions are not powers of two. * * TARGET: ANSI C, OpenGL 1.2 or higher * * * * ************************************************************************* */ #include #include /* malloc */ #include /* gettimeofday */ #include /* gettimeofday */ #include #include #include #include /* TEX_WIDTH - width of the initial texture in the window */ /* must be a power of two; preferably the smallest one */ /* greater than of equal to SUBTEX_WIDTH */ #define TEX_WIDTH 1024 /* TEX_HEIGHT - height of the initial texture in the window */ /* must be a power of two; preferably the smallest one */ /* greater than of equal to SUBTEX_HEIGHT */ #define TEX_HEIGHT 512 /* SUBTEX_WIDTH, SUBTEX_HEIGHT - the size of the texel array */ /* we get for the video on each update. need not be powers of */ /* two, but must be less than or equal to TEX_WIDTH, TEX_HEIGHT */ #define SUBTEX_WIDTH 640 #define SUBTEX_HEIGHT 480 /* WIN_WIDTH, WIN_HEIGHT - size of the window created to display */ /* the video. need not be powers of two, but must be less than */ /* or equal to TEX_WIDTH, TEX_HEIGHT */ #define WIN_WIDTH SUBTEX_WIDTH #define WIN_HEIGHT SUBTEX_HEIGHT #define MIN(a,b) ((a) < (b) ? (a) : (b)) void check_error( char *label ); /* Texture_data_t - a structure for texture or subtexture data */ typedef struct texture_data_s { GLubyte * image; /* pointer to malloc-ed array of texels */ int texture_width; /* in pixels */ int texture_height; /* in pixels */ int ncomponents; /* 1 for luminance, 4 for RGBA */ } Texture_data_t; /* static int attributeListSgl[] attribute list that glXChooseVisual uses to select a single buffered opengl window. (used as fallback in case double buffered is not available.) range of values: any of the declared type accessors: main modifiers: none */ static int attributeListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 1, /*get the deepest buffer with 1 red bit*/ GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; /* static int attributeListDbl[] attribute list that glXChooseVisual uses to select a double buffered opengl window. range of values: any of the declared type accessors: main modifiers: none */ static int attributeListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, /*In case Single buffering is not supported*/ GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; /* ************************************************************************* NAME: WaitForNotify USAGE: static Bool some_static_Bool; Display *d; XEvent *e; char *arg; some_static_Bool = WaitForNotify(d, e, arg); returns: Bool DESCRIPTION: I don't really know what this does (taken from man glxintro) REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 9-Jun-02 initial coding gpk ************************************************************************* */ static Bool WaitForNotify(Display *d, XEvent *e, char *arg) { return (e->type == MapNotify) && (e->xmap.window == (Window)arg); } /* ************************************************************************* NAME: main USAGE: ./videotex4 returns: int DESCRIPTION: this displays a series of test patterns in an attempt to simulate video using a textured polygon and glTexSubImage2D to get the pixels onto the screen. this is taken mostly from man glxintro REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: attributeListSgl, attributeListDbl modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 9-Jun-02 initial coding gpk ************************************************************************* */ int main(int argc, char **argv) { Display *dpy; XVisualInfo *vi; Colormap cmap; XSetWindowAttributes swa; Window win; GLXContext cx; XEvent event; int swap_flag = GL_FALSE; /* prototypes */ void do_textured_poly(Display *dpy, Window win, int swap_flag); /* end prototypes */ /* get a connection */ dpy = XOpenDisplay(0); /* get an appropriate visual: double buffered if we have it, */ /* single buffered if not and set swap_flag if we do get */ /* double buffered. */ vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributeListDbl); if (vi == NULL) { vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributeListSgl); swap_flag = GL_FALSE; } else { swap_flag = GL_TRUE; } /* create a GLX context */ cx = glXCreateContext(dpy, vi, 0, GL_TRUE); /* create a color map */ cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); /* create a window */ swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask; /* XSetWMColormapWindows needed? -gpk 2-Apr-01 */ win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, WIN_WIDTH, WIN_HEIGHT, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); XMapWindow(dpy, win); XIfEvent(dpy, &event, WaitForNotify, (char*)win); /* connect the context to the window */ glXMakeCurrent(dpy, win, cx); /* clear the buffer */ glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); glFlush(); if (swap_flag) glXSwapBuffers(dpy,win); /* draw the wideo */ do_textured_poly(dpy,win, swap_flag); /* wait a while so the use can look at the final frame */ sleep(10); return(1); } /* N_TEXTURES - the number of video frames (subtextures) to */ /* generate in the array of textures that are displayed as */ /* successive images. */ #define N_TEXTURES 32 /* ITERATIONS - the number of frames to display. larger numbers */ /* here mean it runs longer but we get more accurate frame rate */ /* information. */ #define ITERATIONS (100 * N_TEXTURES) /* ************************************************************************* NAME: do_textured_poly USAGE: Display *dpy; Window win; int swap_flag ; do_textured_poly(dpy, win, swap_flag); returns: void DESCRIPTION: REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 9-Jun-02 initial coding gpk ************************************************************************* */ void do_textured_poly(Display *dpy, Window win, int swap_flag ) { Texture_data_t syntexture; /* bacground texture */ Texture_data_t texarray[N_TEXTURES]; /* array of video frame textures */ struct timeval starttime, endtime; /* start and end times of drawing */ long int usec_running; /* how long it took to run */ double usec_frame, frame_rate; /* frame rate */ int i, texture_number; GLsizei width, height; /* sizes of subtextures from texarray */ /* prototypes */ int load_texture(char *filename, Texture_data_t *texturep); void draw_textured_poly(Texture_data_t texture, Display *dpy, Window win, int swap_flag); void draw_symbology_crosshairs(Display *dpy, Window win, GLfloat center_x, GLfloat center_y, GLfloat height, GLfloat width); void draw_textured_poly_dl(Texture_data_t texture, Display *dpy, Window win, int swap_flag); int synthesize_texture(Texture_data_t * texture); void make_texture_array(Texture_data_t texarray[], int nelements); void synthesize_luminance_texture(Texture_data_t * texture); /* end prototypes */ /* give me an initial texture that covers the entire and */ /* rounds up to the nearest power of two (TEX_WIDTH x TEX_HEIGHT) */ /* note that this will probably be larger than the window. */ synthesize_luminance_texture(&syntexture); /* make an array of textures for use as video frames. stored in */ /* texarray[0..N_TEXTURES -1] */ make_texture_array(texarray, N_TEXTURES); width = texarray[0].texture_width; height = texarray[0].texture_height; /* make my test pattern in syntexture the texture for the window */ /* it's an 8bit per pixel luminance texture (grey scale) */ glTexImage2D(GL_TEXTURE_2D , 0, GL_LUMINANCE, syntexture.texture_width, syntexture.texture_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, syntexture.image); check_error("after glTexImage2D"); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); /* check the clock before we draw ITERATIONS frames from texarray */ gettimeofday(&starttime, NULL); for (i = 0; i < ITERATIONS; i++) { /* draw the syntexture texture in the window, then modify */ /* it by substituting in the texture from */ /* texarray[texture_number] and do it again, drawing the */ /* new image. */ draw_textured_poly(syntexture, dpy, win, swap_flag); draw_symbology_crosshairs(dpy, win, 0.0, 0.0, 0.3, 0.3); texture_number = i%N_TEXTURES; /* substitute in the texture from texarray[texture_number] */ /* for the redraw */ check_error("before subtexture"); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, texarray[texture_number].image); check_error("after subtexture"); } /* we're done: check the clock */ gettimeofday(&endtime, NULL); /* compute the number of microseconds between starttime and */ /* endtime, then use that to compute the frame rate. */ usec_running = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (starttime.tv_sec * 1000000 + starttime.tv_usec); usec_frame = (double)usec_running /(double)ITERATIONS; frame_rate = 1000000.0/usec_frame; fprintf(stderr, "%ld microseconds run, average %f frames/sec\n", usec_running, frame_rate); } /* ************************************************************************* NAME: draw_symbology_crosshairs USAGE: Display *dpy; Window win; GLfloat center_x; GLfloat center_y; GLfloat height; GLfloat width; draw_symbology_crosshairs(dpy, win, center_x, center_y, height, width); returns: void DESCRIPTION: draw red crosshairs centered on the given center_x, center_y and having the given height and width REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: modified: FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 9-Jun-02 initial coding gpk ************************************************************************* */ void draw_symbology_crosshairs(Display *dpy, Window win, GLfloat center_x, GLfloat center_y, GLfloat height, GLfloat width) { GLfloat p0[3]; GLfloat p1[3]; GLfloat p2[3]; GLfloat p3[3]; p0[0] = center_x; p0[1] = center_y + height; p0[2] = 0; p1[0] = center_x; p1[1] = center_y - height; p1[2] = 0; p2[0] = center_x - width; p2[1] = center_y; p2[2] = 0; p3[0] = center_x + width; p3[1] = center_y; p3[2] = 0; check_error("before crosshairs symbology"); glPushMatrix(); { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); check_error("setting color mask"); /* glDisable(GL_DEPTH_TEST); */ glBegin(GL_LINES); { glColor3f(1.0, 0.0, 0.0); glVertex3fv(p0); glVertex3fv(p1); glVertex3fv(p2); glVertex3fv(p3); } glEnd(); } glPopMatrix(); glFlush(); glXSwapBuffers(dpy,win); check_error("after crosshairs symbology"); } /* ************************************************************************* NAME: draw_textured_poly USAGE: Texture_data_t texture; Display *dpy; Window win; int swap_flag; draw_textured_poly(texture, dpy, win, swap_flag); returns: void DESCRIPTION: draw a polygon in the window with a texture on it. REFERENCES: LIMITATIONS: the texture argument is not used in this function. we really should move the glTexSubImage2D call down here. GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 10-Jun-02 initial coding gpk ************************************************************************* */ void draw_textured_poly(Texture_data_t texture, Display *dpy, Window win, int swap_flag) { static GLfloat v0[3] = {-1.00, -1.00, 0.0}; /* vertices of */ static GLfloat v1[3] = { 1.00, -1.00, 0.0}; /* polygon */ static GLfloat v2[3] = { 1.00, 1.00, 0.0}; static GLfloat v3[3] = {-1.00, 1.00, 0.0}; static GLfloat t0[2] = { 0.0, 0.0}; /* texture coordinates */ static GLfloat t1[2] = {(float)WIN_WIDTH / (float)TEX_WIDTH, 0.0 }; static GLfloat t2[2] = {(float)WIN_WIDTH / (float)TEX_WIDTH, (float)WIN_HEIGHT / (float)TEX_HEIGHT}; static GLfloat t3[2] = { 0.0, (float)WIN_HEIGHT / (float)TEX_HEIGHT}; glColor4f(1.0, 1.0, 1.0, 1.0); { glPushMatrix(); { glEnable(GL_TEXTURE_2D); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); { glTexCoord2fv(t0); glVertex3fv(v0); glTexCoord2fv(t1); glVertex3fv(v1); glTexCoord2fv(t2); glVertex3fv(v2); glTexCoord2fv(t3); glVertex3fv(v3); } glEnd(); glDisable(GL_TEXTURE_2D); } glPopMatrix(); } } /* ************************************************************************* NAME: check_error USAGE: char *label ; check_error(label); returns: void DESCRIPTION: print out any accumulated opengl errors REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 1-Jun-02 initial coding gpk ************************************************************************* */ void check_error( char *label ) { GLenum error; while ( (error = glGetError()) != GL_NO_ERROR ) printf( "%s: %s\n", label, gluErrorString(error) ); } /* ************************************************************************* NAME: make_texture_array USAGE: Texture_data_t texarray[nelements]; int nelements; make_texture_array(texarray, nelements); returns: void DESCRIPTION: generate a series of textures filled in texarray[0..nelements -1] having SUBTEX_WIDTHxSUBTEX_HEIGHT size such that they can blend together when shown in series. modifies texarray and allocates memory from the heap. this generates a series of luminance textures such that texarray[N] looks like this: | < ------- SUBTEX_WIDTH ------------------>| ------------------------------------------ --- | | | ^ | | | | | med grey | | | (0x7f) | | | | | | | | | | | | | SUBTEX_HEIGHT --- |----------------------------------------| | | | | | | N * SUBTEX_HEIGHT | variable grey | | -------------- | (256 * N)/ | | nelements | nelements | | | | | | | | | | | v --- ------------------------------------------ --- |< N * SUBTEX_WIDTH>| ------------ nelements REFERENCES: LIMITATIONS: These are intended as texel arrays for glTexSubImage2D (ie replacements for parts of a larger texture), so SUBTEX_WIDTH, SUBTEX_HEIGHT are not necessarily powers of two. A textures dimensions must be powers of two if you're going to use it for any other texture operation. GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 1-Jun-02 initial coding gpk ************************************************************************* */ void make_texture_array(Texture_data_t texarray[], int nelements) { int texture_number; unsigned int texture_size, width, height, ncomponents; GLsizei row, column; GLubyte * image; int greylevel; width = SUBTEX_WIDTH; height = SUBTEX_HEIGHT; ncomponents = 1; texture_size = width * height * ncomponents * sizeof(GLubyte); /* GL_UNSIGNED_BYTE format */ for (texture_number = 0; texture_number < nelements; texture_number++) { printf("texture_number %d\n", texture_number); texarray[texture_number].texture_width = width; texarray[texture_number].texture_height = height; texarray[texture_number].ncomponents = ncomponents; texarray[texture_number].image = malloc(texture_size); greylevel = (256 * texture_number) / nelements; image = texarray[texture_number].image; /* make all pixels in rows from 0..texture_number */ /* and columns 0..texture_number white. */ for (row = 0; row < texture_number * height / nelements; row++) { for (column = 0; column < texture_number * width/ nelements; column++) { image[(row * width +column)] = greylevel; } } for (row = texture_number * height / nelements; row < height; row++) { for (column = texture_number * width/ nelements; column < width; column++) { image[(row * width +column)] = 0x7f; /* grey */ } } } } /* ************************************************************************* NAME: synthesize_luminance_texture USAGE: Texture_data_t * texture; synthesize_luminance_texture(texture); returns: void DESCRIPTION: build a simple luminance texture to act as the background texture for the video frames. REFERENCES: LIMITATIONS: as noted in their #define statements, TEX_WIDTH & TEX_HEIGHT must be powers of two. GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 8-Jun-02 initial coding gpk ************************************************************************* */ void synthesize_luminance_texture(Texture_data_t * texture) { GLubyte * p; unsigned int image_size; texture->texture_width = TEX_WIDTH; texture->texture_height = TEX_HEIGHT; texture->ncomponents = 1; image_size = texture->texture_width * texture->texture_height * texture->ncomponents * sizeof(GLubyte); /* GL_UNSIGNED_BYTE format */ texture->image = malloc(image_size); p = texture->image; while (p < texture->image + image_size) { p[0] = 0x00; /* dark */ p[1] = 0x00; /* dark */ p[2] = 0xff; /* light */ p[3] = 0xff; /* light */ p += 4; } }