/* blocktube, Copyright (c) 2003 Lars Damerow * * Based on Jamie Zawinski's original dangerball code. * * 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. */ #define DEBUG 1 #define DEFAULTS "*delay: 40000 \n" \ "*wireframe: False \n" \ "*showFPS: False \n" \ "*suppressRotationAnimation: True\n" \ # define release_blocktube 0 # define blocktube_handle_event xlockmore_no_events #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #include "xlockmore.h" #include "colors.h" #include #include #ifdef USE_GL /* whole file */ #define DEF_HOLDTIME "1000" #define DEF_CHANGETIME "200" #define MAX_ENTITIES 1000 #define DEF_TEXTURE "True" #define DEF_FOG "True" #if defined(USE_XPM) || defined(USE_XPMINC) || defined(STANDALONE) /* USE_XPM & USE_XPMINC in xlock mode ; HAVE_XPM in xscreensaver mode */ #include "ximage-loader.h" #define I_HAVE_XPM #include "images/gen/blocktube_png.h" #endif /* HAVE_XPM */ typedef struct { int id, r, g, b; GLfloat tVal; int age; int lifetime; GLfloat position[3]; GLfloat angle; GLfloat angularVelocity; } entity; typedef struct { GLXContext *glx_context; GLuint block_dlist; int nextID; entity entities[MAX_ENTITIES]; float targetR, targetG, targetB, currentR, currentG, currentB, deltaR, deltaG, deltaB; int counter; int changing; GLfloat zoom; GLfloat tilt; GLuint envTexture; XImage *texti; GLfloat tunnelLength; GLfloat tunnelWidth; int polys; } blocktube_configuration; static blocktube_configuration *lps = NULL; static GLint holdtime; static GLint changetime; static int do_texture; static int do_fog; static XrmOptionDescRec opts[] = { { "-holdtime", ".holdtime", XrmoptionSepArg, 0 }, { "-changetime", ".changetime", XrmoptionSepArg, 0 }, {"-texture", ".texture", XrmoptionNoArg, "True" }, {"+texture", ".texture", XrmoptionNoArg, "False" }, {"-fog", ".fog", XrmoptionNoArg, "True" }, {"+fog", ".fog", XrmoptionNoArg, "False" }, }; static argtype vars[] = { {&holdtime, "holdtime", "Hold Time", DEF_HOLDTIME, t_Int}, {&changetime, "changetime", "Change Time", DEF_CHANGETIME, \ t_Int}, {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool}, {&do_fog, "fog", "Fog", DEF_FOG, t_Bool}, }; static OptionStruct desc[] = { {"-holdtime", "how long to stay on the same color"}, {"-changetime", "how long it takes to fade to a new color"}, }; ENTRYPOINT ModeSpecOpt blocktube_opts = {countof(opts), opts, countof(vars), vars, desc}; #ifdef USE_MODULES ModStruct blocktube_description = {"blocktube", "init_blocktube", "draw_blocktube", (char *)NULL, "draw_blocktube", "init_blocktube", "free_blocktube", &blocktube_opts, 40000, 30, 1, 1, 64, 1.0, "", "A shifting tunnel of reflective blocks", 0, NULL}; #endif /* USE_MODULES */ #if defined( I_HAVE_XPM ) static Bool LoadGLTextures(ModeInfo *mi) { blocktube_configuration *lp = &lps[MI_SCREEN(mi)]; Bool status; status = True; glGenTextures(1, &lp->envTexture); glBindTexture(GL_TEXTURE_2D, lp->envTexture); lp->texti = image_data_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi), blocktube_png, sizeof(blocktube_png)); if (!lp->texti) { status = False; } else { glPixelStorei(GL_UNPACK_ALIGNMENT,1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lp->texti->width, lp->texti->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, lp->texti->data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); # ifndef HAVE_JWZGLES /* #### Sphere maps unimplemented */ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); # endif } return status; } #endif static void newTargetColor(blocktube_configuration *lp) { int luminance = 0; while (luminance <= 150) { lp->targetR = random() % 256; lp->targetG = random() % 256; lp->targetB = random() % 256; lp->deltaR = (lp->targetR - lp->currentR) / changetime; lp->deltaG = (lp->targetG - lp->currentG) / changetime; lp->deltaB = (lp->targetB - lp->currentB) / changetime; luminance = 0.3 * lp->targetR + 0.59 * lp->targetG + 0.11 * lp->targetB; } } static void randomize_entity (blocktube_configuration *lp, entity *ent) { ent->id = lp->nextID++; ent->tVal = 1 - ((float)random() / RAND_MAX / 1.5); ent->age = 0; ent->lifetime = 100; ent->angle = random() % 360; ent->angularVelocity = 0.5-((float)(random()) / RAND_MAX); ent->position[0] = (float)(random()) / RAND_MAX + lp->tunnelWidth; ent->position[1] = (float)(random()) / RAND_MAX * 2; ent->position[2] = -(float)(random()) / RAND_MAX * lp->tunnelLength; } static void entityTick(blocktube_configuration *lp, entity *ent) { ent->angle += ent->angularVelocity; ent->position[2] += 0.1; if (ent->position[2] > lp->zoom) { ent->position[2] = -lp->tunnelLength + ((float)(random()) / RAND_MAX) * 20; } ent->age += 0.1; } static void tick(blocktube_configuration *lp) { lp->counter--; if (!lp->counter) { if (!lp->changing) { newTargetColor(lp); lp->counter = changetime; } else { lp->counter = holdtime; } lp->changing = (!lp->changing); } else { if (lp->changing) { lp->currentR += lp->deltaR; lp->currentG += lp->deltaG; lp->currentB += lp->deltaB; } } } static int cube_vertices(float x, float y, float z, int wire); ENTRYPOINT void reshape_blocktube (ModeInfo *mi, int width, int height); ENTRYPOINT void init_blocktube (ModeInfo *mi) { int loop; GLfloat fogColor[4] = {0,0,0,1}; blocktube_configuration *lp; int wire = MI_IS_WIREFRAME(mi); MI_INIT(mi, lps); lp = &lps[MI_SCREEN(mi)]; lp->glx_context = init_GL(mi); lp->zoom = 30; lp->tilt = 4.5; lp->tunnelLength = 200; lp->tunnelWidth = 5; if (wire) { do_fog = False; do_texture = False; glLineWidth(2); } lp->block_dlist = glGenLists (1); glNewList (lp->block_dlist, GL_COMPILE); lp->polys = cube_vertices(0.15, 1.2, 5.25, wire); glEndList (); #if defined( I_HAVE_XPM ) if (do_texture) { if (!LoadGLTextures(mi)) { fprintf(stderr, "%s: can't load textures!\n", progname); exit(1); } glEnable(GL_TEXTURE_2D); } #endif /* kick on the fog machine */ if (do_fog) { glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glHint(GL_FOG_HINT, GL_NICEST); glFogf(GL_FOG_START, 0); glFogf(GL_FOG_END, lp->tunnelLength/1.8); glFogfv(GL_FOG_COLOR, fogColor); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearDepth(1.0f); if (!do_texture && !wire) { /* If there is no texture, the boxes don't show up without a light. Though I don't understand why all the blocks come out gray. */ GLfloat pos[4] = {0.0, 1.0, 1.0, 0.0}; GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0}; GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_AMBIENT, amb); glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); glLightfv(GL_LIGHT0, GL_SPECULAR, spc); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } lp->counter = holdtime; lp->currentR = random() % 256; lp->currentG = random() % 256; lp->currentB = random() % 256; newTargetColor(lp); for (loop = 0; loop < MAX_ENTITIES; loop++) { randomize_entity(lp, &lp->entities[loop]); } reshape_blocktube(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); glFlush(); } ENTRYPOINT void free_blocktube (ModeInfo *mi) { blocktube_configuration *lp = &lps[MI_SCREEN(mi)]; # if defined ( I_HAVE_XPM ) if (!lp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *lp->glx_context); if (lp->envTexture) glDeleteTextures(1, &lp->envTexture); if (lp->texti) XDestroyImage(lp->texti); if (glIsList(lp->block_dlist)) glDeleteLists(lp->block_dlist, 1); if (lp->envTexture) glDeleteTextures (1, &lp->envTexture); # endif } ENTRYPOINT void reshape_blocktube (ModeInfo *mi, int width, int height) { blocktube_configuration *lp = &lps[MI_SCREEN(mi)]; GLfloat h = (GLfloat) height / (GLfloat) width; int y = 0; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *lp->glx_context); if (width > height * 5) { /* tiny window: show middle */ height = width; y = -height/2; h = height / (GLfloat) width; } glViewport(0, y, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, 1/h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ { int o = (int) current_device_rotation(); if (o != 0 && o != 180 && o != -180) glScalef (1/h, 1/h, 1/h); } # endif } static int cube_vertices(float x, float y, float z, int wire) { int polygon_count = 0; float x2, y2, z2, nv = 0.7; x2 = x/2; y2 = y/2; z2 = z/2; glFrontFace(GL_CW); glNormal3f(0, 0, nv); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-x2, y2, z2); glTexCoord2f(1.0, 0.0); glVertex3f( x2, y2, z2); glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2, z2); glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2, z2); polygon_count++; glEnd(); glNormal3f(0, 0, -nv); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(1.0, 0.0); glVertex3f(-x2, -y2, -z2); glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2, -z2); glTexCoord2f(0.0, 1.0); glVertex3f( x2, y2, -z2); glTexCoord2f(0.0, 0.0); glVertex3f(-x2, y2, -z2); polygon_count++; glEnd(); glNormal3f(0, nv, 0); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(0.0, 1.0); glVertex3f(-x2, y2, -z2); glTexCoord2f(0.0, 0.0); glVertex3f( x2, y2, -z2); glTexCoord2f(1.0, 0.0); glVertex3f( x2, y2, z2); glTexCoord2f(1.0, 1.0); glVertex3f(-x2, y2, z2); polygon_count++; glEnd(); glNormal3f(0, -nv, 0); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(1.0, 1.0); glVertex3f(-x2, -y2, -z2); glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2, z2); glTexCoord2f(0.0, 0.0); glVertex3f( x2, -y2, z2); glTexCoord2f(1.0, 0.0); glVertex3f( x2, -y2, -z2); polygon_count++; glEnd(); if (wire) return polygon_count; glNormal3f(nv, 0, 0); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(1.0, 0.0); glVertex3f( x2, -y2, -z2); glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2, z2); glTexCoord2f(0.0, 1.0); glVertex3f( x2, y2, z2); glTexCoord2f(0.0, 0.0); glVertex3f( x2, y2, -z2); polygon_count++; glEnd(); glNormal3f(-nv, 0, 0); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-x2, -y2, -z2); glTexCoord2f(1.0, 0.0); glVertex3f(-x2, y2, -z2); glTexCoord2f(1.0, 1.0); glVertex3f(-x2, y2, z2); glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2, z2); polygon_count++; glEnd(); return polygon_count; } static void draw_block(ModeInfo *mi, entity *ent) { blocktube_configuration *lp = &lps[MI_SCREEN(mi)]; glCallList (lp->block_dlist); mi->polygon_count += lp->polys; } ENTRYPOINT void draw_blocktube (ModeInfo *mi) { blocktube_configuration *lp = &lps[MI_SCREEN(mi)]; Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); entity *cEnt = NULL; int loop = 0; if (!lp->glx_context) return; mi->polygon_count = 0; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *lp->glx_context); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (do_texture) { glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glBindTexture(GL_TEXTURE_2D, lp->envTexture); } for (loop = 0; loop < MAX_ENTITIES; loop++) { cEnt = &lp->entities[loop]; glLoadIdentity(); glTranslatef(0.0f, 0.0f, lp->zoom); glRotatef(lp->tilt, 1.0f, 0.0f, 0.0f); glRotatef(cEnt->angle, 0.0f, 0.0f, 1.0f); glTranslatef(cEnt->position[0], cEnt->position[1], cEnt->position[2]); glColor4ub((int)(lp->currentR * cEnt->tVal), (int)(lp->currentG * cEnt->tVal), (int)(lp->currentB * cEnt->tVal), 255); draw_block(mi, cEnt); entityTick(lp, cEnt); } tick(lp); if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } XSCREENSAVER_MODULE ("BlockTube", blocktube) #endif /* USE_GL */