summaryrefslogtreecommitdiffstats
path: root/hacks/glx/headroom.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/headroom.c')
-rw-r--r--hacks/glx/headroom.c559
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 */