mirror of https://github.com/fltk/fltk
1498 lines
31 KiB
C++
1498 lines
31 KiB
C++
//
|
|
// OpenGL puzzle demo for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// This is a GLUT demo program to demonstrate fltk's GLUT emulation.
|
|
// Search for "fltk" to find all the changes
|
|
//
|
|
// Copyright 1998-2024 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
// Convenience options 'n' and ' ' and command line switch '-n' added for FLTK
|
|
|
|
// this block added for fltk's distribution so it will compile w/o OpenGL:
|
|
#include <config.h>
|
|
#if !HAVE_GL || !HAVE_GL_GLU_H
|
|
#include <FL/Fl.H>
|
|
#include <FL/fl_message.H>
|
|
int main(int, char**) {
|
|
fl_alert("This demo does not work without GL and GLU");
|
|
return 1;
|
|
}
|
|
#else
|
|
// end of added block
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <FL/glut.H> // changed for fltk
|
|
#include <FL/glu.h> // added for fltk
|
|
#include "trackball.c" // changed from trackball.h for fltk
|
|
|
|
#define WIDTH 4
|
|
#define HEIGHT 5
|
|
#define PIECES 10
|
|
#define OFFSETX -2.0f
|
|
#define OFFSETY -2.5f
|
|
#define OFFSETZ -0.5f
|
|
|
|
typedef char Config[HEIGHT][WIDTH];
|
|
|
|
struct puzzle {
|
|
struct puzzle *backptr;
|
|
struct puzzle *solnptr;
|
|
Config pieces;
|
|
struct puzzle *next;
|
|
unsigned hashvalue;
|
|
};
|
|
|
|
#define HASHSIZE 10691
|
|
|
|
struct puzzlelist {
|
|
struct puzzle *puzzle;
|
|
struct puzzlelist *next;
|
|
};
|
|
|
|
static char convert[PIECES + 1] =
|
|
{0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 4};
|
|
|
|
static unsigned char colors[PIECES + 1][3] =
|
|
{
|
|
{0, 0, 0},
|
|
{255, 255, 127},
|
|
{255, 255, 127},
|
|
{255, 255, 127},
|
|
{255, 255, 127},
|
|
{255, 127, 255},
|
|
{255, 127, 255},
|
|
{255, 127, 255},
|
|
{255, 127, 255},
|
|
{255, 127, 127},
|
|
{255, 255, 255},
|
|
};
|
|
|
|
void changeState(void);
|
|
void animate(void);
|
|
|
|
static struct puzzle *hashtable[HASHSIZE];
|
|
static struct puzzle *startPuzzle;
|
|
static struct puzzlelist *puzzles;
|
|
static struct puzzlelist *lastentry;
|
|
|
|
int curX, curY, visible;
|
|
|
|
#define MOVE_SPEED 0.2f
|
|
static unsigned char movingPiece;
|
|
static float move_x, move_y;
|
|
static float curquat[4];
|
|
static int doubleBuffer = 1;
|
|
static int depth = 1;
|
|
|
|
static char xsize[PIECES + 1] =
|
|
{0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2};
|
|
static char ysize[PIECES + 1] =
|
|
{0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2};
|
|
static float zsize[PIECES + 1] =
|
|
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.6f};
|
|
|
|
static Config startConfig =
|
|
{
|
|
{8, 10, 10, 7},
|
|
{8, 10, 10, 7},
|
|
{6, 9, 9, 5},
|
|
{6, 4, 3, 5},
|
|
{2, 0, 0, 1}
|
|
};
|
|
|
|
static Config thePuzzle =
|
|
{
|
|
{8, 10, 10, 7},
|
|
{8, 10, 10, 7},
|
|
{6, 9, 9, 5},
|
|
{6, 4, 3, 5},
|
|
{2, 0, 0, 1}
|
|
};
|
|
|
|
static int xadds[4] =
|
|
{-1, 0, 1, 0};
|
|
static int yadds[4] =
|
|
{0, -1, 0, 1};
|
|
|
|
static long W = 400, H = 300;
|
|
static GLint viewport[4];
|
|
|
|
#define srandom srand
|
|
#define random() (rand() >> 2)
|
|
|
|
unsigned
|
|
hash(Config config)
|
|
{
|
|
int i, j, value;
|
|
|
|
value = 0;
|
|
for (i = 0; i < HEIGHT; i++) {
|
|
for (j = 0; j < WIDTH; j++) {
|
|
value = value + convert[(int)config[i][j]];
|
|
value *= 6;
|
|
}
|
|
}
|
|
return (value);
|
|
}
|
|
|
|
int
|
|
solution(Config config)
|
|
{
|
|
if (config[4][1] == 10 && config[4][2] == 10)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
float boxcoords[][3] =
|
|
{
|
|
{0.2f, 0.2f, 0.9f},
|
|
{0.8f, 0.2f, 0.9f},
|
|
{0.8f, 0.8f, 0.9f},
|
|
{0.2f, 0.8f, 0.9f},
|
|
{0.2f, 0.1f, 0.8f},
|
|
{0.8f, 0.1f, 0.8f},
|
|
{0.9f, 0.2f, 0.8f},
|
|
{0.9f, 0.8f, 0.8f},
|
|
{0.8f, 0.9f, 0.8f},
|
|
{0.2f, 0.9f, 0.8f},
|
|
{0.1f, 0.8f, 0.8f},
|
|
{0.1f, 0.2f, 0.8f},
|
|
{0.2f, 0.1f, 0.2f},
|
|
{0.8f, 0.1f, 0.2f},
|
|
{0.9f, 0.2f, 0.2f},
|
|
{0.9f, 0.8f, 0.2f},
|
|
{0.8f, 0.9f, 0.2f},
|
|
{0.2f, 0.9f, 0.2f},
|
|
{0.1f, 0.8f, 0.2f},
|
|
{0.1f, 0.2f, 0.2f},
|
|
{0.2f, 0.2f, 0.1f},
|
|
{0.8f, 0.2f, 0.1f},
|
|
{0.8f, 0.8f, 0.1f},
|
|
{0.2f, 0.8f, 0.1f},
|
|
};
|
|
|
|
float boxnormals[][3] =
|
|
{
|
|
{0, 0, 1}, /* 0 */
|
|
{0, 1, 0},
|
|
{1, 0, 0},
|
|
{0, 0, -1},
|
|
{0, -1, 0},
|
|
{-1, 0, 0},
|
|
{0.7071f, 0.7071f, 0.0000f}, /* 6 */
|
|
{0.7071f, -0.7071f, 0.0000f},
|
|
{-0.7071f, 0.7071f, 0.0000f},
|
|
{-0.7071f, -0.7071f, 0.0000f},
|
|
{0.7071f, 0.0000f, 0.7071f}, /* 10 */
|
|
{0.7071f, 0.0000f, -0.7071f},
|
|
{-0.7071f, 0.0000f, 0.7071f},
|
|
{-0.7071f, 0.0000f, -0.7071f},
|
|
{0.0000f, 0.7071f, 0.7071f}, /* 14 */
|
|
{0.0000f, 0.7071f, -0.7071f},
|
|
{0.0000f, -0.7071f, 0.7071f},
|
|
{0.0000f, -0.7071f, -0.7071f},
|
|
{0.5774f, 0.5774f, 0.5774f}, /* 18 */
|
|
{0.5774f, 0.5774f, -0.5774f},
|
|
{0.5774f, -0.5774f, 0.5774f},
|
|
{0.5774f, -0.5774f, -0.5774f},
|
|
{-0.5774f, 0.5774f, 0.5774f},
|
|
{-0.5774f, 0.5774f, -0.5774f},
|
|
{-0.5774f, -0.5774f, 0.5774f},
|
|
{-0.5774f, -0.5774f, -0.5774f},
|
|
};
|
|
|
|
int boxfaces[][4] =
|
|
{
|
|
{0, 1, 2, 3}, /* 0 */
|
|
{9, 8, 16, 17},
|
|
{6, 14, 15, 7},
|
|
{20, 23, 22, 21},
|
|
{12, 13, 5, 4},
|
|
{19, 11, 10, 18},
|
|
{7, 15, 16, 8}, /* 6 */
|
|
{13, 14, 6, 5},
|
|
{18, 10, 9, 17},
|
|
{19, 12, 4, 11},
|
|
{1, 6, 7, 2}, /* 10 */
|
|
{14, 21, 22, 15},
|
|
{11, 0, 3, 10},
|
|
{20, 19, 18, 23},
|
|
{3, 2, 8, 9}, /* 14 */
|
|
{17, 16, 22, 23},
|
|
{4, 5, 1, 0},
|
|
{20, 21, 13, 12},
|
|
{2, 7, 8, -1}, /* 18 */
|
|
{16, 15, 22, -1},
|
|
{5, 6, 1, -1},
|
|
{13, 21, 14, -1},
|
|
{10, 3, 9, -1},
|
|
{18, 17, 23, -1},
|
|
{11, 4, 0, -1},
|
|
{20, 12, 19, -1},
|
|
};
|
|
|
|
#define NBOXFACES (sizeof(boxfaces)/sizeof(boxfaces[0]))
|
|
|
|
/* Draw a box. Bevel as desired. */
|
|
void
|
|
drawBox(int piece, float xoff, float yoff)
|
|
{
|
|
int xlen, ylen;
|
|
int i, k;
|
|
float x, y, z;
|
|
float zlen;
|
|
float *v;
|
|
|
|
xlen = xsize[piece];
|
|
ylen = ysize[piece];
|
|
zlen = zsize[piece];
|
|
|
|
glColor3ubv(colors[piece]);
|
|
glBegin(GL_QUADS);
|
|
for (i = 0; i < 18; i++) {
|
|
glNormal3fv(boxnormals[i]);
|
|
for (k = 0; k < 4; k++) {
|
|
if (boxfaces[i][k] == -1)
|
|
continue;
|
|
v = boxcoords[boxfaces[i][k]];
|
|
x = v[0] + OFFSETX;
|
|
if (v[0] > 0.5)
|
|
x += xlen - 1;
|
|
y = v[1] + OFFSETY;
|
|
if (v[1] > 0.5)
|
|
y += ylen - 1;
|
|
z = v[2] + OFFSETZ;
|
|
if (v[2] > 0.5)
|
|
z += zlen - 1;
|
|
glVertex3f(xoff + x, yoff + y, z);
|
|
}
|
|
}
|
|
glEnd();
|
|
glBegin(GL_TRIANGLES);
|
|
for (i = 18; i < int(NBOXFACES); i++) {
|
|
glNormal3fv(boxnormals[i]);
|
|
for (k = 0; k < 3; k++) {
|
|
if (boxfaces[i][k] == -1)
|
|
continue;
|
|
v = boxcoords[boxfaces[i][k]];
|
|
x = v[0] + OFFSETX;
|
|
if (v[0] > 0.5)
|
|
x += xlen - 1;
|
|
y = v[1] + OFFSETY;
|
|
if (v[1] > 0.5)
|
|
y += ylen - 1;
|
|
z = v[2] + OFFSETZ;
|
|
if (v[2] > 0.5)
|
|
z += zlen - 1;
|
|
glVertex3f(xoff + x, yoff + y, z);
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
float containercoords[][3] =
|
|
{
|
|
{-0.1f, -0.1f, 1.0f},
|
|
{-0.1f, -0.1f, -0.1f},
|
|
{4.1f, -0.1f, -0.1f},
|
|
{4.1f, -0.1f, 1.0f},
|
|
{1.0f, -0.1f, 0.6f}, /* 4 */
|
|
{3.0f, -0.1f, 0.6f},
|
|
{1.0f, -0.1f, 0.0f},
|
|
{3.0f, -0.1f, 0.0f},
|
|
{1.0f, 0.0f, 0.0f}, /* 8 */
|
|
{3.0f, 0.0f, 0.0f},
|
|
{3.0f, 0.0f, 0.6f},
|
|
{1.0f, 0.0f, 0.6f},
|
|
{0.0f, 0.0f, 1.0f}, /* 12 */
|
|
{4.0f, 0.0f, 1.0f},
|
|
{4.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 5.0f, 0.0f}, /* 16 */
|
|
{0.0f, 5.0f, 1.0f},
|
|
{4.0f, 5.0f, 1.0f},
|
|
{4.0f, 5.0f, 0.0f},
|
|
{-0.1f, 5.1f, -0.1f}, /* 20 */
|
|
{4.1f, 5.1f, -0.1f},
|
|
{4.1f, 5.1f, 1.0f},
|
|
{-0.1f, 5.1f, 1.0f},
|
|
};
|
|
|
|
float containernormals[][3] =
|
|
{
|
|
{0, -1, 0},
|
|
{0, -1, 0},
|
|
{0, -1, 0},
|
|
{0, -1, 0},
|
|
{0, -1, 0},
|
|
{0, 1, 0},
|
|
{0, 1, 0},
|
|
{0, 1, 0},
|
|
{1, 0, 0},
|
|
{1, 0, 0},
|
|
{1, 0, 0},
|
|
{-1, 0, 0},
|
|
{-1, 0, 0},
|
|
{-1, 0, 0},
|
|
{0, 1, 0},
|
|
{0, 0, -1},
|
|
{0, 0, -1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
};
|
|
|
|
int containerfaces[][4] =
|
|
{
|
|
{1, 6, 4, 0},
|
|
{0, 4, 5, 3},
|
|
{1, 2, 7, 6},
|
|
{7, 2, 3, 5},
|
|
{16, 19, 18, 17},
|
|
|
|
{23, 22, 21, 20},
|
|
{12, 11, 8, 15},
|
|
{10, 13, 14, 9},
|
|
|
|
{15, 16, 17, 12},
|
|
{2, 21, 22, 3},
|
|
{6, 8, 11, 4},
|
|
|
|
{1, 0, 23, 20},
|
|
{14, 13, 18, 19},
|
|
{9, 7, 5, 10},
|
|
|
|
{12, 13, 10, 11},
|
|
|
|
{1, 20, 21, 2},
|
|
{4, 11, 10, 5},
|
|
|
|
{15, 8, 19, 16},
|
|
{19, 8, 9, 14},
|
|
{8, 6, 7, 9},
|
|
{0, 3, 13, 12},
|
|
{13, 3, 22, 18},
|
|
{18, 22, 23, 17},
|
|
{17, 23, 0, 12},
|
|
};
|
|
|
|
#define NCONTFACES (sizeof(containerfaces)/sizeof(containerfaces[0]))
|
|
|
|
/* Draw the container */
|
|
void
|
|
drawContainer(void)
|
|
{
|
|
int i, k;
|
|
float *v;
|
|
|
|
/* Y is reversed here because the model has it reversed */
|
|
|
|
/* Arbitrary bright wood-like color */
|
|
glColor3ub(209, 103, 23);
|
|
glBegin(GL_QUADS);
|
|
for (i = 0; i < int(NCONTFACES); i++) {
|
|
v = containernormals[i];
|
|
glNormal3f(v[0], -v[1], v[2]);
|
|
for (k = 3; k >= 0; k--) {
|
|
v = containercoords[containerfaces[i][k]];
|
|
glVertex3f(v[0] + OFFSETX, -(v[1] + OFFSETY), v[2] + OFFSETZ);
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
void
|
|
drawAll(void)
|
|
{
|
|
int i, j;
|
|
int piece;
|
|
char done[PIECES + 1];
|
|
float m[4][4];
|
|
|
|
build_rotmatrix(m, curquat);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glTranslatef(0, 0, -10);
|
|
glMultMatrixf(&(m[0][0]));
|
|
glRotatef(180, 0, 0, 1);
|
|
|
|
if (depth) {
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
} else {
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
for (i = 1; i <= PIECES; i++) {
|
|
done[i] = 0;
|
|
}
|
|
glLoadName(0);
|
|
drawContainer();
|
|
for (i = 0; i < HEIGHT; i++) {
|
|
for (j = 0; j < WIDTH; j++) {
|
|
piece = thePuzzle[i][j];
|
|
if (piece == 0)
|
|
continue;
|
|
if (done[piece])
|
|
continue;
|
|
done[piece] = 1;
|
|
glLoadName(piece);
|
|
if (piece == movingPiece) {
|
|
drawBox(piece, move_x, move_y);
|
|
} else {
|
|
drawBox(piece, float(j), float(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
redraw(void)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(45, viewport[2]*1.0/viewport[3], 0.1, 100.0);
|
|
|
|
drawAll();
|
|
|
|
if (doubleBuffer)
|
|
glutSwapBuffers();
|
|
else
|
|
glFinish();
|
|
}
|
|
|
|
void
|
|
solidifyChain(struct puzzle *puzzle)
|
|
{
|
|
int i;
|
|
char buf[256];
|
|
|
|
i = 0;
|
|
while (puzzle->backptr) {
|
|
i++;
|
|
puzzle->backptr->solnptr = puzzle;
|
|
puzzle = puzzle->backptr;
|
|
}
|
|
snprintf(buf, 256, "%d moves to complete!", i);
|
|
glutSetWindowTitle(buf);
|
|
}
|
|
|
|
int
|
|
addConfig(Config config, struct puzzle *back)
|
|
{
|
|
unsigned hashvalue;
|
|
struct puzzle *newpiece;
|
|
struct puzzlelist *newlistentry;
|
|
|
|
hashvalue = hash(config);
|
|
|
|
newpiece = hashtable[hashvalue % HASHSIZE];
|
|
while (newpiece != NULL) {
|
|
if (newpiece->hashvalue == hashvalue) {
|
|
int i, j;
|
|
|
|
for (i = 0; i < WIDTH; i++) {
|
|
for (j = 0; j < HEIGHT; j++) {
|
|
if (convert[(int)config[j][i]] !=
|
|
convert[(int)newpiece->pieces[j][i]])
|
|
goto nomatch;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
nomatch:
|
|
newpiece = newpiece->next;
|
|
}
|
|
|
|
newpiece = (struct puzzle *) malloc(sizeof(struct puzzle));
|
|
newpiece->next = hashtable[hashvalue % HASHSIZE];
|
|
newpiece->hashvalue = hashvalue;
|
|
memcpy(newpiece->pieces, config, HEIGHT * WIDTH);
|
|
newpiece->backptr = back;
|
|
newpiece->solnptr = NULL;
|
|
hashtable[hashvalue % HASHSIZE] = newpiece;
|
|
|
|
newlistentry = (struct puzzlelist *) malloc(sizeof(struct puzzlelist));
|
|
newlistentry->puzzle = newpiece;
|
|
newlistentry->next = NULL;
|
|
|
|
if (lastentry) {
|
|
lastentry->next = newlistentry;
|
|
} else {
|
|
puzzles = newlistentry;
|
|
}
|
|
lastentry = newlistentry;
|
|
|
|
if (back == NULL) {
|
|
startPuzzle = newpiece;
|
|
}
|
|
if (solution(config)) {
|
|
solidifyChain(newpiece);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Checks if a space can move */
|
|
int
|
|
canmove0(Config pieces, int x, int y, int dir, Config newpieces)
|
|
{
|
|
char piece;
|
|
int xadd, yadd;
|
|
int l, m;
|
|
|
|
xadd = xadds[dir];
|
|
yadd = yadds[dir];
|
|
|
|
if (x + xadd < 0 || x + xadd >= WIDTH ||
|
|
y + yadd < 0 || y + yadd >= HEIGHT)
|
|
return 0;
|
|
piece = pieces[y + yadd][x + xadd];
|
|
if (piece == 0)
|
|
return 0;
|
|
memcpy(newpieces, pieces, HEIGHT * WIDTH);
|
|
for (l = 0; l < WIDTH; l++) {
|
|
for (m = 0; m < HEIGHT; m++) {
|
|
if (newpieces[m][l] == piece)
|
|
newpieces[m][l] = 0;
|
|
}
|
|
}
|
|
xadd = -xadd;
|
|
yadd = -yadd;
|
|
for (l = 0; l < WIDTH; l++) {
|
|
for (m = 0; m < HEIGHT; m++) {
|
|
if (pieces[m][l] == piece) {
|
|
int newx, newy;
|
|
|
|
newx = l + xadd;
|
|
newy = m + yadd;
|
|
if (newx < 0 || newx >= WIDTH ||
|
|
newy < 0 || newy >= HEIGHT)
|
|
return 0;
|
|
if (newpieces[newy][newx] != 0)
|
|
return 0;
|
|
newpieces[newy][newx] = piece;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Checks if a piece can move */
|
|
int
|
|
canmove(Config pieces, int x, int y, int dir, Config newpieces)
|
|
{
|
|
int xadd, yadd;
|
|
|
|
xadd = xadds[dir];
|
|
yadd = yadds[dir];
|
|
|
|
if (x + xadd < 0 || x + xadd >= WIDTH ||
|
|
y + yadd < 0 || y + yadd >= HEIGHT)
|
|
return 0;
|
|
if (pieces[y + yadd][x + xadd] == pieces[y][x]) {
|
|
return canmove(pieces, x + xadd, y + yadd, dir, newpieces);
|
|
}
|
|
if (pieces[y + yadd][x + xadd] != 0)
|
|
return 0;
|
|
return canmove0(pieces, x + xadd, y + yadd, (dir + 2) % 4, newpieces);
|
|
}
|
|
|
|
int
|
|
generateNewConfigs(struct puzzle *puzzle)
|
|
{
|
|
int i, j, k;
|
|
Config pieces;
|
|
Config newpieces;
|
|
|
|
memcpy(pieces, puzzle->pieces, HEIGHT * WIDTH);
|
|
for (i = 0; i < WIDTH; i++) {
|
|
for (j = 0; j < HEIGHT; j++) {
|
|
if (pieces[j][i] == 0) {
|
|
for (k = 0; k < 4; k++) {
|
|
if (canmove0(pieces, i, j, k, newpieces)) {
|
|
if (addConfig(newpieces, puzzle))
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
freeSolutions(void)
|
|
{
|
|
struct puzzlelist *nextpuz;
|
|
struct puzzle *puzzle, *next;
|
|
int i;
|
|
|
|
while (puzzles) {
|
|
nextpuz = puzzles->next;
|
|
free((char *) puzzles);
|
|
puzzles = nextpuz;
|
|
}
|
|
lastentry = NULL;
|
|
for (i = 0; i < HASHSIZE; i++) {
|
|
puzzle = hashtable[i];
|
|
hashtable[i] = NULL;
|
|
while (puzzle) {
|
|
next = puzzle->next;
|
|
free((char *) puzzle);
|
|
puzzle = next;
|
|
}
|
|
}
|
|
startPuzzle = NULL;
|
|
}
|
|
|
|
int
|
|
continueSolving(void)
|
|
{
|
|
struct puzzle *nextpuz;
|
|
int i, j;
|
|
int movedPiece;
|
|
int movedir;
|
|
int fromx, fromy;
|
|
int tox, toy;
|
|
|
|
if (startPuzzle == NULL)
|
|
return 0;
|
|
if (startPuzzle->solnptr == NULL) {
|
|
freeSolutions();
|
|
return 0;
|
|
}
|
|
nextpuz = startPuzzle->solnptr;
|
|
movedPiece = 0;
|
|
movedir = 0;
|
|
for (i = 0; i < HEIGHT; i++) {
|
|
for (j = 0; j < WIDTH; j++) {
|
|
if (startPuzzle->pieces[i][j] != nextpuz->pieces[i][j]) {
|
|
if (startPuzzle->pieces[i][j]) {
|
|
movedPiece = startPuzzle->pieces[i][j];
|
|
fromx = j;
|
|
fromy = i;
|
|
if (i < HEIGHT - 1 && nextpuz->pieces[i + 1][j] == movedPiece) {
|
|
movedir = 3;
|
|
} else {
|
|
movedir = 2;
|
|
}
|
|
goto found_piece;
|
|
} else {
|
|
movedPiece = nextpuz->pieces[i][j];
|
|
if (i < HEIGHT - 1 &&
|
|
startPuzzle->pieces[i + 1][j] == movedPiece) {
|
|
fromx = j;
|
|
fromy = i + 1;
|
|
movedir = 1;
|
|
} else {
|
|
fromx = j + 1;
|
|
fromy = i;
|
|
movedir = 0;
|
|
}
|
|
goto found_piece;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
glutSetWindowTitle((char *)"What! No change?");
|
|
freeSolutions();
|
|
return 0;
|
|
|
|
found_piece:
|
|
if (!movingPiece) {
|
|
movingPiece = movedPiece;
|
|
move_x = float(fromx);
|
|
move_y = float(fromy);
|
|
}
|
|
move_x += xadds[movedir] * MOVE_SPEED;
|
|
move_y += yadds[movedir] * MOVE_SPEED;
|
|
|
|
tox = fromx + xadds[movedir];
|
|
toy = fromy + yadds[movedir];
|
|
|
|
if (move_x > tox - MOVE_SPEED / 2 && move_x < tox + MOVE_SPEED / 2 &&
|
|
move_y > toy - MOVE_SPEED / 2 && move_y < toy + MOVE_SPEED / 2) {
|
|
startPuzzle = nextpuz;
|
|
movingPiece = 0;
|
|
}
|
|
memcpy(thePuzzle, startPuzzle->pieces, HEIGHT * WIDTH);
|
|
changeState();
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
solvePuzzle(void)
|
|
{
|
|
struct puzzlelist *nextpuz;
|
|
char buf[256];
|
|
int i;
|
|
|
|
if (solution(thePuzzle)) {
|
|
glutSetWindowTitle((char *)"Puzzle already solved!");
|
|
return 0;
|
|
}
|
|
addConfig(thePuzzle, NULL);
|
|
i = 0;
|
|
|
|
while (puzzles) {
|
|
i++;
|
|
if (generateNewConfigs(puzzles->puzzle))
|
|
break;
|
|
nextpuz = puzzles->next;
|
|
free((char *) puzzles);
|
|
puzzles = nextpuz;
|
|
}
|
|
if (puzzles == NULL) {
|
|
freeSolutions();
|
|
snprintf(buf, 256, "I can't solve it! (%d positions examined)", i);
|
|
glutSetWindowTitle(buf);
|
|
return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
selectPiece(int mousex, int mousey)
|
|
{
|
|
long hits;
|
|
GLuint selectBuf[1024];
|
|
GLuint closest;
|
|
GLuint dist;
|
|
|
|
glSelectBuffer(1024, selectBuf);
|
|
(void) glRenderMode(GL_SELECT);
|
|
glInitNames();
|
|
|
|
/* Because LoadName() won't work with no names on the stack */
|
|
glPushName(0);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPickMatrix(mousex, H - mousey, 4, 4, viewport);
|
|
gluPerspective(45, viewport[2]*1.0/viewport[3], 0.1, 100.0);
|
|
|
|
drawAll();
|
|
|
|
hits = glRenderMode(GL_RENDER);
|
|
if (hits <= 0) {
|
|
return 0;
|
|
}
|
|
closest = 0;
|
|
dist = 0xFFFFFFFFU; //2147483647;
|
|
while (hits) {
|
|
if (selectBuf[(hits - 1) * 4 + 1] < dist) {
|
|
dist = selectBuf[(hits - 1) * 4 + 1];
|
|
closest = selectBuf[(hits - 1) * 4 + 3];
|
|
}
|
|
hits--;
|
|
}
|
|
return closest;
|
|
}
|
|
|
|
void
|
|
nukePiece(int piece)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < HEIGHT; i++) {
|
|
for (j = 0; j < WIDTH; j++) {
|
|
if (thePuzzle[i][j] == piece) {
|
|
thePuzzle[i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
multMatrices(const GLfloat a[16], const GLfloat b[16], GLfloat r[16])
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
r[i * 4 + j] =
|
|
a[i * 4 + 0] * b[0 * 4 + j] +
|
|
a[i * 4 + 1] * b[1 * 4 + j] +
|
|
a[i * 4 + 2] * b[2 * 4 + j] +
|
|
a[i * 4 + 3] * b[3 * 4 + j];
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
makeIdentity(GLfloat m[16])
|
|
{
|
|
m[0 + 4 * 0] = 1;
|
|
m[0 + 4 * 1] = 0;
|
|
m[0 + 4 * 2] = 0;
|
|
m[0 + 4 * 3] = 0;
|
|
m[1 + 4 * 0] = 0;
|
|
m[1 + 4 * 1] = 1;
|
|
m[1 + 4 * 2] = 0;
|
|
m[1 + 4 * 3] = 0;
|
|
m[2 + 4 * 0] = 0;
|
|
m[2 + 4 * 1] = 0;
|
|
m[2 + 4 * 2] = 1;
|
|
m[2 + 4 * 3] = 0;
|
|
m[3 + 4 * 0] = 0;
|
|
m[3 + 4 * 1] = 0;
|
|
m[3 + 4 * 2] = 0;
|
|
m[3 + 4 * 3] = 1;
|
|
}
|
|
|
|
/*
|
|
** inverse = invert(src)
|
|
*/
|
|
int
|
|
invertMatrix(const GLfloat src[16], GLfloat inverse[16])
|
|
{
|
|
int i, j, k, swap;
|
|
float t;
|
|
GLfloat temp[4][4];
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
temp[i][j] = src[i * 4 + j];
|
|
}
|
|
}
|
|
makeIdentity(inverse);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
/*
|
|
** Look for largest element in column */
|
|
swap = i;
|
|
for (j = i + 1; j < 4; j++) {
|
|
if (fabs(temp[j][i]) > fabs(temp[i][i])) {
|
|
swap = j;
|
|
}
|
|
}
|
|
|
|
if (swap != i) {
|
|
/*
|
|
** Swap rows. */
|
|
for (k = 0; k < 4; k++) {
|
|
t = temp[i][k];
|
|
temp[i][k] = temp[swap][k];
|
|
temp[swap][k] = t;
|
|
|
|
t = inverse[i * 4 + k];
|
|
inverse[i * 4 + k] = inverse[swap * 4 + k];
|
|
inverse[swap * 4 + k] = t;
|
|
}
|
|
}
|
|
if (temp[i][i] == 0) {
|
|
/*
|
|
** No non-zero pivot. The matrix is singular, which
|
|
shouldn't ** happen. This means the user gave us a
|
|
bad matrix. */
|
|
return 0;
|
|
}
|
|
t = temp[i][i];
|
|
for (k = 0; k < 4; k++) {
|
|
temp[i][k] /= t;
|
|
inverse[i * 4 + k] /= t;
|
|
}
|
|
for (j = 0; j < 4; j++) {
|
|
if (j != i) {
|
|
t = temp[j][i];
|
|
for (k = 0; k < 4; k++) {
|
|
temp[j][k] -= temp[i][k] * t;
|
|
inverse[j * 4 + k] -= inverse[i * 4 + k] * t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
** This is a screwball function. What it does is the following:
|
|
** Given screen x and y coordinates, compute the corresponding object space
|
|
** x and y coordinates given that the object space z is 0.9 + OFFSETZ.
|
|
** Since the tops of (most) pieces are at z = 0.9 + OFFSETZ, we use that
|
|
** number.
|
|
*/
|
|
int
|
|
computeCoords(int piece, int mousex, int mousey,
|
|
GLfloat * selx, GLfloat * sely)
|
|
{
|
|
GLfloat modelMatrix[16];
|
|
GLfloat projMatrix[16];
|
|
GLfloat finalMatrix[16];
|
|
GLfloat in[4];
|
|
GLfloat a, b, c, d;
|
|
GLfloat top, bot;
|
|
GLfloat z;
|
|
GLfloat w;
|
|
GLfloat height;
|
|
|
|
if (piece == 0)
|
|
return 0;
|
|
height = zsize[piece] - 0.1f + OFFSETZ;
|
|
|
|
glGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix);
|
|
multMatrices(modelMatrix, projMatrix, finalMatrix);
|
|
if (!invertMatrix(finalMatrix, finalMatrix))
|
|
return 0;
|
|
|
|
in[0] = (2.0f * (mousex - viewport[0]) / viewport[2]) - 1;
|
|
in[1] = (2.0f * ((H - mousey) - viewport[1]) / viewport[3]) - 1;
|
|
|
|
a = in[0] * finalMatrix[0 * 4 + 2] +
|
|
in[1] * finalMatrix[1 * 4 + 2] +
|
|
finalMatrix[3 * 4 + 2];
|
|
b = finalMatrix[2 * 4 + 2];
|
|
c = in[0] * finalMatrix[0 * 4 + 3] +
|
|
in[1] * finalMatrix[1 * 4 + 3] +
|
|
finalMatrix[3 * 4 + 3];
|
|
d = finalMatrix[2 * 4 + 3];
|
|
|
|
/*
|
|
** Ok, now we need to solve for z: ** (a + b z) / (c + d
|
|
|
|
z) = height. ** ("height" is the height in object space we
|
|
|
|
want to solve z for) ** ** ==> a + b z = height c +
|
|
height d z ** bz - height d z = height c - a ** z =
|
|
(height c - a) / (b - height d) */
|
|
top = height * c - a;
|
|
bot = b - height * d;
|
|
if (bot == 0.0)
|
|
return 0;
|
|
|
|
z = top / bot;
|
|
|
|
/*
|
|
** Ok, no problem. ** Now we solve for x and y. We know
|
|
that w = c + d z, so we compute it. */
|
|
w = c + d * z;
|
|
|
|
/*
|
|
** Now for x and y: */
|
|
*selx = (in[0] * finalMatrix[0 * 4 + 0] +
|
|
in[1] * finalMatrix[1 * 4 + 0] +
|
|
z * finalMatrix[2 * 4 + 0] +
|
|
finalMatrix[3 * 4 + 0]) / w - OFFSETX;
|
|
*sely = (in[0] * finalMatrix[0 * 4 + 1] +
|
|
in[1] * finalMatrix[1 * 4 + 1] +
|
|
z * finalMatrix[2 * 4 + 1] +
|
|
finalMatrix[3 * 4 + 1]) / w - OFFSETY;
|
|
return 1;
|
|
}
|
|
|
|
static int selected;
|
|
static int selectx, selecty;
|
|
static float selstartx, selstarty;
|
|
|
|
void
|
|
grabPiece(int piece, float selx, float sely)
|
|
{
|
|
int hit;
|
|
|
|
selectx = int(selx);
|
|
selecty = int(sely);
|
|
if (selectx < 0 || selecty < 0 || selectx >= WIDTH || selecty >= HEIGHT) {
|
|
return;
|
|
}
|
|
hit = thePuzzle[selecty][selectx];
|
|
if (hit != piece)
|
|
return;
|
|
if (hit) {
|
|
movingPiece = hit;
|
|
while (selectx > 0 && thePuzzle[selecty][selectx - 1] == movingPiece) {
|
|
selectx--;
|
|
}
|
|
while (selecty > 0 && thePuzzle[selecty - 1][selectx] == movingPiece) {
|
|
selecty--;
|
|
}
|
|
move_x = float(selectx);
|
|
move_y = float(selecty);
|
|
selected = 1;
|
|
selstartx = selx;
|
|
selstarty = sely;
|
|
} else {
|
|
selected = 0;
|
|
}
|
|
changeState();
|
|
}
|
|
|
|
void
|
|
moveSelection(float selx, float sely)
|
|
{
|
|
float deltax, deltay;
|
|
int dir;
|
|
Config newpieces;
|
|
|
|
if (!selected)
|
|
return;
|
|
deltax = selx - selstartx;
|
|
deltay = sely - selstarty;
|
|
|
|
if (fabs(deltax) > fabs(deltay)) {
|
|
deltay = 0;
|
|
if (deltax > 0) {
|
|
if (deltax > 1)
|
|
deltax = 1;
|
|
dir = 2;
|
|
} else {
|
|
if (deltax < -1)
|
|
deltax = -1;
|
|
dir = 0;
|
|
}
|
|
} else {
|
|
deltax = 0;
|
|
if (deltay > 0) {
|
|
if (deltay > 1)
|
|
deltay = 1;
|
|
dir = 3;
|
|
} else {
|
|
if (deltay < -1)
|
|
deltay = -1;
|
|
dir = 1;
|
|
}
|
|
}
|
|
if (canmove(thePuzzle, selectx, selecty, dir, newpieces)) {
|
|
move_x = deltax + selectx;
|
|
move_y = deltay + selecty;
|
|
if (deltax > 0.5) {
|
|
memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
|
|
selectx++;
|
|
selstartx++;
|
|
} else if (deltax < -0.5) {
|
|
memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
|
|
selectx--;
|
|
selstartx--;
|
|
} else if (deltay > 0.5) {
|
|
memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
|
|
selecty++;
|
|
selstarty++;
|
|
} else if (deltay < -0.5) {
|
|
memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
|
|
selecty--;
|
|
selstarty--;
|
|
}
|
|
} else {
|
|
if (deltay > 0 && thePuzzle[selecty][selectx] == 10 &&
|
|
selectx == 1 && selecty == 3) {
|
|
/* Allow visual movement of solution piece outside of the
|
|
|
|
box */
|
|
move_x = float(selectx);
|
|
move_y = sely - selstarty + selecty;
|
|
} else {
|
|
move_x = float(selectx);
|
|
move_y = float(selecty);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dropSelection(void)
|
|
{
|
|
if (!selected)
|
|
return;
|
|
movingPiece = 0;
|
|
selected = 0;
|
|
changeState();
|
|
}
|
|
|
|
static int left_mouse, middle_mouse;
|
|
static int mousex, mousey;
|
|
static int solving;
|
|
static int spinning;
|
|
static int enable_spinning = 1;
|
|
static float lastquat[4];
|
|
static int sel_piece;
|
|
|
|
static void
|
|
Reshape(int width, int height)
|
|
{
|
|
|
|
W = width;
|
|
H = height;
|
|
glViewport(0, 0, (GLsizei)W, (GLsizei)H);
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
}
|
|
|
|
void
|
|
toggleSolve(void)
|
|
{
|
|
if (solving) {
|
|
freeSolutions();
|
|
solving = 0;
|
|
glutChangeToMenuEntry(1, (char *)"Solving", 1);
|
|
glutSetWindowTitle((char *)"glpuzzle");
|
|
movingPiece = 0;
|
|
} else {
|
|
glutChangeToMenuEntry(1, (char *)"Stop solving", 1);
|
|
glutSetWindowTitle((char *)"Solving...");
|
|
if (solvePuzzle()) {
|
|
solving = 1;
|
|
}
|
|
}
|
|
changeState();
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void reset_position(void)
|
|
{
|
|
spinning = 0;
|
|
trackball(curquat, 0.0, 0.0, 0.0, 0.0); // reset position
|
|
glutIdleFunc(animate);
|
|
}
|
|
|
|
void reset(void)
|
|
{
|
|
reset_position();
|
|
if (solving) {
|
|
freeSolutions();
|
|
solving = 0;
|
|
glutChangeToMenuEntry(1, (char *)"Solving", 1);
|
|
glutSetWindowTitle((char *)"glpuzzle");
|
|
movingPiece = 0;
|
|
changeState();
|
|
}
|
|
memcpy(thePuzzle, startConfig, HEIGHT * WIDTH);
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void
|
|
keyboard(unsigned char c, int x, int y)
|
|
{
|
|
int piece;
|
|
|
|
switch (c) {
|
|
case 27:
|
|
exit(0);
|
|
break;
|
|
case ' ':
|
|
case 'n':
|
|
case 'N':
|
|
reset_position();
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
if (solving) {
|
|
freeSolutions();
|
|
solving = 0;
|
|
glutChangeToMenuEntry(1, (char *)"Solving", 1);
|
|
glutSetWindowTitle((char *)"glpuzzle");
|
|
movingPiece = 0;
|
|
changeState();
|
|
}
|
|
piece = selectPiece(x, y);
|
|
if (piece) {
|
|
nukePiece(piece);
|
|
}
|
|
glutPostRedisplay();
|
|
break;
|
|
case 'R':
|
|
case 'r':
|
|
reset();
|
|
break;
|
|
case 'S':
|
|
case 's':
|
|
toggleSolve();
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
depth = 1 - depth;
|
|
if (depth) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
} else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
glutPostRedisplay();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
motion(int x, int y)
|
|
{
|
|
float selx, sely;
|
|
|
|
if (middle_mouse && !left_mouse) {
|
|
if (mousex != x || mousey != y) {
|
|
trackball(lastquat,
|
|
(2.0f*mousex - W) / W,
|
|
(H - 2.0f*mousey) / H,
|
|
(2.0f*x - W) / W,
|
|
(H - 2.0f*y) / H);
|
|
spinning = enable_spinning; // 1 = yes, 0 = disabled (commandline -n)
|
|
} else {
|
|
spinning = 0;
|
|
}
|
|
changeState();
|
|
} else {
|
|
computeCoords(sel_piece, x, y, &selx, &sely);
|
|
moveSelection(selx, sely);
|
|
}
|
|
mousex = x;
|
|
mousey = y;
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void
|
|
mouse(int b, int s, int x, int y)
|
|
{
|
|
float selx, sely;
|
|
|
|
mousex = x;
|
|
mousey = y;
|
|
curX = x;
|
|
curY = y;
|
|
if (s == GLUT_DOWN) {
|
|
switch (b) {
|
|
case GLUT_LEFT_BUTTON:
|
|
if (solving) {
|
|
freeSolutions();
|
|
solving = 0;
|
|
glutChangeToMenuEntry(1, (char *)"Solving", 1);
|
|
glutSetWindowTitle((char *)"glpuzzle");
|
|
movingPiece = 0;
|
|
}
|
|
left_mouse = GL_TRUE;
|
|
sel_piece = selectPiece(mousex, mousey);
|
|
if (!sel_piece) {
|
|
left_mouse = GL_FALSE;
|
|
middle_mouse = GL_TRUE; // let it rotate object
|
|
} else if (computeCoords(sel_piece, mousex, mousey, &selx, &sely)) {
|
|
grabPiece(sel_piece, selx, sely);
|
|
}
|
|
glutPostRedisplay();
|
|
break;
|
|
case GLUT_MIDDLE_BUTTON:
|
|
middle_mouse = GL_TRUE;
|
|
glutPostRedisplay();
|
|
break;
|
|
}
|
|
} else {
|
|
if (left_mouse) {
|
|
left_mouse = GL_FALSE;
|
|
dropSelection();
|
|
glutPostRedisplay();
|
|
} else if (middle_mouse) {
|
|
middle_mouse = GL_FALSE;
|
|
glutPostRedisplay();
|
|
}
|
|
}
|
|
motion(x, y);
|
|
}
|
|
|
|
void
|
|
animate(void)
|
|
{
|
|
if (spinning) {
|
|
add_quats(lastquat, curquat, curquat);
|
|
}
|
|
glutPostRedisplay();
|
|
if (solving) {
|
|
if (!continueSolving()) {
|
|
solving = 0;
|
|
glutChangeToMenuEntry(1, (char *)"Solving", 1);
|
|
glutSetWindowTitle((char *)"glpuzzle");
|
|
}
|
|
}
|
|
if ((!solving && !spinning) || !visible) {
|
|
glutIdleFunc(NULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
changeState(void)
|
|
{
|
|
if (visible) {
|
|
if (!solving && !spinning) {
|
|
glutIdleFunc(NULL);
|
|
} else {
|
|
glutIdleFunc(animate);
|
|
}
|
|
} else {
|
|
glutIdleFunc(NULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
init(void)
|
|
{
|
|
static float lmodel_ambient[] =
|
|
{0.0, 0.0, 0.0, 0.0};
|
|
static float lmodel_twoside[] =
|
|
{GL_FALSE};
|
|
static float lmodel_local[] =
|
|
{GL_FALSE};
|
|
static float light0_ambient[] =
|
|
{0.1f, 0.1f, 0.1f, 1.0f};
|
|
static float light0_diffuse[] =
|
|
{1.0f, 1.0f, 1.0f, 0.0f};
|
|
static float light0_position[] =
|
|
{0.8660254f, 0.5f, 1, 0};
|
|
static float light0_specular[] =
|
|
{0.0, 0.0, 0.0, 0.0};
|
|
static float bevel_mat_ambient[] =
|
|
{0.0, 0.0, 0.0, 1.0};
|
|
static float bevel_mat_shininess[] =
|
|
{40.0};
|
|
static float bevel_mat_specular[] =
|
|
{0.0, 0.0, 0.0, 0.0};
|
|
static float bevel_mat_diffuse[] =
|
|
{1.0, 0.0, 0.0, 0.0};
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_BACK);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClearDepth(1.0);
|
|
|
|
glClearColor(0.5, 0.5, 0.5, 0.0);
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
|
|
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
|
|
glEnable(GL_LIGHT0);
|
|
|
|
glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, lmodel_local);
|
|
glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
|
|
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
|
|
glEnable(GL_LIGHTING);
|
|
|
|
glMaterialfv(GL_FRONT, GL_AMBIENT, bevel_mat_ambient);
|
|
glMaterialfv(GL_FRONT, GL_SHININESS, bevel_mat_shininess);
|
|
glMaterialfv(GL_FRONT, GL_SPECULAR, bevel_mat_specular);
|
|
glMaterialfv(GL_FRONT, GL_DIFFUSE, bevel_mat_diffuse);
|
|
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
glShadeModel(GL_FLAT);
|
|
|
|
trackball(curquat, 0.0, 0.0, 0.0, 0.0);
|
|
srandom((unsigned int)time(NULL));
|
|
}
|
|
|
|
static void
|
|
Usage(void)
|
|
{
|
|
puts("Usage: puzzle [-s]");
|
|
puts(" -s: Run in single buffered mode");
|
|
exit(-1);
|
|
}
|
|
|
|
void
|
|
visibility(int v)
|
|
{
|
|
if (v == GLUT_VISIBLE) {
|
|
visible = 1;
|
|
} else {
|
|
visible = 0;
|
|
}
|
|
changeState();
|
|
}
|
|
|
|
void
|
|
menu(int choice)
|
|
{
|
|
switch(choice) {
|
|
case 1:
|
|
reset_position();
|
|
break;
|
|
case 2:
|
|
toggleSolve();
|
|
break;
|
|
case 3:
|
|
reset();
|
|
break;
|
|
case 4:
|
|
exit(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
long i;
|
|
|
|
Fl::use_high_res_GL(1);
|
|
glutInit(&argc, argv);
|
|
for (i = 1; i < argc; i++) {
|
|
if (argv[i][0] == '-') {
|
|
switch (argv[i][1]) {
|
|
case 'n':
|
|
enable_spinning = 0; // disable (sometimes annoying) spinning behaviour
|
|
break;
|
|
case 's':
|
|
doubleBuffer = 0;
|
|
break;
|
|
default:
|
|
Usage();
|
|
}
|
|
} else {
|
|
Usage();
|
|
}
|
|
}
|
|
|
|
glutInitWindowSize((int)W, (int)H);
|
|
if (doubleBuffer) {
|
|
glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
|
|
} else {
|
|
glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_SINGLE | GLUT_MULTISAMPLE);
|
|
}
|
|
|
|
glutCreateWindow("glpuzzle");
|
|
visible = 1; // added for fltk, bug in original program?
|
|
|
|
init();
|
|
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
|
|
puts("");
|
|
puts("n Normal position - stop spinning");
|
|
puts("r Reset puzzle");
|
|
puts("s Solve puzzle (may take a few seconds to compute)");
|
|
puts("d Destroy a piece - makes the puzzle easier");
|
|
puts("b Toggles the depth buffer on and off");
|
|
puts("");
|
|
puts("Left mouse moves pieces");
|
|
puts("Middle mouse spins the puzzle");
|
|
puts("Right mouse has menu");
|
|
|
|
glutReshapeFunc(Reshape);
|
|
glutDisplayFunc(redraw);
|
|
glutKeyboardFunc(keyboard);
|
|
glutMotionFunc(motion);
|
|
glutMouseFunc(mouse);
|
|
glutVisibilityFunc(visibility);
|
|
glutCreateMenu(menu);
|
|
glutAddMenuEntry("Normal pos", 1);
|
|
glutAddMenuEntry("Solve", 2);
|
|
glutAddMenuEntry("Reset", 3);
|
|
glutAddMenuEntry("Quit", 4);
|
|
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
|
glutMainLoop();
|
|
return 0; /* ANSI C requires main to return int. */
|
|
}
|
|
|
|
#endif // added for fltk's distribution
|