diff options
Diffstat (limited to 'hacks/glx/headroom.c')
-rw-r--r-- | hacks/glx/headroom.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/hacks/glx/headroom.c b/hacks/glx/headroom.c new file mode 100644 index 0000000..c122b22 --- /dev/null +++ b/hacks/glx/headroom.c @@ -0,0 +1,559 @@ +/* headroom, Copyright (c) 2020 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 + * 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. + * + * Well, it's supposed to be Max Headroom, but I have so far been unable to + * find or commission a decent 3D model of Max. So it's a skull instead. + * This will have to do for now. + * + * Created 29-Nov-2020. + */ + +#define DEFAULTS "*delay: 20000 \n" \ + "*showFPS: False \n" \ + "*skullColor: #777777" "\n" \ + "*teethColor: #FFFFFF" "\n" \ + "*torsoColor: #447744" "\n" \ + "*torsoCapColor:#222222" "\n" \ + "*gridColor1: #AA0000" "\n" \ + "*gridColor2: #00FF00" "\n" \ + "*gridColor3: #6666FF" "\n" \ + "*wireframe: False \n" + +# define release_headroom 0 + +#define DEF_SPEED "1.0" +#define DEF_SPIN "XYZ" +#define DEF_WANDER "False" + +#include "xlockmore.h" + +#include <ctype.h> + +#ifdef USE_GL /* whole file */ + +#include "gltrackball.h" +#include "rotator.h" +#include "gllist.h" + +extern const struct gllist + *headroom_model_skull_half, *headroom_model_jaw_half, + *headroom_model_teeth_upper_half, *headroom_model_teeth_lower_half, + *headroom_model_torso_half, *headroom_model_torso_cap_half; + +static const struct gllist * const *all_objs[] = { + &headroom_model_skull_half, &headroom_model_jaw_half, + &headroom_model_teeth_upper_half, &headroom_model_teeth_lower_half, + &headroom_model_torso_half, &headroom_model_torso_cap_half, +}; + +enum { SKULL_HALF, JAW_HALF, TEETH_UPPER_HALF, TEETH_LOWER_HALF, TORSO_HALF, + TORSO_CAP_HALF }; + +typedef struct { GLfloat x, y, z; } XYZ; + +typedef struct { + GLXContext *glx_context; + rotator *rot, *rot2, *rot3; + trackball_state *trackball; + Bool button_down_p; + Bool spinx, spiny, spinz; + + XYZ head_pos; + GLfloat jaw_pos; + GLfloat grid_colors[3][4]; + + GLuint *dlists; + GLfloat component_colors[countof(all_objs)][4]; +} headroom_configuration; + +static headroom_configuration *bps = NULL; + +static GLfloat speed; +static char *do_spin; +static Bool do_wander; + +static XrmOptionDescRec opts[] = { + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-spin", ".spin", XrmoptionSepArg, 0 }, + { "+spin", ".spin", XrmoptionNoArg, "" }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, +}; + +static argtype vars[] = { + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&do_spin, "spin", "Spin", DEF_SPIN, t_String}, + {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, +}; + +ENTRYPOINT ModeSpecOpt headroom_opts = { + countof(opts), opts, countof(vars), vars, NULL}; + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_headroom (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + int y = 0; + + if (width > height * 5) { /* tiny window: show middle */ + height = width * 9/16; + y = -height/2; + h = height / (GLfloat) width; + } + + glViewport (0, y, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective (30, 1/h, 1, 500); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0, 0, 30, + 0, 0, 0, + 0, 1, 0); + + { + GLfloat s = (MI_WIDTH(mi) < MI_HEIGHT(mi) + ? (MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi)) + : 1); + glScalef (s, s, s); + } + + glClear(GL_COLOR_BUFFER_BIT); +} + + +ENTRYPOINT Bool +headroom_handle_event (ModeInfo *mi, XEvent *event) +{ + headroom_configuration *bp = &bps[MI_SCREEN(mi)]; + + if (gltrackball_event_handler (event, bp->trackball, + MI_WIDTH (mi), MI_HEIGHT (mi), + &bp->button_down_p)) + return True; + + return False; +} + + +static void +parse_color (ModeInfo *mi, char *key, GLfloat color[4]) +{ + XColor xcolor; + char *string = get_string_resource (mi->dpy, key, "Color"); + if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor)) + { + fprintf (stderr, "%s: unparsable color in %s: %s\n", progname, + key, string); + exit (1); + } + free (string); + + color[0] = xcolor.red / 65536.0; + color[1] = xcolor.green / 65536.0; + color[2] = xcolor.blue / 65536.0; + color[3] = 1; +} + + +ENTRYPOINT void +init_headroom (ModeInfo *mi) +{ + headroom_configuration *bp; + int wire = MI_IS_WIREFRAME(mi); + int i; + MI_INIT (mi, bps); + + bp = &bps[MI_SCREEN(mi)]; + bp->glx_context = init_GL(mi); + reshape_headroom (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + + glShadeModel(GL_SMOOTH); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glEnable(GL_CULL_FACE); + + if (!wire) + { + GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0}; +/* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.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}; + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT0, GL_SPECULAR, spc); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + + { + double spin_speed = 0.5; + double wander_speed = 0.002 * speed; + double tilt_speed = 0.005 * speed; + double spin_accel = 0.5; + + double spin_speed_2 = 0.2 * speed; + double spin_accel_2 = 0.2; + double wander_speed_2 = 0.01 * speed; + + char *s = do_spin; + while (*s) + { + if (*s == 'x' || *s == 'X') bp->spinx = True; + else if (*s == 'y' || *s == 'Y') bp->spiny = True; + else if (*s == 'z' || *s == 'Z') bp->spinz = True; + else if (*s == '0') ; + else + { + fprintf (stderr, + "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n", + progname, do_spin); + exit (1); + } + s++; + } + + bp->rot = make_rotator (bp->spinx ? spin_speed : 0, + bp->spiny ? spin_speed : 0, + bp->spinz ? spin_speed : 0, + spin_accel, + do_wander ? wander_speed : 0, + False); + bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True); + + bp->rot3 = make_rotator (spin_speed_2, spin_speed_2, spin_speed_2, + spin_accel_2, wander_speed_2, True); + + bp->trackball = gltrackball_init (False); + } + + bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint)); + for (i = 0; i < countof(all_objs); i++) + bp->dlists[i] = glGenLists (1); + + for (i = 0; i < countof(all_objs); i++) + { + const struct gllist *gll = *all_objs[i]; + char *key = 0; + GLfloat spec[4] = {0.4, 0.4, 0.4, 1.0}; + GLfloat shiny = 80; /* 0-128 */ + + glNewList (bp->dlists[i], GL_COMPILE); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + + glRotatef (-90, 1, 0, 0); + + glBindTexture (GL_TEXTURE_2D, 0); + + switch (i) { + case SKULL_HALF: + case JAW_HALF: + key = "skullColor"; + break; + case TEETH_UPPER_HALF: + case TEETH_LOWER_HALF: + key = "teethColor"; + break; + case TORSO_HALF: + key = "torsoColor"; + break; + case TORSO_CAP_HALF: + key = "torsoCapColor"; + break; + default: + abort(); + } + + parse_color (mi, key, bp->component_colors[i]); + + glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec); + glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny); + renderList (gll, wire); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glEndList (); + } + + parse_color (mi, "gridColor1", bp->grid_colors[0]); + parse_color (mi, "gridColor2", bp->grid_colors[1]); + parse_color (mi, "gridColor3", bp->grid_colors[2]); + + reshape_headroom (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); +} + + +static int +draw_unit_panel (ModeInfo *mi, GLfloat *color1, GLfloat *color2) +{ + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + int rows = 36; + GLfloat spacing = 1.0 / rows; + GLfloat thickness = spacing / 8; + int i; + glFrontFace (GL_CCW); + glBegin (wire ? GL_LINES : GL_QUADS); + glNormal3f (0, 0, 1); + for (i = 0; i < rows; i++) + { + GLfloat y = i / (GLfloat) rows + spacing/2; + glColor4fv (color2); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color2); + glVertex3f (1, y, 0); + glColor4fv (color1); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color1); + glVertex3f (0, y, 0); + glVertex3f (0, y + thickness, 0); + glColor4fv (color2); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color2); + glVertex3f (1, y + thickness, 0); + polys++; + } + glEnd(); + return polys; +} + +static int +draw_box (ModeInfo *mi) +{ + headroom_configuration *bp = &bps[MI_SCREEN(mi)]; + int polys = 0; + GLfloat *c0 = bp->grid_colors[0]; + GLfloat *c1 = bp->grid_colors[1]; + GLfloat *c2 = bp->grid_colors[2]; + + glPushMatrix(); + glTranslatef (-0.5, -0.5, 0.5); + polys += draw_unit_panel (mi, c0, c1); + glRotatef (-90, 0, 1, 0); + glTranslatef (-1, 0, 0); + polys += draw_unit_panel (mi, c1, c0); + glRotatef (-90, 0, 1, 0); + glTranslatef (-1, 0, 0); + polys += draw_unit_panel (mi, c0, c1); + glRotatef (-90, 0, 1, 0); + glTranslatef (-1, 0, 0); + polys += draw_unit_panel (mi, c1, c0); + + glRotatef (-90, 1, 0, 0); + glTranslatef (0, 0, 1); + polys += draw_unit_panel (mi, c2, c1); + glRotatef (-180, 1, 0, 0); + glTranslatef (0, -1, 1); + polys += draw_unit_panel (mi, c1, c2); + + + glPopMatrix(); + return polys; +} + + + +static int +draw_component (ModeInfo *mi, int i) +{ + headroom_configuration *bp = &bps[MI_SCREEN(mi)]; + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + bp->component_colors[i]); + + glFrontFace (GL_CCW); + glCallList (bp->dlists[i]); + + glPushMatrix(); + glScalef (-1, 1, 1); + glFrontFace (GL_CW); + glCallList (bp->dlists[i]); + glPopMatrix(); + + return 2 * (*all_objs[i])->points / 3; +} + + +ENTRYPOINT void +draw_headroom (ModeInfo *mi) +{ + headroom_configuration *bp = &bps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int wire = MI_IS_WIREFRAME(mi); + GLfloat s; + + if (!bp->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix (); + glRotatef(current_device_rotation(), 0, 0, 1); + + mi->polygon_count = 0; + + { + double x, y, z; + get_rotation (bp->rot3, &x, &y, &z, !bp->button_down_p); + + glPushMatrix(); + glRotatef (x * 360, 1, 0, 0); + glRotatef (y * 360, 0, 1, 0); + glRotatef (z * 360, 0, 0, 1); + + get_position (bp->rot3, &x, &y, &z, !bp->button_down_p); + s = 3; + glScalef (1+x*s, 1+y*s, 1+z*s); + + s = 60; + glScalef (s, s, s); + + if (!wire) glDisable(GL_LIGHTING); + mi->polygon_count += draw_box (mi); + if (!wire) glEnable(GL_LIGHTING); + glPopMatrix(); + glClear(GL_DEPTH_BUFFER_BIT); + } + + { + double x, y, z; + get_position (bp->rot, &x, &y, &z, !bp->button_down_p); + glTranslatef((x - 0.5) * 8, + (y - 0.5) * 6, + (z - 0.5) * 10); + + gltrackball_rotate (bp->trackball); + + { + double maxx = 40; + double maxy = 20; + double maxz = 2; + get_position (bp->rot2, &x, &y, &z, !bp->button_down_p); + if (bp->spinx) glRotatef (maxy/2 - x*maxy, 1, 0, 0); + if (bp->spiny) glRotatef (maxx/2 - y*maxx, 0, 1, 0); + if (bp->spinz) glRotatef (maxz/2 - z*maxz, 0, 0, 1); + } + } + + glTranslatef (0, -6, 0); + s = 0.03; + + glScalef (s, s, s); + + { + /* head x, nod: -50 to +30 + head y, turn: +- 70 + head z, tilt: +- 30 + jaw, open: 0 - 22 + */ + const XYZ head_base = { 0, 200, 0 }; + const XYZ jaw_base = { 0, 270, 40 }; + + mi->polygon_count += draw_component (mi, TORSO_HALF); + mi->polygon_count += draw_component (mi, TORSO_CAP_HALF); + + glTranslatef (head_base.x, head_base.y, head_base.z); + glRotatef (bp->head_pos.x, 1, 0, 0); + glRotatef (bp->head_pos.y, 0, 1, 0); + glRotatef (bp->head_pos.z, 0, 0, 1); + glTranslatef (-head_base.x, -head_base.y, -head_base.z); + + mi->polygon_count += draw_component (mi, SKULL_HALF); + mi->polygon_count += draw_component (mi, TEETH_UPPER_HALF); + + glTranslatef (jaw_base.x, jaw_base.y, jaw_base.z); + glRotatef (bp->jaw_pos, 1, 0, 0); + glTranslatef (-jaw_base.x, -jaw_base.y, -jaw_base.z); + + mi->polygon_count += draw_component (mi, JAW_HALF); + mi->polygon_count += draw_component (mi, TEETH_LOWER_HALF); + + glFrontFace (GL_CCW); + } + + if (! bp->button_down_p) + { + int twitch = 200 / speed; + int untwitch = 50 / speed; + if (twitch < 10) twitch = 10; + if (untwitch < 5) untwitch = 5; + + if (! (random() % twitch)) + bp->head_pos.x = -20.0 + (random() % (20 + 30)); + if (! (random() % twitch)) + bp->head_pos.y = -50.0 + (random() % (50 * 2)); + if (! (random() % twitch)) + bp->head_pos.z = -30.0 + (random() % (30 * 2)); + if (! (random() % twitch)) + bp->jaw_pos = (random() % 22); + + if (! (random() % untwitch)) + { + bp->head_pos.x = 0; + bp->head_pos.y = 0; + bp->head_pos.z = 0; + bp->jaw_pos = 0; + } + } + + glPopMatrix (); + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + + +ENTRYPOINT void +free_headroom (ModeInfo *mi) +{ + headroom_configuration *bp = &bps[MI_SCREEN(mi)]; + int i; + + if (!bp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); + + if (bp->trackball) gltrackball_free (bp->trackball); + if (bp->rot) free_rotator (bp->rot); + if (bp->rot2) free_rotator (bp->rot2); + if (bp->rot3) free_rotator (bp->rot3); + if (bp->dlists) { + for (i = 0; i < countof(all_objs); i++) + if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1); + free (bp->dlists); + } +} + +XSCREENSAVER_MODULE ("Headroom", headroom) + +#endif /* USE_GL */ |