/* snafooz, "The" squishy foam puzzle. Trademark, registered or otherwise, of * Idea Lab (idealab.net). No permission secured from Idea Lab, despite my * best efforts; they simply didn't respond. */ /* * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * This is the first major thing I've done in GL. As such, all errors are * mine, because I was too lazy to read up on how to do it right. Several * ideas (and bugfixes) came from tooling with lightlab * (http://www.jwz.org/lightlab/) */ /* TODO: - "solveme" mode X make it do something when solved - some wackiness here that I've not figured out - have it print out the solution on the console - interactive mode - texture on front side of the 2x2 area - have one, need to do it better, though - figure out logic for permutations, needed for larger shapes where "pick 10 of 12" applies - use an array of pieces instead of piecemin/piecemax - populate the array using permutations - further optimise the piece drawing - collision detection on all the time, not just at landing - better use of pieces in brute force solveme mode - need a pseudo-random way of picking them - can't be random because that screws up the brute force - complex shapes - 3x1x1 - 2x2x1 - 2x2x2 - 3x3x1 cross - 3x3x3 cross - flat shape data (i.e. return the pieces to their frame) - can use a 2x3 grid without having to define the frame - use linked list in snafu() - use collision detection in snafu() - multiple collision detection points - "show collisions" should light up the entire collision area - mode where shapes are wireframe and impact areas are lit - kill off more globals - change time-based trigger to mechanics-based */ /* xscreensaver glue: cribbed from other hacks */ #include #ifdef STANDALONE # define PROGCLASS "snafooz" # define HACK_INIT snafooz_init # define HACK_DRAW snafooz_draw # define HACK_RESHAPE snafooz_reshape # define snafooz_opts xlockmore_opts # define DEFAULTS "*delay: 20000\n"\ "*showFPS: False\n" \ "*DriftSpeed: 0.05\n" \ #include "xlockmore.h" #else # include "xlock.h" #endif #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #include /* Wow. Everyone is defining their own linked list where necessary. Ok, ME * TOO. Note, this isn't part of the stock xscreensaver init, but it needs to * be defined before the snafoozstruct below */ typedef struct _occupied { int owner; GLfloat x; GLfloat y; GLfloat z; struct _occupied *next; } occupied_list; /* This keeps track of what we've tried in brute-force solveme mode */ typedef struct _tried { int piece; int flipped; int rotated; int place; int ok; struct _tried *next; } tried_list; #include "rotator.h" typedef struct { /* xscreensaver goop */ GLXContext *glx_context; Window window; /* local goop */ occupied_list *occupied; tried_list *tried; GLfloat lastcollision[3]; int showcollision; time_t lastplaced; int busy; rotator *rot; double rotx, roty, rotz; XImage *texture; int puzzle; int minpiece; int maxpiece; int solved; } snafoozstruct; static snafoozstruct *snafooz = NULL; static int showcollisions; static int wireframe; static int showframe; static float driftspeed; static int puzzle; static XrmOptionDescRec opts[] = { { "-showcollisions", ".snafooz.showcollisions", XrmoptionNoArg, (caddr_t) "true" }, { "-wireframe", ".snafooz.wireframe", XrmoptionNoArg, (caddr_t) "true" }, { "-showframe", ".snafooz.showframe", XrmoptionNoArg, (caddr_t) "true" }, { "-driftspeed", ".snafooz.driftspeed", XrmoptionSepArg, 0 }, { "-puzzle", ".snafooz.puzzle", XrmoptionSepArg, 0 }, }; static argtype vars[] = { {(caddr_t *) &showcollisions, "showcollisions", "ShowCollisions", "False", t_Bool }, {(caddr_t *) &wireframe, "wireframe", "Wireframe", "False", t_Bool }, {(caddr_t *) &showframe, "showframe", "ShowFrame", "False", t_Bool }, {(caddr_t *) &driftspeed, "driftspeed", "DriftSpeed", "0.05", t_Float }, {(caddr_t *) &puzzle, "puzzle", "Puzzle", "0", t_Int }, }; ModeSpecOpt snafooz_opts = { countof(opts), opts, countof(vars), vars, NULL }; /* End of xscreensaver glue */ /* This is the texture that goes on the front face */ #include "xpm-ximage.h" #include "../images/snafooz.xpm" /* * This is supposed to be defined in math.h. However, gcc doesn't appear to * give you certain functions if you don't have certain defines enabled when * math.h is included. This prototype is probably a bad idea, but it makes my * code work, thanks. */ extern double round( double x ); /* * A single point, with integer coords. Could use Xpoint for this? */ typedef struct { int x; int y; } pt; /* * Snafooz piece outlines; collections of points defining the outer edge. */ /* YELLOW */ pt shape0[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 2, -2 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape1[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape2[] = { { -3, 1 }, { -3, 3 }, { -2, 3 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 2, -3 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, { -3, 0 }, { -2, 0 }, { -2, 1 }, }; pt shape3[] = { { -3, 1 }, { -3, 2 }, { -1, 2 }, { -1, 3 }, { 0, 3 }, { 0, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape4[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 2, -3 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape5[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, 0 }, { 2, 0 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -1, }, { -3, -1 }, }; /* GREEN */ pt shape6[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, 0 }, { 3, 0 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, { -3, 0 }, { -2, 0 }, { -2, 1 }, }; pt shape7[] = { { -3, 0 }, { -3, 1 }, { -2, 1 }, { -2, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, }; pt shape8[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { 0, -2 }, { 0, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape9[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1, }, { -3, -1 }, }; pt shape10[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 0, 3 }, { 0, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape11[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; /* BLUE */ pt shape12[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 2, -3 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, { -3, 0 }, }; pt shape13[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, 0 }, { 3, 0 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape14[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; /* actually the wrong orientation, but I'm too lazy to retype all this data */ pt shape15[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 2, -2 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape16[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { 0, -2 }, { 0, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, { -3, 0 }, }; pt shape17[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; /* RED */ /* orientation wrong again */ pt shape18[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; /* ditto */ pt shape19[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape20[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -3, 2 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 1 }, { 3, 1 }, { 3, 0 }, { 2, 0 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape21[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, 0 }, { 3, 0 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 3, -2 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape22[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 0, 3 }, { 0, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, { -3, 0 }, }; pt shape23[] = { { -3, 1 }, { -3, 3 }, { -2, 3 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 2, -3 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, { -3, 0 }, { -2, 0 }, { -2, 1 }, }; /* PURPLE */ pt shape24[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 3, -2 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape25[] = { { -3, 1 }, { -2, 1 }, { -2, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { 0, -3 }, { 0, -2 }, { -2, -2 }, { -2, 0 }, { -3, 0 }, }; pt shape26[] = { { -3, 1 }, { -3, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, 0 }, { 3, 0 }, { 3, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape27[] = { /* orientation wrong again */ { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape28[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape29[] = { { -3, 1 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, 0 }, { -2, 0 }, { -1, 1 }, }; /* Orange */ pt shape30[] = { /* orientation wrong again */ { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, }; pt shape31[] = { { -3, 1 }, { -3, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 3, -2 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape32[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, 0 }, { 3, 0 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 3, -2 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -3, -3 }, { -3, -2 }, { -2, -2 }, { -2, -1 }, { -3, -1 }, }; pt shape33[] = { { -3, 1 }, { -3, 3 }, { -2, 3 }, { -2, 2 }, { -1, 2 }, { -1, 3 }, { 1, 3 }, { 1, 2 }, { 2, 2 }, { 2, 1 }, { 3, 1 }, { 3, -1 }, { 2, -1 }, { 2, -2 }, { 1, -2 }, { 1, -3 }, { -1, -3 }, { -1, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 1 }, }; pt shape34[] = { { -3, 1 }, { -2, 1 }, { -2, 2 }, { -3, 2 }, { -3, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 3, 3 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -2 }, { 2, -2 }, { 2, -3 }, { 1, -3 }, { 1, -2 }, { 0, -2 }, { 0, -3 }, { -1, -3 }, { -1, -2 }, { -2, -2 }, { -2, -3 }, { -3, -3 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, { -3, 0 }, }; pt shape35[] = { { -3, 1 }, { -2, 1 }, { -2, 3 }, { -1, 3 }, { -1, 2 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 2, 2 }, { 3, 2 }, { 3, 1 }, { 2, 1 }, { 2, -1 }, { 3, -1 }, { 3, -3 }, { 1, -3 }, { 1, -2 }, { -1, -2 }, { -1, -3 }, { -2, -3 }, { -2, -2 }, { -3, -2 }, { -3, -1 }, { -2, -1 }, { -2, 0 }, { -3, 0 }, }; /* * All the pieces together */ pt *pieces[] = { shape0, shape1, shape2, shape3, shape4, shape5, shape6, shape7, shape8, shape9, shape10, shape11, shape12, shape13, shape14, shape15, shape16, shape17, shape18, shape19, shape20, shape21, shape22, shape23, shape24, shape25, shape26, shape27, shape28, shape29, shape30, shape31, shape32, shape33, shape34, shape35, }; /* Size of each piece (number of points) */ #define countpts(x) (sizeof(x)/sizeof(pt)) int sizes[] = { countpts(shape0), countpts(shape1), countpts(shape2), countpts(shape3), countpts(shape4), countpts(shape5), countpts(shape6), countpts(shape7), countpts(shape8), countpts(shape9), countpts(shape10), countpts(shape11), countpts(shape12), countpts(shape13), countpts(shape14), countpts(shape15), countpts(shape16), countpts(shape17), countpts(shape18), countpts(shape19), countpts(shape20), countpts(shape21), countpts(shape22), countpts(shape23), countpts(shape24), countpts(shape25), countpts(shape26), countpts(shape27), countpts(shape28), countpts(shape29), countpts(shape30), countpts(shape31), countpts(shape32), countpts(shape33), countpts(shape34), countpts(shape35), }; /* * colours */ typedef struct { int min; int max; GLfloat r; GLfloat g; GLfloat b; } colour_range_t; colour_range_t colour_range[] = { /*max,min,r,g,b */ { 0, 5, 1, 1, 0 }, { 6, 11, 0, 1, 0 }, { 12, 17, 0, 0, 1 }, { 18, 23, 1, 0, 0 }, { 24, 29, 1, 0, 1 }, { 30, 35, 1, 0.2, 0.2 }, }; #define YELLOW 0 #define GREEN 1 #define BLUE 2 #define RED 3 #define PURPLE 4 #define ORANGE 5 /* * A translation vector */ typedef struct { GLfloat x; GLfloat y; GLfloat z; } translate_t; /* * Rotations are generally going to be in degrees, hence ints rather than * floats */ typedef struct { int x; int y; int z; } rotate_t; /* * transform to get a piece from the origin (0,0,0) to a given position, * including rotation about the vector and back-to-front flipping if * required. */ typedef struct { int piece; time_t added; int rotated; int flipped; GLfloat offset; GLfloat direction; translate_t translate; rotate_t rotate; } transform_t; /* * transforms to make your basic Snafooz cube */ transform_t cube[] = { /* piece, added, rotated, flipped, offset, direction, translate, rotate */ { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { 90, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { 0, 90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { 0, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { -90, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { 0, -90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 2.5 }, { 0, 180, 0 },}, }; transform_t box2x1x1[] = { { -1, 0, 0, 0, 0, 0, { 0, 0, 5 }, { 90, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 0, 5 }, { -90, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, -2.5, 2.5 }, { 0, 90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, -2.5, 2.5 }, { 0, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, -2.5, 2.5 }, { 0, -90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, -2.5, 2.5 }, { 0, 180, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 2.5, 2.5 }, { 0, 90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 2.5, 2.5 }, { 0, 0, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 2.5, 2.5 }, { 0, -90, 0 },}, { -1, 0, 0, 0, 0, 0, { 0, 2.5, 2.5 }, { 0, 180, 0 },}, }; /* And this is the list of possible puzzles */ transform_t *puzzles[] = { cube, box2x1x1, }; #define counttransforms(x) (sizeof(x)/sizeof(transform_t)) int puzzle_sizes[] = { counttransforms( cube ), counttransforms( box2x1x1 ), }; /* This is how close the pieces are pushed together. When this is set to 1.0, * you can't see the joins except where GL flickers. */ GLfloat dist = 1.0; /* Related: how far away a piece is when it's first placed or when we decide to remove it to the unused pile. Ideally I'd like shapes to drift out until they're offscreen, but I don't know how to test for that just yet. */ #define MAX_OFFSET 4 /* Lighting */ GLfloat l0_ambient[] = { 0.7, 0.7, 0.7, 1.0 }; GLfloat l0_specular[] = { 1, 1, 1, 1 }; GLfloat l0_diffuse[] = { 1, 1, 1, 1 }; GLfloat l0_position[] = { -4, 2, 5, 1 }; /* These are to avoid the presence of magic numbers in the code */ /* Which direction a face is facing in */ #define TOP_FACE 1 #define BOTTOM_FACE 2 #define LEFT_FACE 3 #define RIGHT_FACE 4 #define FRONT_FACE 5 /* unused */ #define BACK_FACE 6 /* unused */ /* Size of a snafooz piece */ #define SNAFOOZ_WIDTH 6 #define SNAFOOZ_HEIGHT 6 /* * Draw a single Snafooz piece */ pt *snafu( int shape ) { int i; pt *occupied = NULL; /* for collision detection */ int lazy_ass_grid[ SNAFOOZ_HEIGHT ][ SNAFOOZ_WIDTH ]; /* size of snafooz piece */ int x, y; int occ_ptr = 0; /* Init the grid */ /* There's nothing this grid is doing that can't be done with the * occupied list and a little more intelligent code. */ for ( y = 0; y < 6; y ++ ) { for ( x = 0; x < 6; x ++ ) { lazy_ass_grid[ x ][ y ] = ( x > 0 && x < 5 && y > 0 && y < 5 ); } } /* * The maximum occupied array is 20 items, since we don't care about * the centrepiece. */ if (( occupied = (pt *) calloc( sizeof( pt ), 20 )) == (pt *)NULL ) { /* well, we could explode here with -ENOMEM, but the thing * will still work without collision detection */ /* return NULL; */ } /* Colour setup */ for ( i = 0; i < sizeof( colour_range ) / sizeof( colour_range_t ); i++ ) { if ( shape >= colour_range[ i ].min && shape <= colour_range[ i ].max ) { GLfloat colour[4]; colour[ 0 ] = colour_range[ i ].r; colour[ 1 ] = colour_range[ i ].g; colour[ 2 ] = colour_range[ i ].b; colour[ 3 ] = 1.0; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colour ); break; } } glFrontFace( GL_CCW ); /* front - winds CCW */ glEnable( GL_TEXTURE_2D ); glBegin( GL_QUADS ); glNormal3f( 0, 0, 1 ); glTexCoord2f( 1, 1 ); glVertex3f( 2, -2, 0.5 ); glTexCoord2f( 0, 1 ); glVertex3f( 2, 2, 0.5 ); glTexCoord2f( 0, 0 ); glVertex3f( -2, 2, 0.5 ); glTexCoord2f( 1, 0 ); glVertex3f( -2, -2, 0.5 ); glEnd(); glDisable( GL_TEXTURE_2D ); /* back - winds CW */ /* Still not 100% clear on why that is, but it works, and I'm not * going to argue. */ glBegin( GL_QUADS ); glNormal3f( 0, 0, -1 ); glVertex3f( -2, -2, -0.5 ); glVertex3f( -2, 2, -0.5 ); glVertex3f( 2, 2, -0.5 ); glVertex3f( 2, -2, -0.5 ); glEnd(); /* edge pieces. * Basically, trace the outer edge of the shape, then figure out if * "cover" pieces are needed for the front and back of what we've * just outlined. * There's some overlap here, because dammit it works and I've not * fixed it. Some parts do get drawn a few more times than is * strictly necessary, though. */ for ( i = 0; i < sizes[shape]; i++ ) { int x1, y1, x2, y2, x3, y3, x4, y4; int side = 0; /* First set of coords is easy */ x2 = pieces[shape][i].x; y2 = pieces[shape][i].y; /* Figure out the second set of coords */ if ( i == 0 ) { x1 = pieces[shape][ sizes[shape]-1 ].x; y1 = pieces[shape][ sizes[shape]-1 ].y; } else { x1 = pieces[shape][ i - 1 ].x; y1 = pieces[shape][ i - 1 ].y; } /* and the third */ if ( i < sizes[shape] - 1 ) { x3 = pieces[shape][ i + 1 ].x; y3 = pieces[shape][ i + 1 ].y; } else { x3 = pieces[shape][ 0 ].x; y3 = pieces[shape][ 0 ].y; } /* Figure out which way this faces */ if ( x1 < x2 ) { side = TOP_FACE; } else if ( x1 > x2 ) { side = BOTTOM_FACE; } else { if ( y1 < y2 ) { side = LEFT_FACE; } else { side = RIGHT_FACE; } } /* Draw the damn thing */ glBegin( GL_QUADS ); glNormal3f( side == LEFT_FACE ? -1 : ( side == RIGHT_FACE ? 1 : 0 ), side == BOTTOM_FACE ? -1 : ( side == TOP_FACE ? 1 : 0 ), 0 ); glVertex3f( x2, y2, 0.5 ); glVertex3f( x2, y2, -0.5 ); glVertex3f( x1, y1, -0.5 ); glVertex3f( x1, y1, 0.5 ); glEnd(); /* Figure out if we're on a concave/convex line segment */ if ( side == TOP_FACE ) { if ( y3 > y2 ) { /* _| */ continue; } } else if ( side == BOTTOM_FACE ) { if ( y3 < y2 ) { /* |~ */ continue; } } else if ( side == LEFT_FACE ) { if ( x3 < x2 ) { /* ~| */ continue; } } else { if ( x3 > x2 ) { /* |_ */ continue; } } /* Don't bother if the piece is entirely within the 2x2 * centrepiece, i.e. already drawn */ if ( x1 >= -2 && x2 >= -2 && x3 >= -2 && x2 <= 2 && x2 <= 2 && x3 <= 2 && y1 >= -2 && y2 >= -2 && y3 >= -2 && y1 <= 2 && y2 <= 2 && y3 <= 2 ) { continue; } /* If we made it this far, we've got more drawin' to do. */ /* Figure out the fourth corner */ if ( side == TOP_FACE || side == BOTTOM_FACE ) { x4 = x1; y4 = y3; } else { x4 = x3; y4 = y1; } /* Need to figure out what squares this occupies */ { int bottom_left_x = MIN( x1, MIN( x2, x3 )); int bottom_left_y = MIN( y1, MIN( y2, y3 )); int top_right_x = MAX( x1, MAX( x2, x3 )); int top_right_y = MAX( y1, MAX( y2, y3 )); int xi, yi; for ( xi = bottom_left_x; xi < top_right_x; xi++ ) { for ( yi = bottom_left_y; yi < top_right_y; yi++ ) { /* Hey, lazy_ass_grid has a purpose! stops some overlap, yay */ if ( lazy_ass_grid[ xi + 3 ][ yi + 3 ] ) { continue; } else { lazy_ass_grid[ xi + 3 ][ yi + 3 ] = 1; } } } } glBegin( GL_QUADS ); glNormal3f( 0, 0, 1 ); /* Front */ glVertex3f( x4, y4, 0.5 ); glVertex3f( x3, y3, 0.5 ); glVertex3f( x2, y2, 0.5 ); glVertex3f( x1, y1, 0.5 ); glEnd(); glBegin( GL_QUADS ); glNormal3f( 0, 0, -1 ); /* Back */ glVertex3f( x1, y1, -0.5 ); glVertex3f( x2, y2, -0.5 ); glVertex3f( x3, y3, -0.5 ); glVertex3f( x4, y4, -0.5 ); glEnd(); } /* We can build the occupancy list now */ for ( y = 0; y < SNAFOOZ_HEIGHT; y ++ ) { for ( x = 0; x < SNAFOOZ_WIDTH; x ++ ) { if ( x > 0 && x < 5 && y > 0 && y < 5 ) { continue; } if ( lazy_ass_grid[ x ][ y ] ) { occupied[ occ_ptr ].x = x - 3; occupied[ occ_ptr ].y = y - 3; occ_ptr++; } } } /* these are 'can't happen' values */ occupied[ occ_ptr ].x = 0; occupied[ occ_ptr ].y = 0; return occupied; } /* Remove a piece from the tried list */ void print_tried( tried_list *tried ) { while( tried ) { printf( "%d[ %d ] ", tried->place, tried->piece ); tried = tried->next; } } tried_list *remove_tried( tried_list *tried, int piece ) { tried_list *prev = NULL, *current = tried, *next = NULL; while( current ) { next = current->next; if ( current->piece == piece ) { if ( prev != NULL ) { prev->next = next; } else { /* head of list */ tried = next; } free( current ); print_tried( tried ); printf( "REMOVED %d\n", piece ); } else { prev = current; } current = next; } return tried; } /* Mark a piece on the tried list as NOT OK (collided) */ tried_list *collide_tried( tried_list *tried, int piece ) { tried_list *current = tried; while( current ) { if ( current->piece == piece ) { current->ok = 0; printf( "Collided: %d\n", piece ); } current = current->next; } return tried; } /* check if a piece is in use in the current puzzle or not. return -1 if it's * ok or return the index in the current puzzle if * it's realy in use */ int used( ModeInfo *mi, int piece ) { snafoozstruct *gp = &snafooz[MI_SCREEN(mi)]; int puzzle = gp->puzzle; int i; for ( i = 0; i < puzzle_sizes[ puzzle ]; i++ ) { if ( puzzles[puzzle][i].piece == piece ) { return i; } } return -1; } /* * pick a random piece and throw it into the puzzle */ void pickpiece( ModeInfo *mi ) { snafoozstruct *gp = &snafooz[MI_SCREEN(mi)]; int piece = -1, p; int place = -1; int flipped = 0; int rotated = 0; int puzzle = gp->puzzle; int min = gp->minpiece; int max = gp->maxpiece; while( piece == -1 ) { if ( gp->tried == NULL || gp->tried->ok ) { /* The placed pieces stack is empty, or the last piece we placed is ok. Pick a piece and somewhere to put it. */ for ( p = min; p <= max; p++ ) { if ( used( mi, p ) == -1 ) { piece = p; break; } } /* pick a spot */ for ( p = 0; p < puzzle_sizes[ puzzle ]; p++ ) { if ( puzzles[ puzzle ][ p ].piece == -1 ) { place = p; break; } } /* If we can't pick a piece, they're all used. If we * can't find a place, they're all occupied. Thus, * we've solved the puzzle. */ if ( place == -1 && place == -1 ) { gp->lastplaced = time( NULL ); gp->solved = 1; printf( "Apparently, this is solved now.\n" ); return; } printf( "PICK: piece %d place %d\n", piece, place ); } else { /* Check the last piece we tried */ piece = gp->tried->piece; place = gp->tried->place; flipped = gp->tried->flipped; rotated = gp->tried->rotated; printf( "LIST: last try piece %d place %d flip %d rotate %d\n", piece, place, flipped, rotated ); /* Increase rotation */ rotated += 90; printf( "LIST: rotated to %d\n", rotated ); /* If we've come full circle, try a different place */ if ( rotated > 270 ) { rotated = 0; /* Find the next free slot */ while( place++ < puzzle_sizes[ puzzle ]) { if ( puzzles[puzzle][place].piece == -1 ) { /* fake out the last thing we tried */ break; } } printf( "LIST: moved to place %d\n", place ); /* If we've tried all places, flip over the piece and try again. */ if ( place > puzzle_sizes[puzzle] ) { place = -1; /* Find the next free slot */ while( place++ < puzzle_sizes[puzzle]) { if ( puzzles[puzzle][place].piece == -1 ) { break; } } /* XXX */ if ( place == puzzle_sizes[ puzzle ] ) { printf( "ERROR: nowhere to go\n" ); exit( 1 ); } flipped++; printf( "LIST: flipped to %d\n", flipped ); if ( flipped > 1 ) { flipped = 0; gp->tried = remove_tried( gp->tried, piece ); printf( "LIST: backtrack\n" ); if ( gp->tried ) { int k; printf( "Piece %d\n", gp->tried->piece ); gp->tried = collide_tried( gp->tried, piece ); for ( k = 0; k < puzzle_sizes[ puzzle ]; k++ ) { if ( puzzles[ puzzle ][k].piece == piece ) { puzzles[puzzle][k].direction = driftspeed; puzzles[puzzle][k].offset = 1; break; } } return; } else { printf( "Can't do anything with %d\n", piece ); } piece = -1; } } } if ( piece != -1 ) { printf( "LIST: trying piece %d place %d flip %d rotate %d\n", piece, place, flipped, rotated ); } else { printf( "LIST: trying again\n" ); } } } if ( piece == -1 ) { /* might have solved the puzzle, dood */ printf( "Can't find a piece to use\n" ); exit( 1 ); } if ( place == -1 ) { printf( "can't find anywhere to put this piece!\n" ); exit( 1 ); } gp->lastplaced = time( NULL ); gp->busy = 1; if (used( mi, piece ) >= 0 ) { return; /* letting the piece exit the puzzle */ } else { if ( puzzles[ puzzle ][ place ].piece == -1 ) { tried_list *new; puzzles[ puzzle ][ place ].piece = piece; puzzles[ puzzle ][ place ].rotated = rotated; puzzles[ puzzle ][ place ].offset = MAX_OFFSET; puzzles[ puzzle ][ place ].direction = -driftspeed; /* drift in */ puzzles[ puzzle ][ place ].flipped = flipped; puzzles[ puzzle ][ place ].added = gp->lastplaced; if (( new = calloc( sizeof( tried_list ), 1 )) == NULL ) { /* XXX ENOMEM */ exit( 1 ); } else { /* nuke the old one */ gp->tried = remove_tried( gp->tried, piece ); new->piece = piece; new->place = place; new->rotated = rotated; new->flipped = flipped; new->next = gp->tried; new->ok = 1; gp->tried = new; print_tried( gp->tried ); printf( "ADDED %d in %d\n", piece, place ); } } } } /* check for a collision. returns the owner of the location if there's a * collision, or -1 for no collision */ int check_collision( occupied_list *occupied, GLfloat x, GLfloat y, GLfloat z ) { occupied_list *luke = occupied; while( luke ) { if ( luke->x == x && luke->y == y && luke->z == z ) { return luke->owner; } else { luke = luke->next; } } return -1; } /* Add an item to the occupied list. */ occupied_list *add_occupied( occupied_list *occupied, int owner, GLfloat x, GLfloat y, GLfloat z ) { occupied_list *new = calloc( sizeof( occupied_list ), 1 ); occupied_list *luke = occupied; if ( new != (occupied_list *)NULL ) { new->owner = owner; new->x = x; new->y = y; new->z = z; if ( occupied ) { while( luke->next ) { luke = luke->next; } luke->next = new; } else { occupied = new; } } /* else ENOMEM XXX */ return occupied; } /* Remove a piece from the occupied list */ occupied_list *remove_occupied( occupied_list *occupied, int piece ) { occupied_list *prev = NULL, *current = occupied, *next = NULL; while( current ) { next = current->next; if ( current->owner == piece ) { if ( prev != NULL ) { prev->next = next; } else { /* head of list */ occupied = next; } free( current ); } else { prev = current; } current = next; } return occupied; } /* Erase the occupied list */ occupied_list *delete_occupied( occupied_list *occupied ) { occupied_list *luke = occupied; while( luke ) { luke = luke->next; free( occupied ); occupied = luke; } return NULL; } /* Draw an impact area XXX make this a full 1x1 cube */ void impactarea( GLfloat x, GLfloat y, GLfloat z ) { glPushMatrix(); glLoadIdentity(); glDisable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); glDisable( GL_LIGHTING ); glColor3f( 0, 255, 0 ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glBegin( GL_QUADS ); glVertex3f( x - 0.5, y - 0.5, z - 0.5); glVertex3f( x - 0.5, y + 0.5, z - 0.5); glVertex3f( x + 0.5, y + 0.5, z + 0.5); glVertex3f( x + 0.5, y - 0.5, z + 0.5); glEnd(); glEnable( GL_CULL_FACE ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); glPopMatrix(); } /* The main drawing function */ void snafooz_draw( ModeInfo *mi ) { Display *display = MI_DISPLAY( mi ); Window window = MI_WINDOW( mi ); snafoozstruct *gp; pt *this_one_occupied = NULL; GLfloat x,y,z; int i, j; int owner; int puzzle; /* don't bother if the global struct isn't set */ if ( snafooz == NULL ) return; gp = &snafooz[MI_SCREEN(mi)]; MI_IS_DRAWN(mi) = True; /* Also don't bother if glx_context isn't set */ if ( !gp->glx_context) return; glXMakeCurrent( display, window, *(gp->glx_context)); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); /* Apply rotations to the whole view */ /* I should probably be doing smart things with the MODEL matrix, but this works. Basically, I run into gimbal lock problems. At least, I think that's what's happening. So screw it. */ glMatrixMode( GL_PROJECTION ); glPushMatrix(); get_rotation( gp->rot, &gp->rotx, &gp->roty, &gp->rotz, True ); glRotatef( gp->rotx * 360, 1, 0, 0 ); glRotatef( gp->roty * 360, 0, 1, 0 ); glRotatef( gp->rotz * 360, 0, 0, 1 ); /* Show off the solution */ if ( gp->solved ) { dist += ( driftspeed / 4 ) * gp->solved; if ( dist < 1 || dist > 2 ) { gp->solved = -(gp->solved); } } glMatrixMode( GL_MODELVIEW ); puzzle = gp->puzzle; if ( showframe ) { for ( i = 0; i < puzzle_sizes[ puzzle ]; i++ ) { glLoadIdentity(); /* Rotate to the correct orientation */ glRotatef( puzzles[ puzzle ][ i ].rotate.x, 1, 0, 0 ); glRotatef( puzzles[ puzzle ][ i ].rotate.y, 0, 1, 0 ); glRotatef( puzzles[ puzzle ][ i ].rotate.z, 0, 0, 1 ); /* Push the piece out to the correct location. */ glTranslatef( puzzles[ puzzle ][ i ].translate.x, puzzles[ puzzle ][ i ].translate.y, puzzles[ puzzle ][ i ].translate.z ); glDisable( GL_LIGHTING ); glDisable( GL_CULL_FACE ); if ( !wireframe ) glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); glColor3f( 1, 1, 1 ); glBegin( GL_QUADS ); glVertex3f( -3, -3, 0.5 ); glVertex3f( -3, 3, 0.5 ); glVertex3f( 3, 3, 0.5 ); glVertex3f( 3, -3, 0.5 ); glEnd(); if ( !wireframe ) glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glEnable( GL_CULL_FACE ); glEnable( GL_LIGHTING ); } } /* Figure out where to place the bits in use */ for ( i = 0; i < puzzle_sizes[puzzle]; i++ ) { if ( puzzles[ puzzle ][ i ].piece != -1 ) { GLfloat matrix[16]; GLfloat distance = dist * puzzles[ puzzle ][ i ].offset; glLoadIdentity(); /* Rotate the piece to the correct orientation */ glRotatef( puzzles[ puzzle ][ i ].rotate.x, 1, 0, 0 ); glRotatef( puzzles[ puzzle ][ i ].rotate.y, 0, 1, 0 ); glRotatef( puzzles[ puzzle ][ i ].rotate.z, 0, 0, 1 ); /* Push the piece out to the correct location. We * only float in on the [translated, rotated] z axis, * because otherwise you get weird angular * drift which looks naff. */ glTranslatef( puzzles[ puzzle ][ i ].translate.x /** distance*/, puzzles[ puzzle ][ i ].translate.y /** distance*/, puzzles[ puzzle ][ i ].translate.z * distance ); /* Final Rotations: * One using the z of the translate vector as a * normal, which I sort of understand, but not * really. */ glRotatef( puzzles[ puzzle ][ i ].rotated, 0, 0, puzzles[ puzzle ][ i ].translate.z ); /* And one to do the 180 flip if required */ if ( puzzles[ puzzle ][ i ].flipped ) { glRotatef( 180, 1, 1, 0 ); } this_one_occupied = snafu( puzzles[ puzzle ][ i ].piece ); /* get the current matrix */ glGetFloatv( GL_MODELVIEW_MATRIX, &matrix[ 0 ] ); /* if the piece has landed, update the occupied matrix and do collision detection */ if ( this_one_occupied != NULL && puzzles[ puzzle ][ i ].direction == 0 ) { GLfloat mx, my, mz; for( j = 0; j < 20; j++ ) { x = this_one_occupied[ j ].x + 0.5; y = this_one_occupied[ j ].y + 0.5; z = 0; /* breakout value */ if ( this_one_occupied[j].x == 0 && this_one_occupied[j].y == 0 ) break; /* transpose data to correct location * and store in occupied, check for * abuse */ mx = x * matrix[0] + y * matrix[4] + z * matrix[8] + matrix[12]; my = x * matrix[1] + y * matrix[5] + z * matrix[9] + matrix[13]; mz = x * matrix[2] + y * matrix[6] + z * matrix[10] + matrix[14]; x = mx; y = my; z = mz; /* trim away excess floating point bits */ x = round( x * 10 ) / 10; y = round( y * 10 ) / 10; z = round( z * 10 ) / 10; /* make this an option, I think */ /* impactarea( x, y, z ); */ /* Check for collisions */ owner = check_collision( gp->occupied, x, y, z ); if ( owner != -1 && owner != puzzles[ puzzle ][ i ].piece ) { /* make sure it's the johnny-come-lately piece that gets kicked */ int k; for ( k = 0; k < puzzle_sizes[puzzle]; k ++ ) { if ( puzzles[ puzzle ][ k ].piece == owner && puzzles[ puzzle ][ k ].added > puzzles[ puzzle ][ i ].added ) { puzzles[ puzzle ][k].direction = driftspeed; puzzles[ puzzle ][k].offset = 1; gp->tried = collide_tried( gp->tried, puzzles[ puzzle ][k].piece ); owner = -1; /* flag */ break; } } if ( owner != -1 ) { puzzles[ puzzle ][i].direction = driftspeed; gp->tried = collide_tried( gp->tried, puzzles[ puzzle ][i].piece ); } gp->lastcollision[0] = x; gp->lastcollision[1] = y; gp->lastcollision[2] = z; gp->showcollision = 1; break; /* no point in checking more */ } else { if ( owner != puzzles[ puzzle ][ i ].piece ) { gp->occupied = add_occupied( gp->occupied, puzzles[ puzzle ][ i ].piece, x, y, z ); gp->busy = 0; } } } free( this_one_occupied ); } /* drift the pieces in and out of place */ if ( puzzles[ puzzle ][ i ].direction ) { puzzles[ puzzle ][ i ].offset += puzzles[ puzzle ][ i ].direction; if ( puzzles[ puzzle ][ i ].offset <= 1 ) { puzzles[ puzzle ][ i ].offset = 1; puzzles[ puzzle ][ i ].direction = 0; } else if ( puzzles[ puzzle ][ i ].offset > MAX_OFFSET ) { /* remove the piece */ gp->occupied = remove_occupied( gp->occupied, puzzles[ puzzle ][ i ].piece ); puzzles[ puzzle ][ i ].direction = 0; puzzles[ puzzle ][ i ].piece = -1; gp->showcollision = 0; gp->busy = 0; } } } } /* draw last collision point */ if ( showcollisions ) { if ( gp->showcollision ) { /* XXX make this a full cube */ /* XXX show ALL collision points */ glLoadIdentity(); glDisable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); glDisable( GL_LIGHTING ); glColor3f( 1, 1, 1 ); if ( wireframe ) { glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); } x = gp->lastcollision[0]; y = gp->lastcollision[ 1 ]; z = gp->lastcollision[2]; glBegin( GL_QUADS ); glVertex3f( x - 0.5, y - 0.5, z - 0.5); glVertex3f( x - 0.5, y + 0.5, z - 0.5); glVertex3f( x + 0.5, y + 0.5, z + 0.5); glVertex3f( x + 0.5, y - 0.5, z + 0.5); glEnd(); glEnable( GL_CULL_FACE ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); if ( wireframe ) { glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); } } } glMatrixMode( GL_PROJECTION ); glPopMatrix(); /* Display the unused pieces elsewhere */ glMatrixMode( GL_MODELVIEW ); for ( i = gp->minpiece; i <= gp->maxpiece; i++ ) { if ( used( mi, i ) < 0 ) { glLoadIdentity(); /* Shift */ glTranslatef( 10 * (( i % 6 ) - 2.5 ), -18 + 8 * ( i/6 ), -20 ); /* it looks crap when all the pieces are exactly in * step, so do some arbitrary frobbing here */ glRotatef( fmod(( gp->rotx * 360 + i * 60 ), 360 ), 1, 0, 0 ); glRotatef( fmod(( gp->roty * 360 + i * 60 ), 360 ), 0, 1, 0 ); glRotatef( fmod(( gp->rotz * 360 + i * 60 ), 360 ), 0, 0, 1 ); /* now draw the damn thing */ this_one_occupied = snafu( i ); /* We don't actually use the occupied data here */ if ( this_one_occupied != NULL ) free( this_one_occupied ); } } if ( MI_IS_FPS(mi)) do_fps( mi ); /* pick a piece if we've not done so in the last five seconds */ if ( gp->lastplaced + 5 < time( NULL )) { gp->busy = 0; } if ( !gp->busy ) { pickpiece( mi ); } glFlush(); glXSwapBuffers( display, window ); } void snafooz_reshape( ModeInfo *mi, int w, int h ) { GLdouble aspect = w/h; glViewport( 0, 0, (GLint)w, (GLint)h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum( -aspect, aspect, -1, 1, 1, 9999 ); glTranslatef( 0, 2, -15 ); glLineWidth(1); glPointSize(1); glClear( GL_COLOR_BUFFER_BIT ); } /* * One-shot init function (set up lights and such) */ static void pinit( ModeInfo *mi ) { snafoozstruct *gp; gp = &snafooz[ MI_SCREEN( mi )]; glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glLightfv( GL_LIGHT0, GL_POSITION, l0_position ); glLightfv( GL_LIGHT0, GL_AMBIENT, l0_ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, l0_diffuse ); glLightfv( GL_LIGHT0, GL_SPECULAR, l0_specular ); glEnable( GL_LIGHTING ); glEnable( GL_LIGHT0 ); glEnable( GL_TEXTURE_2D ); glShadeModel( GL_SMOOTH ); /* not sure this is right here, but hey */ gluLookAt(0, 0, 30, /* eye */ 0, 0, 0, /* center */ 0, 1, 0 ); /* up */ /* init vars */ gp->occupied = NULL; gp->lastplaced = 0; /* Texture */ gp->texture = xpm_to_ximage( mi->dpy, mi->xgwa.visual, mi->xgwa.colormap, snafooz_xpm ); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, gp->texture->width /* width */, gp->texture->width/* height */, 0/* border */, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, gp->texture->data ); gp->puzzle = puzzle; /* XXX random pick? */ gp->minpiece = colour_range[YELLOW].min; gp->maxpiece = colour_range[ORANGE].max; gp->solved = 0; } void snafooz_init( ModeInfo *mi ) { snafoozstruct *gp; if ( snafooz == NULL ) { if (( snafooz = ( snafoozstruct *) calloc( MI_NUM_SCREENS(mi), sizeof( snafoozstruct ))) == NULL ) return; } gp = &snafooz[ MI_SCREEN( mi )]; if ((gp->glx_context = init_GL( mi )) != NULL ) { snafooz_reshape( mi, MI_WIDTH( mi ), MI_HEIGHT( mi )); glDrawBuffer( GL_BACK ); pinit( mi ); { double rot_speed = 0.5; gp->rot = make_rotator( rot_speed, rot_speed, rot_speed, 1, 0, True ); } } else { MI_CLEARWINDOW( mi ); } }