//============================================================================ // // Terence Welsh Screensaver - Euphoria // http://www.reallyslick.com/ // // Ported to KDE by Karl Robillard // /* * Copyright (C) 2002 Terence M. Welsh * * Euphoria is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Euphoria 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //============================================================================ #include #include #include #include #include #include "Euphoria.h" #include "Euphoria.moc" #include "EuphoriaTexture.h" #define NUMCONSTS 9 #define PIx2 6.28318530718f //---------------------------------------------------------------------------- #include #include // Returns the system time, in seconds. double timeGetTime() { struct timeval tp; gettimeofday( &tp, 0 ); return (double)tp.tv_sec + (double)tp.tv_usec / 1000000; } //---------------------------------------------------------------------------- class rsVec { public: float v[3]; rsVec() {} rsVec(float xx, float yy, float zz); void set(float xx, float yy, float zz); float normalize(); float dot(rsVec); void cross(rsVec, rsVec); float & operator [] (int i) {return v[i];} const float & operator [] (int i) const {return v[i];} rsVec & operator = (const rsVec &vec) {v[0]=vec[0];v[1]=vec[1];v[2]=vec[2];return *this;}; rsVec operator + (const rsVec &vec) {return(rsVec(v[0]+vec[0], v[1]+vec[1], v[2]+vec[2]));}; rsVec operator - (const rsVec &vec) {return(rsVec(v[0]-vec[0], v[1]-vec[1], v[2]-vec[2]));}; rsVec operator * (const float &mul) {return(rsVec(v[0]*mul, v[1]*mul, v[2]*mul));}; rsVec operator / (const float &div) {float rec = 1.0f/div; return(rsVec(v[0]*rec, v[1]*rec, v[2]*rec));}; rsVec & operator += (const rsVec &vec) {v[0]+=vec[0];v[1]+=vec[1];v[2]+=vec[2];return *this;}; rsVec & operator -= (const rsVec &vec) {v[0]-=vec[0];v[1]-=vec[1];v[2]-=vec[2];return *this;}; rsVec & operator *= (const rsVec &vec) {v[0]*=vec[0];v[1]*=vec[1];v[2]*=vec[2];return *this;}; rsVec & operator *= (const float &mul) {v[0]*=mul;v[1]*=mul;v[2]*=mul;return *this;}; }; rsVec::rsVec(float xx, float yy, float zz){ v[0] = xx; v[1] = yy; v[2] = zz; } void rsVec::set(float xx, float yy, float zz){ v[0] = xx; v[1] = yy; v[2] = zz; } float rsVec::normalize(){ float length = float(sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])); if(length == 0.0f){ v[1] = 1.0f; return(0.0f); } float reciprocal = 1.0f / length; v[0] *= reciprocal; v[1] *= reciprocal; v[2] *= reciprocal; // Really freakin' stupid compiler bug fix for VC++ 5.0 /*v[0] /= length; v[1] /= length; v[2] /= length;*/ return(length); } float rsVec::dot(rsVec vec1){ return(v[0] * vec1[0] + v[1] * vec1[1] + v[2] * vec1[2]); } void rsVec::cross(rsVec vec1, rsVec vec2){ v[0] = vec1[1] * vec2[2] - vec2[1] * vec1[2]; v[1] = vec1[2] * vec2[0] - vec2[2] * vec1[0]; v[2] = vec1[0] * vec2[1] - vec2[0] * vec1[1]; } //---------------------------------------------------------------------------- void hsl2rgb(float h, float s, float l, float &r, float &g, float &b) { // hue influence if(h < 0.166667){ // full red, some green r = 1.0; g = h * 6.0f; b = 0.0; } else { if(h < 0.5){ // full green g = 1.0; if(h < 0.333333){ // some red r = 1.0f - ((h - 0.166667f) * 6.0f); b = 0.0; } else{ // some blue b = (h - 0.333333f) * 6.0f; r = 0.0; } } else{ if(h < 0.833333){ // full blue b = 1.0; if(h < 0.666667){ // some green g = 1.0f - ((h - 0.5f) * 6.0f); r = 0.0; } else{ // some red r = (h - 0.666667f) * 6.0f; g = 0.0; } } else{ // full red, some blue r = 1.0; b = 1.0f - ((h - 0.833333f) * 6.0f); g = 0.0; } } } // saturation influence r = 1.0f - (s * (1.0f - r)); g = 1.0f - (s * (1.0f - g)); b = 1.0f - (s * (1.0f - b)); // luminosity influence r *= l; g *= l; b *= l; } // Useful random number macros // Don't forget to initialize with srand() inline int myRandi(int x){ return((rand() * x) / RAND_MAX); } inline float myRandf(float x){ return(float(rand() * x) / float(RAND_MAX)); } //---------------------------------------------------------------------------- // Context pointer to allow many instances. static EuphoriaWidget* _ec = 0; class wisp { public: wisp(); ~wisp(); void update(); void draw(); void drawAsBackground(); int density; float*** vertices; float c[NUMCONSTS]; // constants float cr[NUMCONSTS]; // constants' radial position float cv[NUMCONSTS]; // constants' change velocities float hsl[3]; float rgb[3]; float hueSpeed; float saturationSpeed; }; wisp::wisp() { int i, j; float recHalfDens = 1.0f / (float(_ec->dDensity) * 0.5f); density = _ec->dDensity; vertices = new float**[density+1]; for(i=0; i<=density; i++) { vertices[i] = new float*[density+1]; for(j=0; j<=density; j++) { vertices[i][j] = new float[7]; vertices[i][j][3] = float(i) * recHalfDens - 1.0f; // x position on grid vertices[i][j][4] = float(j) * recHalfDens - 1.0f; // y position on grid // distance squared from the center vertices[i][j][5] = vertices[i][j][3] * vertices[i][j][3] + vertices[i][j][4] * vertices[i][j][4]; vertices[i][j][6] = 0.0f; // intensity } } // initialize constants for(i=0; idSpeed * 0.03f) + (_ec->dSpeed * 0.001f); } // pick color hsl[0] = myRandf(1.0f); hsl[1] = 0.1f + myRandf(0.9f); hsl[2] = 1.0f; hueSpeed = myRandf(0.1f) - 0.05f; saturationSpeed = myRandf(0.04f) + 0.001f; } wisp::~wisp() { int i, j; for(i=0; i<=density; i++) { for(j=0; j<=density; j++) { delete[] vertices[i][j]; } delete[] vertices[i]; } delete[] vertices; } void wisp::update() { int i, j; rsVec up, right, crossvec; // visibility constants static float viscon1 = float(_ec->dVisibility) * 0.01f; static float viscon2 = 1.0f / viscon1; // update constants for(i=0; ielapsedTime; if(cr[i] > PIx2) cr[i] -= PIx2; c[i] = cos(cr[i]); } // update vertex positions for(i=0; i<=density; i++){ for(j=0; j<=density; j++){ vertices[i][j][0] = vertices[i][j][3] * vertices[i][j][3] * vertices[i][j][4] * c[0] + vertices[i][j][5] * c[1] + 0.5f * c[2]; vertices[i][j][1] = vertices[i][j][4] * vertices[i][j][4] * vertices[i][j][5] * c[3] + vertices[i][j][3] * c[4] + 0.5f * c[5]; vertices[i][j][2] = vertices[i][j][5] * vertices[i][j][5] * vertices[i][j][3] * c[6] + vertices[i][j][4] * c[7] + c[8]; } } // update vertex normals for most of mesh for(i=1; i 1.0f) vertices[i][j][6] = 1.0f; if(vertices[i][j][6] < 0.0f) vertices[i][j][6] = 0.0f; } } // update color hsl[0] += hueSpeed * _ec->elapsedTime; if(hsl[0] < 0.0f) hsl[0] += 1.0f; if(hsl[0] > 1.0f) hsl[0] -= 1.0f; hsl[1] += saturationSpeed * _ec->elapsedTime; if(hsl[1] <= 0.1f){ hsl[1] = 0.1f; saturationSpeed = -saturationSpeed; } if(hsl[1] >= 1.0f){ hsl[1] = 1.0f; saturationSpeed = -saturationSpeed; } hsl2rgb(hsl[0], hsl[1], hsl[2], rgb[0], rgb[1], rgb[2]); } void wisp::draw() { int i, j; glPushMatrix(); if(_ec->dWireframe) { for(i=1; idWireframe) { for(i=1; i PIx2) fr[i] -= PIx2; } f[0] = 30.0f * cos(fr[0]); f[1] = 0.2f * cos(fr[1]); f[2] = 0.2f * cos(fr[2]); f[3] = 0.8f * cos(fr[3]); for(i=0; i<3; i++) { lr[i] += elapsedTime * lv[i]; if(lr[i] > PIx2) lr[i] -= PIx2; l[i] = cos(lr[i]); l[i] = l[i] * l[i]; } // Create drawing area for feedback texture glViewport(0, 0, feedbacktexsize, feedbacktexsize); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(30.0, aspectRatio, 0.01f, 20.0f); glMatrixMode(GL_MODELVIEW); // Draw glClear(GL_COLOR_BUFFER_BIT); glColor3f(feedbackIntensity, feedbackIntensity, feedbackIntensity); glBindTexture(GL_TEXTURE_2D, feedbacktex); glPushMatrix(); glTranslatef(f[1] * l[1], f[2] * l[1], f[3] * l[2]); glRotatef(f[0] * l[0], 0, 0, 1); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(-0.5f, -0.5f); glVertex3f(-aspectRatio*2.0f, -2.0f, 1.25f); glTexCoord2f(1.5f, -0.5f); glVertex3f(aspectRatio*2.0f, -2.0f, 1.25f); glTexCoord2f(-0.5f, 1.5f); glVertex3f(-aspectRatio*2.0f, 2.0f, 1.25f); glTexCoord2f(1.5f, 1.5f); glVertex3f(aspectRatio*2.0f, 2.0f, 1.25f); glEnd(); glPopMatrix(); glBindTexture(GL_TEXTURE_2D, texName); for(i=0; istart( _frameTime, true ); } #ifdef UNIT_TEST void EuphoriaWidget::keyPressEvent( QKeyEvent* e ) { if( e->key() == Qt::Key_0 ) { setDefaults( 0 ); updateParameters(); } if( e->key() == Qt::Key_1 ) { setDefaults( 1 ); updateParameters(); } if( e->key() == Qt::Key_2 ) { setDefaults( 2 ); updateParameters(); } if( e->key() == Qt::Key_3 ) { setDefaults( 3 ); updateParameters(); } if( e->key() == Qt::Key_4 ) { setDefaults( 4 ); updateParameters(); } if( e->key() == Qt::Key_5 ) { setDefaults( 5 ); updateParameters(); } if( e->key() == Qt::Key_6 ) { setDefaults( 6 ); updateParameters(); } if( e->key() == Qt::Key_7 ) { setDefaults( 7 ); updateParameters(); } if( e->key() == Qt::Key_8 ) { setDefaults( 8 ); updateParameters(); } } #endif void EuphoriaWidget::nextFrame() { updateGL(); _timer->start( _frameTime, true ); } void EuphoriaWidget::updateParameters() { srand((unsigned)time(NULL)); rand(); rand(); rand(); rand(); rand(); elapsedTime = 0.0f; fr[0] = 0.0f; fr[1] = 0.0f; fr[2] = 0.0f; fr[3] = 0.0f; lr[0] = 0.0f; lr[1] = 0.0f; lr[2] = 0.0f; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glLineWidth(2.0f); // Commented out because smooth lines and textures don't mix on my TNT. // It's like it rendering in software mode glEnable(GL_LINE_SMOOTH); //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); if(dTexture) { int whichtex = dTexture; if(whichtex == 4) // random texture whichtex = myRandi(3) + 1; glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Initialize texture glGenTextures(1, &texName); glBindTexture(GL_TEXTURE_2D, texName); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); switch(whichtex){ case 1: gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEXSIZE, TEXSIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, plasmamap); break; case 2: gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEXSIZE, TEXSIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, stringymap); break; case 3: gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEXSIZE, TEXSIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, linesmap); } } else if ( texName ) { glDeleteTextures( 1, &texName ); texName = 0; } if(dFeedback) { feedbacktexsize = int(pow(2.0, dFeedbacksize)); while(feedbacktexsize > viewport[2] || feedbacktexsize > viewport[3]){ dFeedbacksize -= 1; feedbacktexsize = int(pow(2.0, dFeedbacksize)); } // feedback texture setup glEnable(GL_TEXTURE_2D); delete [] feedbackmap; feedbackmap = new unsigned char[feedbacktexsize*feedbacktexsize*3]; glGenTextures(1, &feedbacktex); glBindTexture(GL_TEXTURE_2D, feedbacktex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexImage2D(GL_TEXTURE_2D, 0, 3, feedbacktexsize, feedbacktexsize, 0, GL_RGB, GL_UNSIGNED_BYTE, feedbackmap); // feedback velocity variable setup fv[0] = float(dFeedbackspeed) * (myRandf(0.025f) + 0.025f); fv[1] = float(dFeedbackspeed) * (myRandf(0.05f) + 0.05f); fv[2] = float(dFeedbackspeed) * (myRandf(0.05f) + 0.05f); fv[3] = float(dFeedbackspeed) * (myRandf(0.1f) + 0.1f); lv[0] = float(dFeedbackspeed) * (myRandf(0.0025f) + 0.0025f); lv[1] = float(dFeedbackspeed) * (myRandf(0.0025f) + 0.0025f); lv[2] = float(dFeedbackspeed) * (myRandf(0.0025f) + 0.0025f); } else if ( feedbacktex ) { glDeleteTextures( 1, &feedbacktex ); feedbacktex = 0; } // Initialize wisps _ec = this; delete[] _wisps; delete[] _backwisps; _wisps = new wisp[dWisps]; _backwisps = new wisp[dBackground]; } /** May be called at any time - makes no OpenGL calls. */ void EuphoriaWidget::setDefaults(int which) { switch(which) { case Grid: dWisps = 4; dBackground = 1; dDensity = 25; dVisibility = 70; dSpeed = 15; dFeedback = 0; dFeedbackspeed = 1; dFeedbacksize = 8; dWireframe = 1; dTexture = 0; break; case Cubism: dWisps = 15; dBackground = 0; dDensity = 4; dVisibility = 15; dSpeed = 10; dFeedback = 0; dFeedbackspeed = 1; dFeedbacksize = 8; dWireframe = 0; dTexture = 0; break; case BadMath: dWisps = 2; dBackground = 2; dDensity = 20; dVisibility = 40; dSpeed = 30; dFeedback = 40; dFeedbackspeed = 5; dFeedbacksize = 8; dWireframe = 1; dTexture = 2; break; case MTheory: dWisps = 3; dBackground = 0; dDensity = 25; dVisibility = 15; dSpeed = 20; dFeedback = 40; dFeedbackspeed = 20; dFeedbacksize = 8; dWireframe = 0; dTexture = 0; break; case UHFTEM: dWisps = 0; dBackground = 3; dDensity = 35; dVisibility = 5; dSpeed = 50; dFeedback = 0; dFeedbackspeed = 1; dFeedbacksize = 8; dWireframe = 0; dTexture = 0; break; case Nowhere: dWisps = 0; dBackground = 3; dDensity = 30; dVisibility = 40; dSpeed = 20; dFeedback = 80; dFeedbackspeed = 10; dFeedbacksize = 8; dWireframe = 1; dTexture = 3; break; case Echo: dWisps = 3; dBackground = 0; dDensity = 25; dVisibility = 30; dSpeed = 20; dFeedback = 85; dFeedbackspeed = 30; dFeedbacksize = 8; dWireframe = 0; dTexture = 1; break; case Kaleidoscope: dWisps = 3; dBackground = 0; dDensity = 25; dVisibility = 40; dSpeed = 15; dFeedback = 90; dFeedbackspeed = 3; dFeedbacksize = 8; dWireframe = 0; dTexture = 0; break; case Regular: default: dWisps = 5; dBackground = 0; dDensity = 25; dVisibility = 35; dSpeed = 15; dFeedback = 0; dFeedbackspeed = 1; dFeedbacksize = 8; dWireframe = 0; dTexture = 2; break; } } //---------------------------------------------------------------------------- #ifndef UNIT_TEST #include #include #include // libkscreensaver interface extern "C" { KDE_EXPORT const char* kss_applicationName = "keuphoria.kss"; KDE_EXPORT const char* kss_description = I18N_NOOP( "Euphoria" ); KDE_EXPORT const char* kss_version = "1.0"; KDE_EXPORT KScreenSaver* kss_create( WId id ) { return new KEuphoriaScreenSaver( id ); } KDE_EXPORT QDialog* kss_setup() { return new KEuphoriaSetup; } } //---------------------------------------------------------------------------- KEuphoriaScreenSaver::KEuphoriaScreenSaver( WId id ) : KScreenSaver( id ) { _effect = new EuphoriaWidget; readSettings(); embed( _effect ); _effect->show(); } KEuphoriaScreenSaver::~KEuphoriaScreenSaver() { } static int filterRandom( int n ) { if( (n < 0) || (n >= EuphoriaWidget::DefaultModes) ) { srand((unsigned)time(NULL)); n = rand() % EuphoriaWidget::DefaultModes; } return n; } void KEuphoriaScreenSaver::readSettings() { KConfig* config = KGlobal::config(); config->setGroup("Settings"); _mode = config->readNumEntry( "Mode", EuphoriaWidget::Regular ); _effect->setDefaults( filterRandom(_mode) ); } /** Any invalid mode will select one at random. */ void KEuphoriaScreenSaver::setMode( int id ) { _mode = id; _effect->setDefaults( filterRandom(id) ); _effect->updateParameters(); } //---------------------------------------------------------------------------- #include #include #include #include static const char* defaultText[] = { I18N_NOOP( "Regular" ), I18N_NOOP( "Grid" ), I18N_NOOP( "Cubism" ), I18N_NOOP( "Bad Math" ), I18N_NOOP( "M-Theory" ), I18N_NOOP( "UHFTEM" ), //"ultra high frequency tunneling electron microscope", I18N_NOOP( "Nowhere" ), I18N_NOOP( "Echo" ), I18N_NOOP( "Kaleidoscope" ), I18N_NOOP( "(Random)" ), 0 }; KEuphoriaSetup::KEuphoriaSetup( QWidget* parent, const char* name ) : KDialogBase( parent, name, true, i18n("Setup Euphoria Screen Saver"), Ok|Cancel|Help, Ok, true ) { setButtonText( Help, i18n( "A&bout" ) ); QWidget *main = makeMainWidget(); QHBoxLayout* top = new QHBoxLayout(main, 0, spacingHint()); QVBoxLayout* leftCol = new QVBoxLayout; top->addLayout( leftCol ); QLabel* label = new QLabel( i18n("Mode:"), main ); leftCol->addWidget( label ); modeW = new QComboBox( main ); int i = 0; while (defaultText[i]) modeW->insertItem( i18n(defaultText[i++]) ); leftCol->addWidget( modeW ); leftCol->addStretch(); // Preview QWidget* preview; preview = new QWidget( main ); preview->setFixedSize( 220, 170 ); preview->setBackgroundColor( black ); preview->show(); // otherwise saver does not get correct size _saver = new KEuphoriaScreenSaver( preview->winId() ); top->addWidget(preview); // Now that we have _saver... modeW->setCurrentItem( _saver->mode() ); // set before we connect connect( modeW, SIGNAL(activated(int)), _saver, SLOT(setMode(int)) ); setMinimumSize( sizeHint() ); } KEuphoriaSetup::~KEuphoriaSetup() { delete _saver; } void KEuphoriaSetup::slotHelp() { KMessageBox::about(this, i18n("

Euphoria 1.0

\n

Copyright (c) 2002 Terence M. Welsh
\nhttp://www.reallyslick.com/

\n\n

Ported to KDE by Karl Robillard

"), QString::null, KMessageBox::AllowLink); } /** Ok pressed - save settings and exit */ void KEuphoriaSetup::slotOk() { KConfig* config = KGlobal::config(); config->setGroup("Settings"); QString val; val.setNum( modeW->currentItem() ); config->writeEntry("Mode", val ); config->sync(); accept(); } #endif //---------------------------------------------------------------------------- #ifdef UNIT_TEST // moc Euphoria.h -o Euphoria.moc // g++ -g -DUNIT_TEST Euphoria.cpp -I/usr/lib/qt3/include -lqt -L/usr/lib/qt3/lib -lGLU -lGL #include int main( int argc, char** argv ) { QApplication app( argc, argv ); EuphoriaWidget w; w.setDefaults( EuphoriaWidget::UHFTEM ); app.setMainWidget( &w ); w.show(); return app.exec(); } #endif //EOF