X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fcubetwist.c;fp=hacks%2Fglx%2Fcubetwist.c;h=8205561e723587596f853ce7e7558a65a2fe6c9c;hp=0000000000000000000000000000000000000000;hb=d6b0217f2417bd19187f0ebc389d6c5c2233b11c;hpb=aa75c7476aeaa84cf3abc192b376a8b03c325213 diff --git a/hacks/glx/cubetwist.c b/hacks/glx/cubetwist.c new file mode 100644 index 00000000..8205561e --- /dev/null +++ b/hacks/glx/cubetwist.c @@ -0,0 +1,594 @@ +/* cubetwist, Copyright (c) 2016 Jamie Zawinski + * + * 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 DEFAULTS "*delay: 30000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*suppressRotationAnimation: True\n" \ + +# define refresh_cube 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#include "xlockmore.h" +#include "normals.h" +#include "rotator.h" +#include "gltrackball.h" +#include + +#ifdef USE_GL /* whole file */ + + +#define DEF_SPIN "True" +#define DEF_WANDER "True" +#define DEF_SPEED "1.0" +#define DEF_FLAT "True" +#define DEF_THICKNESS "0.0" +#define DEF_DISPLACEMENT "0.0" + +typedef struct cube cube; +struct cube { + GLfloat size, thickness; + XYZ pos, rot; + GLfloat color[4]; + cube *next; +}; + +typedef struct oscillator oscillator; +struct oscillator { + double ratio, from, to, speed, *var; + int remaining; + oscillator *next; +}; + +typedef struct { + GLXContext *glx_context; + rotator *rot; + trackball_state *trackball; + Bool button_down_p; + cube *cubes; + oscillator *oscillators; +} cube_configuration; + +static cube_configuration *bps = NULL; + +static Bool do_flat; +static Bool do_spin; +static GLfloat speed; +static GLfloat thickness; +static GLfloat displacement; +static Bool do_wander; + +static XrmOptionDescRec opts[] = { + { "-spin", ".spin", XrmoptionNoArg, "True" }, + { "+spin", ".spin", XrmoptionNoArg, "False" }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, + { "-flat", ".flat", XrmoptionNoArg, "True" }, + { "+flat", ".flat", XrmoptionNoArg, "False" }, + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-thickness", ".thickness", XrmoptionSepArg, 0 }, + { "-displacement", ".displacement", XrmoptionSepArg, 0 }, +}; + +static argtype vars[] = { + {&do_flat, "flat", "flat", DEF_FLAT, t_Bool}, + {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool}, + {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float}, + {&displacement, "displacement", "Displacement", DEF_DISPLACEMENT, t_Float}, +}; + +ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + +static int +draw_strut (ModeInfo *mi, cube *c) +{ + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + + glPushMatrix(); + glFrontFace (GL_CW); + glNormal3f (0, 0, -1); + glTranslatef (-c->size/2, -c->size/2, -c->size/2); + + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN); + glVertex3f (0, 0, 0); + glVertex3f (c->size, 0, 0); + glVertex3f (c->size - c->thickness, c->thickness, 0); + glVertex3f (c->thickness, c->thickness, 0); + glEnd(); + polys += 2; + + glNormal3f (0, 1, 0); + glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN); + glVertex3f (c->thickness, c->thickness, 0); + glVertex3f (c->size - c->thickness, c->thickness, 0); + glVertex3f (c->size - c->thickness, c->thickness, c->thickness); + glVertex3f (c->thickness, c->thickness, c->thickness); + glEnd(); + polys += 2; + glPopMatrix(); + + return polys; +} + + +static int +draw_cubes (ModeInfo *mi, cube *c) +{ + int polys = 0; + int i, j; + + glPushMatrix(); + glColor4fv (c->color); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c->color); + + glPushMatrix(); + for (j = 0; j < 6; j++) + { + for (i = 0; i < 4; i++) + { + polys += draw_strut (mi, c); + glRotatef (90, 0, 0, 1); + } + if (j == 3) + glRotatef (90, 0, 0, 1); + if (j < 4) + glRotatef (90, 0, 1, 0); + else + glRotatef (180, 1, 0, 0); + } + glPopMatrix(); + + if (c->next) + { + glRotatef (c->rot.x, 1, 0, 0); + glRotatef (c->rot.y, 0, 1, 0); + glRotatef (c->rot.z, 0, 0, 1); + glTranslatef (c->pos.x, c->pos.y, c->pos.z); + c->next->pos = c->pos; + c->next->rot = c->rot; + polys += draw_cubes (mi, c->next); + } + glPopMatrix(); + + return polys; +} + + +static void +make_cubes (ModeInfo *mi) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + GLfloat step = 2 * (thickness + displacement); + GLfloat size = 1.0; + cube *tail = 0; + GLfloat cc[4], cstep; + int depth = 0; + cube *c; + + cc[0] = 0.3 + frand(0.7); + cc[1] = 0.3 + frand(0.7); + cc[2] = 0.3 + frand(0.7); + cc[3] = 1; + + if (bp->cubes) abort(); + while (1) + { + cube *c = (cube *) calloc (1, sizeof (*c)); + c->size = size; + c->thickness = thickness; + if (tail) + tail->next = c; + else + bp->cubes = c; + tail = c; + + depth++; + size -= step; + if (size <= step) + break; + } + + cstep = 0.8 / depth; + for (c = bp->cubes; c; c = c->next) + { + memcpy (c->color, cc, sizeof(cc)); + cc[0] -= cstep; + cc[1] -= cstep; + cc[2] -= cstep; + } +} + + +static GLfloat +ease_fn (GLfloat r) +{ + return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */ +} + + +static GLfloat +ease_ratio (GLfloat r) +{ + GLfloat ease = 0.5; + if (r <= 0) return 0; + else if (r >= 1) return 1; + else if (r <= ease) return ease * ease_fn (r / ease); + else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease); + else return r; +} + + +static void +tick_oscillators (ModeInfo *mi) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + oscillator *prev = 0; + oscillator *a = bp->oscillators; + GLfloat tick = 0.1 / speed; + + while (a) + { + oscillator *next = a->next; + a->ratio += tick * a->speed; + if (a->ratio > 1) + a->ratio = 1; + + *a->var = a->from + (a->to - a->from) * ease_ratio (a->ratio); + + if (a->ratio < 1) /* mid cycle */ + prev = a; + else if (--a->remaining <= 0) /* ended, and expired */ + { + if (prev) + prev->next = next; + else + bp->oscillators = next; + free (a); + } + else /* keep going the other way */ + { + GLfloat swap = a->from; + a->from = a->to; + a->to = swap; + a->ratio = 0; + prev = a; + } + + a = next; + } +} + + +static void +add_oscillator (ModeInfo *mi, double *var, GLfloat speed, GLfloat to, + int repeat) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + oscillator *a; + + /* If an oscillator is already running on this variable, don't + add another. */ + for (a = bp->oscillators; a && a->next; a = a->next) + if (a->var == var) + return; + + a = (oscillator *) calloc (1, sizeof (*a)); + if (repeat <= 0) abort(); + a->ratio = 0; + a->from = *var; + a->to = to; + a->speed = speed; + a->var = var; + a->remaining = repeat; + a->next = bp->oscillators; + bp->oscillators = a; +# if 0 + fprintf (stderr, "%s: %3d %6.2f -> %6.2f %s\n", + progname, repeat, *var, to, + (var == &bp->midpoint.z ? "z" : + var == &bp->tilt ? "tilt" : + var == &bp->axial_radius ? "r" : + var == &bp->speed ? "speed" : "?")); +# endif +} + + +#undef RANDSIGN +#define RANDSIGN() ((random() & 1) ? 1 : -1) + +static void +add_random_oscillator (ModeInfo *mi) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + cube *c = bp->cubes; + double s1 = speed * 0.07; + double s2 = speed * 0.3; + double disp = (thickness + displacement); + int c1 = 1 + ((random() % 4) ? 0 : (random() % 3)); + int c2 = 2; + int n = random() % 6; + + switch (n) { + case 0: add_oscillator (mi, &c->rot.x, s1, 90 * RANDSIGN(), c1); break; + case 1: add_oscillator (mi, &c->rot.y, s1, 90 * RANDSIGN(), c1); break; + case 2: add_oscillator (mi, &c->rot.z, s1, 90 * RANDSIGN(), c1); break; + case 3: add_oscillator (mi, &c->pos.x, s2, disp * RANDSIGN(), c2); break; + case 4: add_oscillator (mi, &c->pos.y, s2, disp * RANDSIGN(), c2); break; + case 5: add_oscillator (mi, &c->pos.z, s2, disp * RANDSIGN(), c2); break; + default: abort(); break; + } +} + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_cube (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + + glViewport (0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective (30.0, 1/h, 1.0, 100.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 30.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + +# 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 + + glClear(GL_COLOR_BUFFER_BIT); +} + + +ENTRYPOINT Bool +cube_handle_event (ModeInfo *mi, XEvent *event) +{ + cube_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; + else if (event->xany.type == KeyPress) + { + KeySym keysym; + char c = 0; + XLookupString (&event->xkey, &c, 1, &keysym, 0); + if (c == ' ' || c == '\t') + { + while (bp->cubes) + { + cube *c = bp->cubes->next; + free (bp->cubes); + bp->cubes = c; + } + + while (bp->oscillators) + { + oscillator *o = bp->oscillators->next; + free (bp->oscillators); + bp->oscillators = o; + } + + if (random() & 1) + { + thickness = 0.03 + frand(0.02); + displacement = (random() & 1) ? 0 : (thickness / 3); + } + else + { + thickness = 0.001 + frand(0.02); + displacement = 0; + } + + make_cubes (mi); + + return True; + } + } + + return False; +} + + +ENTRYPOINT void +init_cube (ModeInfo *mi) +{ + cube_configuration *bp; + int wire = MI_IS_WIREFRAME(mi); + + if (!bps) { + bps = (cube_configuration *) + calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration)); + if (!bps) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } + } + + bp = &bps[MI_SCREEN(mi)]; + + bp->glx_context = init_GL(mi); + + reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + + if (!wire && !do_flat) + { + GLfloat color[4] = {1, 1, 1, 1}; + GLfloat cspec[4] = {1, 1, 0, 1}; + static const GLfloat shiny = 30; + + static GLfloat pos0[4] = { 0.5, -1, -0.5, 0}; + static GLfloat pos1[4] = {-0.75, -1, 0, 0}; + static GLfloat amb[4] = {0, 0, 0, 1}; + static GLfloat dif[4] = {1, 1, 1, 1}; + static GLfloat spc[4] = {1, 1, 1, 1}; + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + glLightfv(GL_LIGHT0, GL_POSITION, pos0); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT0, GL_SPECULAR, spc); + + glLightfv(GL_LIGHT1, GL_POSITION, pos1); + glLightfv(GL_LIGHT1, GL_AMBIENT, amb); + glLightfv(GL_LIGHT1, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT1, GL_SPECULAR, spc); + + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + glMaterialfv (GL_FRONT, GL_SPECULAR, cspec); + glMateriali (GL_FRONT, GL_SHININESS, shiny); + } + + { + double spin_speed = 0.05; + double wander_speed = 0.005; + double spin_accel = 1.0; + + bp->rot = make_rotator (do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + spin_accel, + do_wander ? wander_speed : 0, + True); + bp->trackball = gltrackball_init (True); + } + + if (thickness > 0.5) + thickness = 0.5; + if (displacement > 0.5) + displacement = 0.5; + + if (thickness <= 0.0001) + { + if (random() & 1) + { + thickness = 0.03 + frand(0.02); + displacement = (random() & 1) ? 0 : (thickness / 3); + } + else + { + thickness = 0.001 + frand(0.02); + displacement = 0; + } + } + + make_cubes (mi); +} + + +ENTRYPOINT void +draw_cube (ModeInfo *mi) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + + if (!bp->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context)); + + glShadeModel(GL_SMOOTH); + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glEnable(GL_CULL_FACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix (); + + glScalef(1.1, 1.1, 1.1); + + { + double x, y, z; + get_position (bp->rot, &x, &y, &z, !bp->button_down_p); + glTranslatef((x - 0.5) * 4, + (y - 0.5) * 4, + (z - 0.5) * 2); + + gltrackball_rotate (bp->trackball); + + get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p); + glRotatef (x * 360, 1.0, 0.0, 0.0); + glRotatef (y * 360, 0.0, 1.0, 0.0); + glRotatef (z * 360, 0.0, 0.0, 1.0); + } + + mi->polygon_count = 0; + + glScalef (6, 6, 6); + + mi->polygon_count = draw_cubes (mi, bp->cubes); + glPopMatrix (); + + if (!bp->button_down_p) + tick_oscillators (mi); + + if (! bp->oscillators && + !bp->button_down_p && + !(random() % 60)) + { + bp->cubes->pos.x = bp->cubes->pos.y = bp->cubes->pos.z = 0; + bp->cubes->rot.x = bp->cubes->rot.y = bp->cubes->rot.z = 0; + add_random_oscillator (mi); + } + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + +ENTRYPOINT void +release_cube (ModeInfo *mi) +{ + cube_configuration *bp = &bps[MI_SCREEN(mi)]; + while (bp->cubes) + { + cube *c = bp->cubes->next; + free (bp->cubes); + bp->cubes = c; + } + + while (bp->oscillators) + { + oscillator *o = bp->oscillators->next; + free (bp->oscillators); + bp->oscillators = o; + } +} + + +XSCREENSAVER_MODULE_2 ("CubeTwist", cubetwist, cube) + +#endif /* USE_GL */