/***************************************************************************
 *   Copyright (C) 2004 by David Sansome                                   *
 *   me@davidsansome.com                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
 
#include <stdlib.h>
#include <kapplication.h>
#include <klocale.h>
#include <kconfig.h>
#include <kglobal.h>
#include <qtimer.h>
#include <kconfig.h>
#include <qlayout.h>
#include <qspinbox.h>
#include <qslider.h>
#include <qcombobox.h>
#include <qgroupbox.h>
#include <qcheckbox.h>
#include <kmessagebox.h>
#include <math.h>
#include <sys/time.h> 
#include "lattice.h"
#include "rsMath.h"
#include "lattice_data.h"


extern "C"
{
	const char *kss_applicationName = "lattice.kss";
	const char *kss_description = I18N_NOOP( "Lattice" );
	const char *kss_version = "0.1";
	
	KLatticeScreenSaver *kss_create( WId id )
	{
		return new KLatticeScreenSaver( id );
	}
	
	QDialog *kss_setup()
	{
		return new KLatticeSetup();
	}
}


KLatticeScreenSaver::KLatticeScreenSaver( WId id )
 : KScreenSaver( id )
{
	readSettings();
	
	_effect = new LatticeWidget( NULL, 0, dLongitude, dLatitude, dThick, dDensity, dDepth, dFov, dPathrand, dSpeed, dTexture, dSmooth, dFog, dWidescreen);
	embed( _effect );
	_effect->show();
}

KLatticeScreenSaver::KLatticeScreenSaver( WId id, int lon, int lat, int thick, int dense, int depth, int fov, int path, int speed, int tex, bool smooth, bool fog, bool wide )
 : KScreenSaver( id ), dPreset(1),
   dLongitude(lon), dLatitude(lat), dThick(thick), dDensity(dense), dDepth(depth), dFov(fov), dPathrand(path), dSpeed(speed), dTexture(tex), dSmooth(smooth), dFog(fog), dWidescreen(wide)
{
	_effect = new LatticeWidget( NULL, 0, dLongitude, dLatitude, dThick, dDensity, dDepth, dFov, dPathrand, dSpeed, dTexture, dSmooth, dFog, dWidescreen);
	embed( _effect );
	_effect->show();
}


KLatticeScreenSaver::KLatticeScreenSaver( WId id, int preset )
 : KScreenSaver( id ), dPreset(preset)
{
	setPreset(preset);
	_effect = new LatticeWidget( NULL, 0, dLongitude, dLatitude, dThick, dDensity, dDepth, dFov, dPathrand, dSpeed, dTexture, dSmooth, dFog, dWidescreen);
	embed( _effect );
	_effect->show();
}



KLatticeScreenSaver::~KLatticeScreenSaver()
{
	delete _effect;
}


void KLatticeScreenSaver::readSettings()
{
	KConfig* config = KGlobal::config();
	config->setGroup("Settings");
	
	dPreset = config->readNumEntry( "Preset", 0 );
	if (dPreset != 1)
		setPreset(dPreset);
	else  // Custom
	{
		dLongitude = config->readNumEntry( "Longitude", 18 );
		dLatitude = config->readNumEntry( "Latitude", 6 );
		dThick = config->readNumEntry( "Thick", 50 );
		dDensity = config->readNumEntry( "Density", 50 );
		dDepth = config->readNumEntry( "Depth", 4 );
		dFov = config->readNumEntry( "Fov", 90 );
		dPathrand = config->readNumEntry( "Pathrand", 7 );
		dSpeed = config->readNumEntry( "Speed", 10 );
		dTexture = config->readNumEntry( "Texture", 9 );
		dSmooth = config->readBoolEntry( "Smooth", true );
		dFog = config->readBoolEntry( "Fog", true );
		dWidescreen = config->readBoolEntry( "Widescreen", false );
	}
}



void KLatticeScreenSaver::setPreset (int which)
{
	dWidescreen = FALSE;
	switch (which) {
	case 0: // Random
	{
		struct timeval now;
		gettimeofday (&now, NULL);
		srand((now.tv_sec & 65535) * 1000 + now.tv_usec / 1000);
		setPreset(myRandi (6) + 2);
		break;
	}
	case 2:	/* Regular */
		dLongitude = 16;
		dLatitude = 8;
		dThick = 50;
		dDensity = 50;
		dDepth = 4;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 0;
		dSmooth = FALSE;
		dFog = TRUE;
		break;
	case 3:	/* Chainmail */
		dLongitude = 24;
		dLatitude = 12;
		dThick = 50;
		dDensity = 80;
		dDepth = 3;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 3;
		dSmooth = TRUE;
		dFog = TRUE;
		break;
	case 4:	/* Brass Mesh */
		dLongitude = 4;
		dLatitude = 4;
		dThick = 40;
		dDensity = 50;
		dDepth = 4;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 4;
		dSmooth = FALSE;
		dFog = TRUE;
		break;
	case 5:	/* Computer */
		dLongitude = 4;
		dLatitude = 6;
		dThick = 70;
		dDensity = 90;
		dDepth = 4;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 7;
		dSmooth = FALSE;
		dFog = TRUE;
		break;
	case 6:	/* Slick */
		dLongitude = 24;
		dLatitude = 12;
		dThick = 100;
		dDensity = 30;
		dDepth = 4;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 5;
		dSmooth = TRUE;
		dFog = TRUE;
		break;
	case 7:	/* Tasty */
		dLongitude = 24;
		dLatitude = 12;
		dThick = 100;
		dDensity = 25;
		dDepth = 4;
		dFov = 90;
		dPathrand = 7;
		dSpeed = 10;
		dTexture = 8;
		dSmooth = TRUE;
		dFog = TRUE;
		break;
	}
}


//----------------------------------------------------------------------------


void camera::init(float* mat, float f)
{
	float temp;

	// far clipping plane
	farplane = f;

	// bottom and planes' vectors
	temp = atanf(1.0f / mat[5]);
	cullVec[0][0] = 0.0f;
	cullVec[0][1] = cosf(temp);
	cullVec[0][2] = -sinf(temp);
	cullVec[1][0] = 0.0f;
	cullVec[1][1] = -cullVec[0][1];
	cullVec[1][2] = cullVec[0][2];

	// left and right planes' vectors
	temp = atanf(1.0f / mat[0]);
	cullVec[2][0] = cosf(temp);
	cullVec[2][1] = 0.0f;
	cullVec[2][2] = -sinf(temp);
	cullVec[3][0] = -cullVec[2][0];
	cullVec[3][1] = 0.0f;
	cullVec[3][2] = cullVec[2][2];
}


bool camera::inViewVolume(float* pos, float radius)
{
	// check back plane
	if(pos[2] < -(farplane + radius))
		return 0;
	else
		// check bottom plane
		if(pos[0]*cullVec[0][0] + pos[1]*cullVec[0][1] + pos[2]*cullVec[0][2] < -radius)
			return 0;
		else
			// check top plane
			if(pos[0]*cullVec[1][0] + pos[1]*cullVec[1][1] + pos[2]*cullVec[1][2] < -radius)
				return 0;
			else
				// check left plane
				if(pos[0]*cullVec[2][0] + pos[1]*cullVec[2][1] + pos[2]*cullVec[2][2] < -radius)
					return 0;
				else
					// check right plane
					if(pos[0]*cullVec[3][0] + pos[1]*cullVec[3][1] + pos[2]*cullVec[3][2] < -radius)
						return 0;

	return 1;
}



int transitions[20][6] = {
		1, 2, 12, 4, 14, 8,
		0, 3, 15, 7, 7, 7,
		3, 4, 14, 0, 7, 16,
		2, 1, 15, 7, 7, 7,
		5, 10, 12, 17, 17, 17,
		4, 3, 13, 11, 9, 17,
		12, 4, 10, 17, 17, 17,
		2, 0, 14, 8, 16, 19,
		1, 3, 15, 7, 7, 7,
		4, 10, 12, 17, 17, 17,
		11, 4, 12, 17, 17, 17,
		10, 5, 15, 13, 17, 18,
		13, 10, 4, 17, 17, 17,
		12, 1, 11, 5, 6, 17,
		15, 2, 12, 0, 7, 19,
		14, 3, 1, 7, 7, 7,
		3, 1, 15, 7, 7, 7,
		5, 11, 13, 6, 9, 18,
		10, 4, 12, 17, 17, 17,
		15, 1, 3, 7, 7, 7 };


LatticeWidget::LatticeWidget( QWidget* parent, const char* name, int lon, int lat, int thick, int dense, int depth, int fov, int path, int speed, int tex, bool smooth, bool fog, bool wide )
 : QGLWidget(parent, name), elapsedTime(0.0f),
   dLongitude(lon), dLatitude(lat), dThick(thick), dDensity(dense), dDepth(depth), dFov(fov), dPathrand(path), dSpeed(speed), dTexture(tex), dSmooth(smooth), dFog(fog), dWidescreen(wide)
{
	loadLatticeData();
	_timer = new QTimer( this );
	_frameTime = 1000 / 60;
	connect( _timer, SIGNAL(timeout()), this, SLOT(nextFrame()) );
}


LatticeWidget::~LatticeWidget()
{
}


// Modulus function for picking the correct element of lattice array
int LatticeWidget::myMod(int x){
	while(x < 0)
		x += LATSIZE;
	return(x % LATSIZE);
}


// start point, start slope, end point, end slope, position (0.0 - 1.0)
// returns point somewhere along a smooth curve between the start point
// and end point
float LatticeWidget::interpolate(float a, float b, float c, float d, float where)
{
	float q = 2.0f * (a - c) + b + d;
	float r = 3.0f * (c - a) - 2.0f * b - d;
	return((where * where * where * q) + (where * where * r) + (where * b) + a);
}


void LatticeWidget::initTextures()
{
	if(dTexture == 1)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB,
			GL_UNSIGNED_BYTE, indtex1);

		glBindTexture(GL_TEXTURE_2D, 2);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB,
			GL_UNSIGNED_BYTE, indtex2);
	}
	if(dTexture == 2)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB,
			GL_UNSIGNED_BYTE, crystex);
	}
	if(dTexture == 3)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB,
			GL_UNSIGNED_BYTE, chrometex);
	}
	if(dTexture == 4)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB,
			GL_UNSIGNED_BYTE, brasstex);
	}
	if(dTexture == 5)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 4, TEXSIZE, TEXSIZE, GL_RGBA,
			GL_UNSIGNED_BYTE, shinytex);
	}
	if(dTexture == 6)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, TEXSIZE, TEXSIZE, GL_ALPHA, 
			GL_UNSIGNED_BYTE, ghostlytex);
	}
	if(dTexture == 7)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, TEXSIZE, TEXSIZE, GL_ALPHA, 
			GL_UNSIGNED_BYTE, circuittex);
	}
	if(dTexture == 8)
	{
		glBindTexture(GL_TEXTURE_2D, 1);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 4, TEXSIZE, TEXSIZE, GL_RGBA,
			GL_UNSIGNED_BYTE, doughtex);
	}
}


//  Build the lattice display lists
void LatticeWidget::makeLatticeObjects()
{
	int i, d = 0;
	float thick = float(dThick) * 0.001f;

	if(dTexture > 0 && dTexture < 8)
		glColor3f(1.0f, 1.0f, 1.0f);

	for(i=1; i<=NUMOBJECTS; i++)
	{
		glNewList(i, GL_COMPILE);
			if(dTexture >= 2)
				glBindTexture(GL_TEXTURE_2D, 1);
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(-0.25f, -0.25f, -0.25f);
					if(myRandi(2))
						glRotatef(180.0f, 1, 0, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
			d = (d + 37) % 100;
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(0.25f, -0.25f, -0.25f);
					if(myRandi(2))
						glRotatef(90.0f, 1, 0, 0);
					else
						glRotatef(-90.0f, 1, 0, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
			d = (d + 37) % 100;
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(0.25f, -0.25f, 0.25f);
					if(myRandi(2))
						glRotatef(90.0f, 0, 1, 0);
					else
						glRotatef(-90.0f, 0, 1, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
			d = (d + 37) % 100;
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(0.25f, 0.25f, 0.25f);
					if(myRandi(2))
						glRotatef(180.0f, 1, 0, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
			d = (d + 37) % 100;
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(-0.25f, 0.25f, 0.25f);
					if(myRandi(2))
						glRotatef(90.0f, 1, 0, 0);
					else
						glRotatef(-90.0f, 1, 0, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
			d = (d + 37) % 100;
			if(d < dDensity)
			{
				glPushMatrix();
					if(dTexture == 0 || dTexture >= 5)
						glColor3f(myRandf(1.0f), myRandf(1.0f), myRandf(1.0f));
					if(dTexture == 1)
						glBindTexture(GL_TEXTURE_2D, myRandi(2)+1);
					glTranslatef(-0.25f, 0.25f, -0.25f);
					if(myRandi(2))
						glRotatef(90.0f, 0, 1, 0);
					else
						glRotatef(-90.0f, 0, 1, 0);
					makeTorus(dSmooth, dLongitude, dLatitude, 0.36f - thick, thick);
				glPopMatrix();
			}
		glEndList();
		d = (d + 37) % 100;
	}
}

void LatticeWidget::makeTorus(int smooth, int longitude, int latitude, float centerradius, float thickradius)
{
	int i, j;
	float r, rr;  // Radius
	float z, zz;  // Depth
	float cosa, sina;  // Longitudinal positions
	float cosn, cosnn, sinn, sinnn;  // Normals for shading
	float ncosa, nsina;  // Longitudinal positions for shading
	float u, v1, v2, ustep, vstep;
	float temp;
	float oldcosa = 0.0f;
	float oldsina = 0.0f;
	float oldncosa = 0.0f;
	float oldnsina = 0.0f;
	float oldcosn = 0.0f;
	float oldcosnn = 0.0f;
	float oldsinn = 0.0f;
	float oldsinnn = 0.0f;

	// Smooth shading?
	if(smooth)
		glShadeModel(GL_SMOOTH);
	else
		glShadeModel(GL_FLAT);

	// Initialize texture stuff
	vstep = 1.0f / float(latitude);
	ustep = float(int((centerradius / thickradius) + 0.5f)) / float(longitude);
	v2 = 0.0f;

	for(i=0; i<latitude; i++)
	{
		temp = PIx2 * float(i) / float(latitude);
		cosn = cosf(temp);
		sinn = sinf(temp);
		temp = PIx2 * float(i+1) / float(latitude);
		cosnn = cosf(temp);
		sinnn = sinf(temp);
		r = centerradius + thickradius * cosn;
		rr = centerradius + thickradius * cosnn;
		z = thickradius * sinn;
		zz = thickradius * sinnn;
		if(!smooth)  // Redefine normals for flat shaded model
		{
			temp = PIx2 * (float(i) + 0.5f) / float(latitude);
			cosn = cosnn = cosf(temp);
			sinn = sinnn = sinf(temp);
		}
		v1 = v2;
		v2 += vstep;
		u = 0.0f;
		glBegin(GL_TRIANGLE_STRIP);
			for(j=0; j<longitude; j++)
			{
				temp = PIx2 * float(j) / float(longitude);
				cosa = cosf(temp);
				sina = sinf(temp);
				if(smooth)
				{
					ncosa = cosa;
					nsina = sina;
				}
				else  // Redefine longitudinal component of normal for flat shading
				{
					temp = PIx2 * (float(j) - 0.5f) / float(longitude);
					ncosa = cosf(temp);
					nsina = sinf(temp);
				}
				if(j==0)  // Save first values for end of circular tri-strip
				{
					oldcosa = cosa;
					oldsina = sina;
					oldncosa = ncosa;
					oldnsina = nsina;
					oldcosn = cosn;
					oldcosnn = cosnn;
					oldsinn = sinn;
					oldsinnn = sinnn;
				}
				glNormal3f(cosnn * ncosa, cosnn * nsina, sinnn);
				glTexCoord2f(u, v2);
				glVertex3f(cosa * rr, sina * rr, zz);
				glNormal3f(cosn * ncosa, cosn * nsina, sinn);
				glTexCoord2f(u, v1);
				glVertex3f(cosa * r, sina * r, z);
				u += ustep;  // update u texture coordinate
			}
			//  Finish off circular tri-strip with saved first values
			glNormal3f(oldcosnn * oldncosa, oldcosnn * oldnsina, oldsinnn);
			glTexCoord2f(u, v2);
			glVertex3f(oldcosa * rr, oldsina * rr, zz);
			glNormal3f(oldcosn * oldncosa, oldcosn * oldnsina, oldsinn);
			glTexCoord2f(u, v1);
			glVertex3f(oldcosa * r, oldsina * r, z);
		glEnd();
	}
}


void LatticeWidget::paintGL()
{
	int i, j, k;
	// Time from one frame to the next
	//static float elapsedTime = 0.0f;
	//static DWORD thisTime = timeGetTime(), lastTime;
	int indexx, indexy, indexz;
	rsVec xyz, dir, angvel, tempVec;
	static rsVec oldxyz(0.0f, 0.0f, 0.0f);
	static rsVec oldDir(0.0f, 0.0f, -1.0f);
	static rsVec oldAngvel(0.0f, 0.0f, 0.0f);
	float angle, distance;
	static float rotationInertia = 0.007f * float(dSpeed);
	static float maxSpin = 0.0025f * float(dSpeed);
	float rotMat[16];
	rsQuat newQuat;
	static rsQuat quat;
	static int flymode = 1;
	static float flymodeChange = 20.0f;
	static int seg = 0;  // Section of path
	static float where = 0.0f;  // Position on path
	static float rollVel = 0.0f, rollAcc = 0.0f;
	rsQuat cullQuat;
	float cullMat[16];
	float transMat[16];
	rsVec cull[5];  // storage for transformed culling vectors
	static int drawDepth = dDepth + 2;

	where += float(dSpeed) * 0.05f * elapsedTime;
	if(where >= 1.0f)
	{
		where -= 1.0f;
		seg++;
	}
	if(seg >= segments)
	{
		seg = 0;
		reconfigure();
	}

	// Calculate position
	xyz[0] = interpolate(path[seg][0], path[seg][3], path[seg+1][0],
		path[seg+1][3], where);
	xyz[1] = interpolate(path[seg][1], path[seg][4], path[seg+1][1],
		path[seg+1][4], where);
	xyz[2] = interpolate(path[seg][2], path[seg][5], path[seg+1][2],
		path[seg+1][5], where);

	// Do rotation stuff
	dir = xyz - oldxyz;  // Direction of motion
	dir.normalize();
	angvel.cross(dir, oldDir);  // Desired axis of rotation
	angle = acosf(oldDir.dot(dir));  // Desired turn angle
	if(angle > maxSpin)  // Cap the spin
		angle = maxSpin;
	if(angle < -maxSpin)
		angle = -maxSpin;
	angvel.scale(angle);  // Desired angular velocity
	tempVec = angvel - oldAngvel;  // Change in angular velocity
	distance = tempVec.length();  // Don't let angular velocity change too much
	if(distance > rotationInertia * elapsedTime)
	{
		tempVec.scale((rotationInertia * elapsedTime) / distance);
		angvel = oldAngvel + tempVec;
	}
	flymodeChange -= elapsedTime;
	if(flymodeChange <= 1.0f)  // prepare to transition
		angvel.scale(flymodeChange);
	if(flymodeChange <= 0.0f)  // transition from one fly mode to the other?
	{
		flymode = myRandi(4);
		flymodeChange = myRandf(float(150 - dSpeed)) + 5.0f;
	}
	tempVec = angvel;  // Recompute desired rotation
	angle = tempVec.normalize();
	newQuat.make(angle, tempVec[0], tempVec[1], tempVec[2]);  // Use rotation	
	if(flymode)  // fly normal (straight)
		quat.preMult(newQuat);
	else  // don't fly normal (go backwards and stuff)
		quat.postMult(newQuat);

	// Roll
	static float rollChange = myRandf(10.0f) + 2.0f;
	rollChange -= elapsedTime;
	if(rollChange <= 0.0f)
	{
		rollAcc = myRandf(0.02f * float(dSpeed)) - (0.01f * float(dSpeed));
		rollChange = myRandf(10.0f) + 2.0f;
	}
	rollVel += rollAcc * elapsedTime;
	if(rollVel > (0.04f * float(dSpeed)) && rollAcc > 0.0f)
		rollAcc = 0.0f;
	if(rollVel < (-0.04f * float(dSpeed)) && rollAcc < 0.0f)
		rollAcc = 0.0f;
	newQuat.make(rollVel * elapsedTime, oldDir[0], oldDir[1], oldDir[2]);
	quat.preMult(newQuat);

	quat.toMat(rotMat);

	// Save old stuff
	oldxyz = xyz;
	oldDir[0] = -rotMat[2];
	oldDir[1] = -rotMat[6];
	oldDir[2] = -rotMat[10];
	oldAngvel = angvel;

	// Apply transformations
	glLoadMatrixf(rotMat);
	//glLoadIdentity();
	glGetFloatv(GL_MODELVIEW, cullMat);
	glTranslatef(-xyz[0], -xyz[1], -xyz[2]);

	glGetFloatv(GL_MODELVIEW, transMat);

	// Render everything
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	for(i=globalxyz[0]-drawDepth; i<=globalxyz[0]+drawDepth; i++)
	{
		for(j=globalxyz[1]-drawDepth; j<=globalxyz[1]+drawDepth; j++)
		{
			for(k=globalxyz[2]-drawDepth; k<=globalxyz[2]+drawDepth; k++)
			{
				tempVec[0] = float(i) - xyz[0];
				tempVec[1] = float(j) - xyz[1];
				tempVec[2] = float(k) - xyz[2];
				float tpos[3];  // transformed position
				tpos[0] = tempVec[0]*rotMat[0] + tempVec[1]*rotMat[4] + tempVec[2]*rotMat[8];
				tpos[1] = tempVec[0]*rotMat[1] + tempVec[1]*rotMat[5] + tempVec[2]*rotMat[9];
				tpos[2] = tempVec[0]*rotMat[2] + tempVec[1]*rotMat[6] + tempVec[2]*rotMat[10];
				if(theCamera->inViewVolume(tpos, 0.9f)){
					indexx = myMod(i);
					indexy = myMod(j);
					indexz = myMod(k);
					// draw it
					glPushMatrix();
						glTranslatef(float(i), float(j), float(k));
						glCallList(lattice[indexx][indexy][indexz]);
					glPopMatrix();
				}
			}
		}
	}

	glFlush();
}


void LatticeWidget::reconfigure()
{
	int i, j;
	int newBorder, positive;

	// End of old path = start of new path
	for(i=0; i<6; i++)
		path[0][i] = path[segments][i];

	// determine if direction of motion is positive or negative
	// update global position
	if(lastBorder < 6)
	{
		if((path[0][3] + path[0][4] + path[0][5]) > 0.0f)
		{
			positive = 1;
			globalxyz[lastBorder / 2] ++;
		}
		else
		{
			positive = 0;
			globalxyz[lastBorder / 2] --;
		}
	}
	else
	{
		if(path[0][3] > 0.0f)
		{
			positive = 1;
			globalxyz[0] ++;
		}
		else
		{
			positive = 0;
			globalxyz[0] --;
		}
		if(path[0][4] > 0.0f)
			globalxyz[1] ++;
		else
			globalxyz[1] --;
		if(path[0][5] > 0.0f)
			globalxyz[2] ++;
		else
			globalxyz[2] --;
	}

	if(!myRandi(11 - dPathrand))  // Change directions
	{
		if(!positive)
			lastBorder += 10;
		newBorder = transitions[lastBorder][myRandi(6)];
		positive = 0;
		if(newBorder < 10)
			positive = 1;
		else
			newBorder -= 10;
		for(i=0; i<6; i++)  // set the new border point
			path[1][i] = bPnt[newBorder][i];
		if(!positive)  // flip everything if direction is negative
		{
			if(newBorder < 6)
				path[1][newBorder/2] *= -1.0f;
			else
				for(i=0; i<3; i++)
					path[1][i] *= -1.0f;
			for(i=3; i<6; i++)	
				path[1][i] *= -1.0f;
		}
		for(i=0; i<3; i++)  // reposition the new border
			path[1][i] += globalxyz[i];
		lastBorder = newBorder;
		segments = 1;
	}
	else  // Just keep going straight
	{
		newBorder = lastBorder;
		for(i=0; i<6; i++)
			path[1][i] = bPnt[newBorder][i];
		i = newBorder / 2;
		if(!positive)
		{
			if(newBorder < 6)
				path[1][i] *= -1.0f;
			else{
				path[1][0] *= -1.0f;
				path[1][1] *= -1.0f;
				path[1][2] *= -1.0f;
			}
			path[1][3] *= -1.0f;
			path[1][4] *= -1.0f;
			path[1][5] *= -1.0f;
		}
		for(j=0; j<3; j++)
		{
			path[1][j] += globalxyz[j];
			if((newBorder < 6) && (j != 1))
				path[1][j] += myRandf(0.15f) - 0.075f;
		}
		if(newBorder >= 6)
			path[1][0] += myRandf(0.1f) - 0.05f;
		segments = 1;
	}
}


void LatticeWidget::resizeGL( int w, int h )
{
	int i, j, k;
	
	
	if(dWidescreen)
		glViewport(0, h/2 - w/4, w, w/2);
	else
		glViewport(0, 0, w, h);

	if(dTexture == 9)  // Choose random texture
		dTexture = myRandi(9);

	if(dTexture != 2 && dTexture != 6)  // No z-buffering for crystal or ghostly
		glEnable(GL_DEPTH_TEST);
	glFrontFace(GL_CCW);
	glEnable(GL_CULL_FACE);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	float mat[16] = {cosf(float(dFov) * 0.5f * D2R) / sinf(float(dFov) * 0.5f * D2R), 0.0f, 0.0f, 0.0f,
		0.0f, 0.0, 0.0f, 0.0f,
		0.0f, 0.0f, -1.0f - 0.02f / float(dDepth), -1.0f,
		0.0f, 0.0f, -(0.02f + 0.0002f / float(dDepth)), 0.0f};
	if(dWidescreen)
		mat[5] = mat[0] * 2.0f;
	else
		mat[5] = mat[0] * (float(w) / float(h));
	glLoadMatrixf(mat);
	theCamera = new camera;
	theCamera->init(mat, float(dDepth));
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	if(dTexture != 3 && dTexture != 4 && dTexture != 6)  // No lighting for chrome, brass, or ghostly
	{
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
		float ambient[4] = {0.0f, 0.0f, 0.0f, 0.0f};
		float diffuse[4] = {1.0f, 1.0f, 1.0f, 0.0f};
		float specular[4] = {1.0f, 1.0f, 1.0f, 0.0f};
		float position[4] = {400.0f, 300.0f, 500.0f, 0.0f};
		//float position[4] = {0.0f, 0.0f, 0.0f, 1.0f};
		glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
		glLightfv(GL_LIGHT0, GL_POSITION, position);
	}
	glEnable(GL_COLOR_MATERIAL);
	if(dTexture == 0 || dTexture == 5 || dTexture >= 7)
	{
		glMaterialf(GL_FRONT, GL_SHININESS, 50.0f);
		glColorMaterial(GL_FRONT, GL_SPECULAR);
	}
	if(dTexture == 2)
	{
		glMaterialf(GL_FRONT, GL_SHININESS, 10.0f);
		glColorMaterial(GL_FRONT, GL_SPECULAR);
	}
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	if(dFog)
	{
		glEnable(GL_FOG);
		float fog_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
		glFogfv(GL_FOG_COLOR, fog_color);
		glFogf(GL_FOG_MODE, GL_LINEAR);
		glFogf(GL_FOG_START, float(dDepth) * 0.3f);
		glFogf(GL_FOG_END, float(dDepth) - 0.1f);
	}

	if(dTexture == 2 || dTexture == 6)  // Use blending for crystal and ghostly
	{
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		glEnable(GL_BLEND);
	}

	if(dTexture == 7)  // Use blending for circuits
	{
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	}

	if(dTexture)
	{
		glEnable(GL_TEXTURE_2D);
		initTextures();
		// Environment mapping for crystal, chrome, brass, shiny, and ghostly
		if(dTexture == 2 || dTexture == 3 || dTexture == 4  || dTexture == 5 || dTexture == 6)
		{
			glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
			glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
		}
	}

	// Initialize lattice objects and their positions in the lattice array
	makeLatticeObjects();
	for(i=0; i<LATSIZE; i++)
	{
		for(j=0; j<LATSIZE; j++)
		{
			for(k=0; k<LATSIZE; k++)
			{
				lattice[i][j][k] = myRandi(NUMOBJECTS) + 1;
			}
		}
	}

	// Initialize border points
	// horizontal border points
	for(i=0; i<6; i++)
	{
		for(j=0; j<6; j++)
		{
			bPnt[i][j] = 0.0f;
		}
	}
	bPnt[0][0] = 0.5f;    bPnt[0][1] = -0.25f;  bPnt[0][2] = 0.25f;  bPnt[0][3] = 1.0f; // right
	bPnt[1][0] = 0.5f;    bPnt[1][1] = 0.25f;   bPnt[1][2] = -0.25f; bPnt[1][3] = 1.0f; // right
	bPnt[2][0] = -0.25f;  bPnt[2][1] = 0.5f;    bPnt[2][2] = 0.25f;  bPnt[2][4] = 1.0f; // top
	bPnt[3][0] = 0.25f;   bPnt[3][1] = 0.5f;    bPnt[3][2] = -0.25f; bPnt[3][4] = 1.0f; // top
	bPnt[4][0] = -0.25f;  bPnt[4][1] = -0.25f;  bPnt[4][2] = 0.5f;   bPnt[4][5] = 1.0f; // front
	bPnt[5][0] = 0.25f;   bPnt[5][1] = 0.25f;   bPnt[5][2] = 0.5f;   bPnt[5][5] = 1.0f; // front
	// diagonal border points
	bPnt[6][0] = 0.5f;    bPnt[6][1] = -0.5f;   bPnt[6][2] = -0.5f;  bPnt[6][3] = 1.0f; bPnt[6][4] = -1.0f; bPnt[6][5] = -1.0f;
	bPnt[7][0] = 0.5f;    bPnt[7][1] = 0.5f;    bPnt[7][2] = -0.5f;  bPnt[7][3] = 1.0f; bPnt[7][4] = 1.0f;  bPnt[7][5] = -1.0f;
	bPnt[8][0] = 0.5f;    bPnt[8][1] = -0.5f;   bPnt[8][2] = 0.5f;   bPnt[8][3] = 1.0f; bPnt[8][4] = -1.0f; bPnt[8][5] = 1.0f;
	bPnt[9][0] = 0.5f;    bPnt[9][1] = 0.5f;    bPnt[9][2] = 0.5f;   bPnt[9][3] = 1.0f; bPnt[9][4] = 1.0f;  bPnt[9][5] = 1.0f;

	globalxyz[0] = 0;
	globalxyz[1] = 0;
	globalxyz[2] = 0;

	// Set up first path section
	path[0][0] = 0.0f;
	path[0][1] = 0.0f;
	path[0][2] = 0.0f;
	path[0][3] = 0.0f;
	path[0][4] = 0.0f;
	path[0][5] = 0.0f;
	j = myRandi(12);
	k = j % 6;
	for(i=0; i<6; i++)
		path[1][i] = bPnt[k][i];
	if(j > 5)  // If we want to head in a negative direction
	{
		i = k / 2;  // then we need to flip along the appropriate axis
		path[1][i] *= -1.0f;
		path[1][i+3] *= -1.0f;
	}
	lastBorder = k;
	segments = 1;
}


// Window initialization
void LatticeWidget::initializeGL()
{
	struct timeval now;
	gettimeofday (&now, NULL);
	lastTime = (now.tv_sec & 65535) * 1000 + now.tv_usec / 1000;
	srand(lastTime);
	
	_timer->start( _frameTime, true );
}


void LatticeWidget::nextFrame()
{
	unsigned int thisTime;
	static float times[10] = { 0.03f, 0.03f, 0.03f, 0.03f, 0.03f,
		0.03f, 0.03f, 0.03f, 0.03f, 0.03f
	};
	static int timeindex = 0;
	struct timeval now;

	/*
	 * update time 
	 */
	gettimeofday (&now, NULL);
	thisTime = (now.tv_sec & 65535) * 1000 + now.tv_usec / 1000;

	if (thisTime >= lastTime)
		times[timeindex] = (thisTime - lastTime) * 0.001f;
	else
		times[timeindex] = elapsedTime;

	lastTime = thisTime;

	elapsedTime = 0.1f * (times[0] + times[1] + times[2] + times[3] + times[4] + times[5] + times[6] + times[7] + times[8] + times[9]);

	timeindex++;
	if (timeindex >= 10)
		timeindex = 0;
	
	updateGL();
	_timer->start( _frameTime, true );
}


//----------------------------------------------------------------------------


KLatticeSetup::KLatticeSetup( QWidget* parent, const char* name )
        : KDialogBase( parent, name, true, i18n("Setup Lattice Screen Saver"),
          Ok|Cancel|Help|Default|Apply, Ok, true )
{
	setButtonText( Help, i18n( "A&bout" ) );
	
	QWidget *main = makeMainWidget();
	
	QHBoxLayout* top    = new QHBoxLayout(main, 0, spacingHint());
	
	setupWidget = new LatticeSetup(main);
	slotDefault();
	top->addWidget(setupWidget);
	
	// Preview
	preview = new QWidget( main );
	preview->setFixedSize( 220, 170 );
	preview->setBackgroundColor( black );
	preview->show();    // otherwise saver does not get correct size
	_saver = new KLatticeScreenSaver( preview->winId() );
	top->addWidget(preview);
	
	// Now that we have _saver...
	setupWidget->preset->setCurrentItem( _saver->preset() );
	presetChanged();
	if (_saver->preset() == 1)  // Custom
	{
		setupWidget->longitude->setValue( _saver->longitude() );
		setupWidget->latitude->setValue( _saver->latitude() );
		setupWidget->texture->setCurrentItem( _saver->texture() );
		setupWidget->thick->setValue( _saver->thick() );
		setupWidget->density->setValue( _saver->density() );
		setupWidget->depth->setValue( _saver->depth() );
		setupWidget->fov->setValue( _saver->fov() );
		setupWidget->pathrand->setValue( _saver->pathrand() );
		setupWidget->speed->setValue( _saver->speed() );
		setupWidget->smooth->setChecked( _saver->smooth() );
		setupWidget->fog->setChecked( _saver->fog() );
		setupWidget->widescreen->setChecked( _saver->widescreen() );
	}
	else if (_saver->preset() > 1)
		setPreset(_saver->preset());
		
	
	setMinimumSize( sizeHint() );
	
	timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), SLOT(updatePreview()));
	
	connect(setupWidget->preset, SIGNAL(activated(int)), SLOT(presetChanged()));
	connect(setupWidget->preset, SIGNAL(activated(int)), SLOT(resetTimer()));
	connect(setupWidget->longitude, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->latitude, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->texture, SIGNAL(activated(int)), SLOT(resetTimer()));
	connect(setupWidget->thick, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->density, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->depth, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->fov, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->pathrand, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->speed, SIGNAL(valueChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->smooth, SIGNAL(stateChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->fog, SIGNAL(stateChanged(int)), SLOT(resetTimer()));
	connect(setupWidget->widescreen, SIGNAL(stateChanged(int)), SLOT(resetTimer()));
}


KLatticeSetup::~KLatticeSetup()
{
	delete _saver;
}


void KLatticeSetup::presetChanged()
{
	setupWidget->customBox->setEnabled(setupWidget->preset->currentItem() == 1);
	setPreset(setupWidget->preset->currentItem());
}

void KLatticeSetup::slotHelp()
{
	KMessageBox::about(this, 
		i18n("<h3>Lattice 1.0</h3>\n<p>Copyright (c) 2004 Terence M. Welsh<br>\n<a href=\"http://www.reallyslick.com/\">http://www.reallyslick.com/</a></p>\n\n<p>Ported to KDE by David Sansome</p>"),
		QString::null, KMessageBox::AllowLink);
}



void KLatticeSetup::slotOk()
{
	saveSettings();
	accept();
}

void KLatticeSetup::slotApply()
{
	saveSettings();
	updatePreview();
}

void KLatticeSetup::saveSettings()
{
	KConfig* config = KGlobal::config();
	config->setGroup("Settings");
	
	config->writeEntry("Preset", setupWidget->preset->currentItem());
	
	if (setupWidget->preset->currentItem() == 1)
	{
		config->writeEntry("Longitude", setupWidget->longitude->value() );
		config->writeEntry("Latitude", setupWidget->latitude->value() );
		config->writeEntry("Thick", setupWidget->thick->value() );
		config->writeEntry("Density", setupWidget->density->value() );
		config->writeEntry("Depth", setupWidget->depth->value() );
		config->writeEntry("Fov", setupWidget->fov->value() );
		config->writeEntry("Pathrand", setupWidget->pathrand->value() );
		config->writeEntry("Speed", setupWidget->speed->value() );
		config->writeEntry("Texture", setupWidget->texture->currentItem() );
		config->writeEntry("Smooth", setupWidget->smooth->isChecked() );
		config->writeEntry("Fog", setupWidget->fog->isChecked() );
		config->writeEntry("Widescreen", setupWidget->widescreen->isChecked() );
	}
	
	config->sync();
}

void KLatticeSetup::updatePreview()
{
	delete _saver;
	if (setupWidget->preset->currentItem() != 1)
		_saver = new KLatticeScreenSaver( preview->winId(), setupWidget->preset->currentItem());
	else
	{
		_saver = new KLatticeScreenSaver( preview->winId(), setupWidget->longitude->value(),
								    setupWidget->latitude->value(),
								    setupWidget->thick->value(),
								    setupWidget->density->value(),
								    setupWidget->depth->value(),
								    setupWidget->fov->value(),
								    setupWidget->pathrand->value(),
								    setupWidget->speed->value(),
								    setupWidget->texture->currentItem(),
								    setupWidget->smooth->isChecked(),
								    setupWidget->fog->isChecked(),
								    setupWidget->widescreen->isChecked());
	}
}

void KLatticeSetup::resetTimer()
{
	timer->start(750, true);
}

void KLatticeSetup::slotDefault()
{
	setupWidget->preset->setCurrentItem( 0 );
	setupWidget->longitude->setValue( 16 );
	setupWidget->latitude->setValue( 8 );
	setupWidget->thick->setValue( 50 );
	setupWidget->density->setValue( 50 );
	setupWidget->depth->setValue( 4 );
	setupWidget->fov->setValue( 90 );
	setupWidget->pathrand->setValue( 7 );
	setupWidget->speed->setValue( 7 );
	setupWidget->texture->setCurrentItem( 9 );
	setupWidget->smooth->setChecked( true );
	setupWidget->fog->setChecked( true );
	setupWidget->widescreen->setChecked( false );
	presetChanged();
}

void KLatticeSetup::setPreset (int which)
{
	setupWidget->widescreen->setChecked(FALSE);
	switch (which) {
	case 0: // Random
	case 1: // Custom
		break;
	case 2:	/* Regular */
		setupWidget->longitude->setValue(16);
		setupWidget->latitude->setValue(8);
		setupWidget->thick->setValue(50);
		setupWidget->density->setValue(50);
		setupWidget->depth->setValue(4);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(0);
		setupWidget->smooth->setChecked(FALSE);
		setupWidget->fog->setChecked(TRUE);
		break;
	case 3:	/* Chainmail */
		setupWidget->longitude->setValue(24);
		setupWidget->latitude->setValue(12);
		setupWidget->thick->setValue(50);
		setupWidget->density->setValue(80);
		setupWidget->depth->setValue(3);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(3);
		setupWidget->smooth->setChecked(TRUE);
		setupWidget->fog->setChecked(TRUE);
		break;
	case 4:	/* Brass Mesh */
		setupWidget->longitude->setValue(4);
		setupWidget->latitude->setValue(4);
		setupWidget->thick->setValue(40);
		setupWidget->density->setValue(50);
		setupWidget->depth->setValue(4);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(4);
		setupWidget->smooth->setChecked(FALSE);
		setupWidget->fog->setChecked(TRUE);
		break;
	case 5:	/* Computer */
		setupWidget->longitude->setValue(4);
		setupWidget->latitude->setValue(6);
		setupWidget->thick->setValue(70);
		setupWidget->density->setValue(90);
		setupWidget->depth->setValue(4);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(7);
		setupWidget->smooth->setChecked(FALSE);
		setupWidget->fog->setChecked(TRUE);
		break;
	case 6:	/* Slick */
		setupWidget->longitude->setValue(24);
		setupWidget->latitude->setValue(12);
		setupWidget->thick->setValue(100);
		setupWidget->density->setValue(30);
		setupWidget->depth->setValue(4);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(5);
		setupWidget->smooth->setChecked(TRUE);
		setupWidget->fog->setChecked(TRUE);
		break;
	case 7:	/* Tasty */
		setupWidget->longitude->setValue(24);
		setupWidget->latitude->setValue(12);
		setupWidget->thick->setValue(100);
		setupWidget->density->setValue(25);
		setupWidget->depth->setValue(4);
		setupWidget->fov->setValue(90);
		setupWidget->pathrand->setValue(7);
		setupWidget->speed->setValue(10);
		setupWidget->texture->setCurrentItem(8);
		setupWidget->smooth->setChecked(TRUE);
		setupWidget->fog->setChecked(TRUE);
		break;
	}
}

#include "lattice.moc"
