From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/glx/rubikblocks.c | 633 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 633 insertions(+) create mode 100644 hacks/glx/rubikblocks.c (limited to 'hacks/glx/rubikblocks.c') diff --git a/hacks/glx/rubikblocks.c b/hacks/glx/rubikblocks.c new file mode 100644 index 0000000..fadeb0d --- /dev/null +++ b/hacks/glx/rubikblocks.c @@ -0,0 +1,633 @@ +/* rubikblocks, Copyright (c) 2009 Vasek Potocek + * + * 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. + */ + +/* RubikBlocks - a Rubik's Mirror Blocks puzzle introduced in 2008. + * No mirrors in this version, though, hence the altered name. + */ + +/* TODO: + * add reflection to the faces + */ + +#define DEFAULTS "*delay: 20000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*suppressRotationAnimation: True\n" \ + +# define free_rubikblocks 0 +# define release_rubikblocks 0 +#include "xlockmore.h" +#include "rotator.h" +#include "gltrackball.h" + +#ifdef USE_GL + +#define DEF_SPIN "True" +#define DEF_WANDER "True" +#define DEF_TEXTURE "True" +#define DEF_RANDOMIZE "False" +#define DEF_SPINSPEED "0.1" +#define DEF_ROTSPEED "3.0" +#define DEF_WANDERSPEED "0.005" +#define DEF_WAIT "40.0" +#define DEF_CUBESIZE "1.0" + +#define SHUFFLE 100 + +#define TEX_WIDTH 64 +#define TEX_HEIGHT 64 +#define BORDER 5 +#define BORDER2 (BORDER*BORDER) + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#define rnd01() ((int)(random()%2)) + +/*************************************************************************/ + +static Bool spin, wander, rndstart, tex; +static float spinspeed, tspeed, wspeed, twait, size; + +static argtype vars[] = { + { &spin, "spin", "Spin", DEF_SPIN, t_Bool}, + { &wander, "wander", "Wander", DEF_WANDER, t_Bool}, + { &rndstart, "randomize", "Randomize", DEF_RANDOMIZE, t_Bool}, + { &tex, "texture", "Texture", DEF_TEXTURE, t_Bool}, + { &spinspeed, "spinspeed", "SpinSpeed", DEF_SPINSPEED, t_Float}, + { &tspeed, "rotspeed", "RotSpeed", DEF_ROTSPEED, t_Float}, + { &wspeed, "wanderspeed", "WanderSpeed", DEF_WANDERSPEED, t_Float}, + { &twait, "wait", "Wait", DEF_WAIT, t_Float}, + { &size, "cubesize", "CubeSize", DEF_CUBESIZE, t_Float}, +}; + +static XrmOptionDescRec opts[] = { + { "-spin", ".spin", XrmoptionNoArg, "True" }, + { "+spin", ".spin", XrmoptionNoArg, "False" }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, + { "-randomize", ".randomize", XrmoptionNoArg, "True" }, + { "+randomize", ".randomize", XrmoptionNoArg, "False" }, + { "-texture", ".texture", XrmoptionNoArg, "True" }, + { "+texture", ".texture", XrmoptionNoArg, "False" }, + { "-spinspeed", ".spinspeed", XrmoptionSepArg, 0 }, + { "-wanderspeed", ".wanderspeed", XrmoptionSepArg, 0 }, + { "-rotspeed", ".rotspeed", XrmoptionSepArg, 0 }, + { "-wait", ".wait", XrmoptionSepArg, 0 }, + { "-cubesize", ".cubesize", XrmoptionSepArg, 0 }, +}; + +ENTRYPOINT ModeSpecOpt rubikblocks_opts = {countof(opts), opts, countof(vars), vars, NULL}; + +#ifdef USE_MODULES +ModStruct rubikblocks_description = +{ "rubikblocks", "init_rubikblocks", "draw_rubikblocks", NULL, + "draw_rubikblocks", "change_rubikblocks", NULL, &rubikblocks_opts, + 25000, 1, 1, 1, 1.0, 4, "", + "Shows randomly shuffling Rubik's Mirror Blocks puzzle", 0, NULL +}; +#endif + +typedef struct { + float pos[3]; /* _original_ position */ + float qr[4]; /* quaternion of rotation */ + Bool act; /* flag if it is undergoing the current rotation */ +} piece_t; + +typedef struct { + GLXContext *glx_context; + rotator *rot; + trackball_state *trackball; + GLfloat ratio; + Bool button_down; + + Bool pause; /* pause between two rotations */ + float qfram[4]; /* quaternion describing the rotation in one anim. frame */ + GLfloat t, tmax; /* rotation clock */ + piece_t pieces[27]; /* type and tilt of all the pieces */ + + unsigned char texture[TEX_HEIGHT][TEX_WIDTH]; + GLuint list_base; + Bool wire; +} rubikblocks_conf; + +static rubikblocks_conf *rubikblocks = NULL; + +static const GLfloat shininess = 20.0; +static const GLfloat ambient[] = {0.0, 0.0, 0.0, 1.0}; +static const GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0}; +static const GLfloat position0[] = {1.0, 1.0, 1.0, 0.0}; +static const GLfloat position1[] = {-1.0, -1.0, 1.0, 0.0}; +static const GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0}; +static const GLfloat material_ambient[] = {0.7, 0.7, 0.7, 1.0}; +static const GLfloat material_diffuse[] = {0.7, 0.7, 0.7, 1.0}; +static const GLfloat material_specular[] = {0.2, 0.2, 0.2, 1.0}; + +/*************************************************************************/ + +/* Multiplies two quaternions, src*dest, and stores the result in dest. */ +static void +mult_quat(float src[4], float dest[4]) +{ + float r, i, j, k; + r = src[0]*dest[0] - src[1]*dest[1] - src[2]*dest[2] - src[3]*dest[3]; + i = src[0]*dest[1] + src[1]*dest[0] + src[2]*dest[3] - src[3]*dest[2]; + j = src[0]*dest[2] + src[2]*dest[0] + src[3]*dest[1] - src[1]*dest[3]; + k = src[0]*dest[3] + src[3]*dest[0] + src[1]*dest[2] - src[2]*dest[1]; + dest[0] = r; + dest[1] = i; + dest[2] = j; + dest[3] = k; +} + +/* Sets the 'act' flag for pieces which will undergo the rotation. */ +static void +flag_pieces(piece_t pieces[27], int axis, int side) +{ + int i, j; + float q[4]; + for(i = 0; i < 27; i++) + { + q[0] = 0; + q[1] = pieces[i].pos[0]; + q[2] = pieces[i].pos[1]; + q[3] = pieces[i].pos[2]; + mult_quat(pieces[i].qr, q); + for(j = 1; j < 4; j++) + q[j] = -q[j]; + mult_quat(pieces[i].qr, q); + for(j = 1; j < 4; j++) + q[j] = -q[j]; + if(fabs(q[axis] - side) < 0.1) + pieces[i].act = True; + else + pieces[i].act = False; + } +} + +/* "Rounds" the value to the nearest from the set {0, +-1/2, +-1/sqrt(2), +-1}. + * It is guaranteed to be pretty close to one when this function is called. */ +static float +settle_value(float v) +{ + if(v > 0.9) return 1; + else if(v < -0.9) return -1; + else if(v > 0.6) return M_SQRT1_2; + else if(v < -0.6) return -M_SQRT1_2; + else if(v > 0.4) return 0.5; + else if(v < -0.4) return -0.5; + else return 0; +} + +static void +randomize(rubikblocks_conf *cp) +{ + int axis, side; + int i, j; + for(i = 0; i < SHUFFLE; i++) + { + axis = (random()%3)+1; + side = rnd01()*2-1; + flag_pieces(cp->pieces, axis, side); + for(j = 1; j < 4; j++) + cp->qfram[j] = 0; + cp->qfram[0] = M_SQRT1_2; + cp->qfram[axis] = M_SQRT1_2; + for(j = 0; j < 27; j++) + { + if(cp->pieces[j].act) + mult_quat(cp->qfram, cp->pieces[j].qr); + } + } +} + +static void +finish(rubikblocks_conf *cp) +{ + static int axis = 1; + int side, angle; + int i, j; + if(cp->pause) + { + switch(axis) + { + case 1: + axis = rnd01()+2; + break; + case 2: + axis = 2*rnd01()+1; + break; + default: + axis = rnd01()+1; + } + side = rnd01()*2-1; + angle = rnd01()+1; + flag_pieces(cp->pieces, axis, side); + cp->pause = False; + cp->tmax = 90.0*angle; + for(i = 1; i < 4; i++) + cp->qfram[i] = 0; + cp->qfram[0] = cos(tspeed*M_PI/360); + cp->qfram[axis] = sin((rnd01()*2-1)*tspeed*M_PI/360); + } + else + { + for(i = 0; i < 27; i++) + { + for(j = 0; j < 4; j++) + { + cp->pieces[i].qr[j] = settle_value(cp->pieces[i].qr[j]); + } + } + cp->pause = True; + cp->tmax = twait; + } + cp->t = 0; +} + +static Bool +draw_main(ModeInfo *mi, rubikblocks_conf *cp) +{ + int i; + double x, y, z; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + get_position(cp->rot, &x, &y, &z, !cp->button_down); + glTranslatef((x-0.5)*6, (y-0.5)*6, -20); + + gltrackball_rotate(cp->trackball); + + get_rotation(cp->rot, &x, &y, &z, !cp->button_down); + glRotatef(x*360, 1, 0, 0); + glRotatef(y*360, 0, 1, 0); + glRotatef(z*360, 0, 0, 1); + glScalef(size, size, size); + +# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ + { + GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi); + int o = (int) current_device_rotation(); + if (o != 0 && o != 180 && o != -180) + glScalef (1/h, 1/h, 1/h); + } +# endif + + if(cp->wire) glColor3f(0.7, 0.7, 0.7); + if(!cp->pause) + for(i = 0; i < 27; i++) + if(cp->pieces[i].act) + mult_quat(cp->qfram, cp->pieces[i].qr); + for(i = 0; i < 27; i++) + { + glPushMatrix(); + if(fabs(cp->pieces[i].qr[0]) < 1) + glRotatef(360/M_PI*acos(cp->pieces[i].qr[0]), + cp->pieces[i].qr[1], cp->pieces[i].qr[2], cp->pieces[i].qr[3]); + glCallList(cp->list_base + i); + glPopMatrix(); + } + if((cp->t += tspeed) > cp->tmax) finish(cp); + return True; +} + +static void +draw_horz_line(rubikblocks_conf *cp, int x1, int x2, int y) +{ + int x, y0 = y, w; + if(y < BORDER) y = -y; + else y = -BORDER; + for(; y < BORDER; y++) { + if(y0+y >= TEX_HEIGHT) break; + w = y*y*255/BORDER2; + for(x = x1; x <= x2; x++) + if(cp->texture[y0+y][x]>w) cp->texture[y0+y][x] = w; + } +} + +static void +draw_vert_line(rubikblocks_conf *cp, int x, int y1, int y2) +{ + int x0 = x, y, w; + if(x= TEX_WIDTH) break; + w = x*x*255/BORDER2; + for(y = y1; y <= y2; y++) + if(cp->texture[y][x0+x]>w) cp->texture[y][x0+x] = w; + } +} + +static void +make_texture(rubikblocks_conf *cp) +{ + int x, y; + for(y = 0; y < TEX_HEIGHT; y++) + for(x = 0; x < TEX_WIDTH; x++) + cp->texture[y][x] = 255; + draw_horz_line(cp, 0, TEX_WIDTH-1, 0); + draw_horz_line(cp, 0, TEX_WIDTH-1, TEX_HEIGHT-1); + draw_vert_line(cp, 0, 0, TEX_HEIGHT-1); + draw_vert_line(cp, TEX_WIDTH-1, 0, TEX_HEIGHT-1); +} + +/* These simple transforms make the actual shape of the pieces. The parameters + * A, B and C affect the excentricity of the pieces in each direction. */ +static float +fx(float x) +{ + const float A = 0.5; + if(x > 1.4) return 1.5 - A; + else if(x < -1.4) return -1.5 - A; + else return x; +} + +static float +fy(float y) +{ + const float B = 0.25; + if(y > 1.4) return 1.5 - B; + else if(y < -1.4) return -1.5 - B; + else return y; +} + +static float +fz(float z) +{ + const float C = 0.0; + if(z > 1.4) return 1.5 - C; + else if(z < -1.4) return -1.5 - C; + else return z; +} + +static void +init_lists(rubikblocks_conf *cp) +{ + GLuint base; + int i; + float x, y, z; + base = cp->list_base = glGenLists(27); + for(i = 0; i < 27; i++) + { + x = cp->pieces[i].pos[0]; + y = cp->pieces[i].pos[1]; + z = cp->pieces[i].pos[2]; + glNewList(base+i, GL_COMPILE); + glBegin(GL_QUAD_STRIP); + glNormal3f(1, 0, 0); + glTexCoord2f(0, 0); + glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5)); + glTexCoord2f(0, 1); + glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5)); + glTexCoord2f(1, 0); + glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5)); + glTexCoord2f(1, 1); + glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5)); + glNormal3f(0, 0, 1); + glTexCoord2f(0, 0); + glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5)); + glTexCoord2f(0, 1); + glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5)); + glNormal3f(-1, 0, 0); + glTexCoord2f(1, 0); + glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5)); + glTexCoord2f(1, 1); + glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5)); + glNormal3f(0, 0, -1); + glTexCoord2f(0, 0); + glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5)); + glTexCoord2f(0, 1); + glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5)); + glEnd(); + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glTexCoord2f(0, 0); + glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5)); + glTexCoord2f(0, 1); + glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5)); + glTexCoord2f(1, 1); + glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5)); + glTexCoord2f(1, 0); + glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5)); + glNormal3f(0, -1, 0); + glTexCoord2f(0, 0); + glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5)); + glTexCoord2f(0, 1); + glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5)); + glTexCoord2f(1, 1); + glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5)); + glTexCoord2f(1, 0); + glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5)); + glEnd(); + glEndList(); + } +} + +/* It looks terrible... FIXME: any other ideas, maybe some anisotropic filtering? */ +/*#define MIPMAP*/ + +static void +init_gl(ModeInfo *mi) +{ + rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)]; +#ifdef MIPMAP + int status; +#endif + cp->wire = MI_IS_WIREFRAME(mi); + +# ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */ + cp->wire = 0; +# endif + + if(MI_IS_MONO(mi)) + tex = False; + if(cp->wire) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + return; + } + + glClearDepth(1.0); + glDrawBuffer(GL_BACK); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glShadeModel(GL_FLAT); + glDepthFunc(GL_LESS); + glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, position0); + glLightfv(GL_LIGHT1, GL_AMBIENT, ambient); + glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); + glLightfv(GL_LIGHT1, GL_POSITION, position1); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHTING); + glEnable(GL_NORMALIZE); + glEnable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); + if (!tex) return; + glEnable(GL_TEXTURE_2D); +#ifdef MIPMAP + clear_gl_error(); + status = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEX_WIDTH, TEX_HEIGHT, + GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture); + if (status) { + const char *s = (char *)gluErrorString(status); + fprintf (stderr, "%s: error mipmapping texture: %s\n", progname, (s?s:"(unknown)")); + exit (1); + } + check_gl_error("mipmapping"); +#else + glTexImage2D(GL_TEXTURE_2D, 0, 1, TEX_WIDTH, TEX_HEIGHT, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture); +#endif + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#ifdef MIPMAP + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +#else + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#endif +} + +static void +init_cp(rubikblocks_conf *cp) +{ + int i, j, k, m; + + cp->pause = True; + cp->t = 0.0; + cp->tmax = twait; + + for(i = -1, m = 0; i <= 1; i++) + for(j = -1; j <= 1; j++) + for(k = -1; k <= 1; k++) + { + cp->pieces[m].pos[0] = k; + cp->pieces[m].pos[1] = j; + cp->pieces[m].pos[2] = i; + cp->pieces[m].qr[0] = 1; + cp->pieces[m].qr[1] = 0; + cp->pieces[m].qr[2] = 0; + cp->pieces[m].qr[3] = 0; + m++; + } + + cp->rot = make_rotator(spin?spinspeed:0, spin?spinspeed:0, spin?spinspeed:0, + 0.1, wander?wspeed:0, True); + cp->trackball = gltrackball_init(True); + + if(rndstart) randomize(cp); +} + +/*************************************************************************/ + +ENTRYPOINT void +reshape_rubikblocks(ModeInfo *mi, int width, int height) +{ + rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)]; + int y = 0; + + if (width > height * 5) { /* tiny window: show middle */ + height = width; + y = -height/2; + } + if(!height) height = 1; + cp->ratio = (GLfloat)width/(GLfloat)height; + glViewport(0, y, (GLint) width, (GLint) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(30.0, cp->ratio, 1.0, 100.0); + glMatrixMode(GL_MODELVIEW); + glClear(GL_COLOR_BUFFER_BIT); +} + +ENTRYPOINT void +init_rubikblocks(ModeInfo *mi) +{ + rubikblocks_conf *cp; + MI_INIT(mi, rubikblocks); + cp = &rubikblocks[MI_SCREEN(mi)]; + + if(tex) + make_texture(cp); + + if ((cp->glx_context = init_GL(mi)) != NULL) + { + init_gl(mi); + init_cp(cp); + init_lists(cp); + reshape_rubikblocks(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } + else + { + MI_CLEARWINDOW(mi); + } +} + +ENTRYPOINT void +draw_rubikblocks(ModeInfo * mi) +{ + Display *display = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + rubikblocks_conf *cp; + if (!rubikblocks) return; + cp = &rubikblocks[MI_SCREEN(mi)]; + MI_IS_DRAWN(mi) = True; + if (!cp->glx_context) return; + mi->polygon_count = 0; + glXMakeCurrent(display, window, *(cp->glx_context)); + if (!draw_main(mi, cp)) + { + MI_ABORT(mi); + return; + } + if (MI_IS_FPS(mi)) do_fps (mi); + glFlush(); + glXSwapBuffers(display, window); +} + +#ifndef STANDALONE +ENTRYPOINT void +change_rubikblocks(ModeInfo * mi) +{ + rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)]; + if (!cp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context)); + init_gl(mi); +} +#endif /* !STANDALONE */ + +ENTRYPOINT Bool +rubikblocks_handle_event (ModeInfo *mi, XEvent *event) +{ + rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)]; + + if (gltrackball_event_handler (event, cp->trackball, + MI_WIDTH (mi), MI_HEIGHT (mi), + &cp->button_down)) + return True; + + return False; +} + + +XSCREENSAVER_MODULE ("RubikBlocks", rubikblocks) + +#endif -- cgit v1.2.3-55-g7522