summaryrefslogtreecommitdiffstats
path: root/tdescreensaver/kdesavers/SolarWinds.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdescreensaver/kdesavers/SolarWinds.cpp')
-rw-r--r--tdescreensaver/kdesavers/SolarWinds.cpp778
1 files changed, 778 insertions, 0 deletions
diff --git a/tdescreensaver/kdesavers/SolarWinds.cpp b/tdescreensaver/kdesavers/SolarWinds.cpp
new file mode 100644
index 00000000..c55fe531
--- /dev/null
+++ b/tdescreensaver/kdesavers/SolarWinds.cpp
@@ -0,0 +1,778 @@
+//============================================================================
+//
+// Terence Welsh Screensaver - Solar Winds
+// http://www.reallyslick.com/
+//
+// Ported to KDE by Karl Robillard
+//
+/*
+ * Copyright (C) 2002 Terence M. Welsh
+ *
+ * Solar Winds 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.
+ *
+ * Solar Winds 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+#include <tqtimer.h>
+#include "SolarWinds.h"
+#include "SolarWinds.moc"
+
+
+#define NUMCONSTS 9
+#define PIx2 6.28318530718f
+#define DEG2RAD 0.0174532925f
+
+
+// Useful random number macro
+// Don't forget to initialize with srand()
+
+// This is the original myRandf() which does not work on Linux.
+// I grabed the inline version from Flux.cpp.
+//#define myRandf(x) (float(rand()) * (x * 0.0000305185095f))
+
+inline float myRandf(float x){
+ return(float(rand() * x) / float(RAND_MAX));
+}
+
+
+// Context pointer to allow many instances.
+static SWindsWidget* _ec = 0;
+
+
+struct Emitter
+{
+ float x, y, z;
+};
+
+
+struct Particle
+{
+ float x, y, z;
+ float r, g, b;
+};
+
+
+class wind
+{
+public:
+
+ wind();
+ ~wind();
+ void update();
+
+ Emitter* emitters;
+ Particle* particles;
+ int** linelist;
+ int* lastparticle;
+ int whichparticle;
+ int partCount;
+ int emitCount;
+ bool useLines;
+ float c[NUMCONSTS];
+ float ct[NUMCONSTS];
+ float cv[NUMCONSTS];
+};
+
+
+wind::wind()
+{
+ int i;
+
+ partCount = _ec->dParticles;
+ emitCount = _ec->dEmitters;
+ useLines = (_ec->dGeometry == 2);
+
+ emitters = new Emitter[emitCount];
+ for(i=0; i<emitCount; i++)
+ {
+ emitters[i].x = myRandf(60.0f) - 30.0f;
+ emitters[i].y = myRandf(60.0f) - 30.0f;
+ emitters[i].z = myRandf(30.0f) - 15.0f;
+ }
+
+ particles = new Particle[partCount];
+ for(i=0; i<partCount; i++)
+ {
+ particles[i].x = 0.0f;
+ particles[i].y = 0.0f;
+ particles[i].z = 100.0f; // start particles behind viewer
+ }
+
+ whichparticle = 0;
+
+ if( useLines )
+ {
+ linelist = new int*[partCount];
+ for(i=0; i<partCount; i++)
+ {
+ linelist[i] = new int[2];
+ linelist[i][0] = -1;
+ linelist[i][1] = -1;
+ }
+ lastparticle = new int[emitCount];
+ for(i=0; i<emitCount; i++)
+ lastparticle[i] = i;
+ }
+
+ float windspeed = (float) (_ec->dWindspeed);
+ for(i=0; i<NUMCONSTS; i++)
+ {
+ ct[i] = myRandf(PIx2);
+ cv[i] = myRandf(0.00005f * windspeed * windspeed)
+ + 0.00001f * windspeed * windspeed;
+ //printf( "KR ct %g cv %g\n", ct[i], cv[i] );
+ }
+}
+
+
+wind::~wind()
+{
+ delete[] emitters;
+ delete[] particles;
+
+ if( useLines )
+ {
+ int i;
+ for(i=0; i<partCount; i++)
+ delete[] linelist[i];
+ delete[] linelist;
+ delete[] lastparticle;
+ }
+}
+
+
+void wind::update()
+{
+ int i;
+ float x, y, z;
+ float temp;
+ float particleSpeed = (float) _ec->dParticlespeed;
+
+ float evel = float(_ec->dEmitterspeed) * 0.01f;
+ float pvel = particleSpeed * 0.01f;
+ float pointsize = 0.04f * _ec->dSize;
+ float linesize = 0.005f * _ec->dSize;
+
+ // update constants
+ for(i=0; i<NUMCONSTS; i++)
+ {
+ ct[i] += cv[i];
+ if(ct[i] > PIx2)
+ ct[i] -= PIx2;
+ c[i] = cos(ct[i]);
+ }
+
+ // calculate emissions
+ for(i=0; i<emitCount; i++)
+ {
+ emitters[i].z += evel; // emitter moves toward viewer
+ if(emitters[i].z > 15.0f)
+ {
+ // reset emitter
+ emitters[i].x = myRandf(60.0f) - 30.0f;
+ emitters[i].y = myRandf(60.0f) - 30.0f;
+ emitters[i].z = -15.0f;
+ }
+
+ particles[whichparticle].x = emitters[i].x;
+ particles[whichparticle].y = emitters[i].y;
+ particles[whichparticle].z = emitters[i].z;
+
+ if( useLines )
+ {
+ // link particles to form lines
+ if(linelist[whichparticle][0] >= 0)
+ linelist[linelist[whichparticle][0]][1] = -1;
+ linelist[whichparticle][0] = -1;
+ if(emitters[i].z == -15.0f)
+ linelist[whichparticle][1] = -1;
+ else
+ linelist[whichparticle][1] = lastparticle[i];
+ linelist[lastparticle[i]][0] = whichparticle;
+ lastparticle[i] = whichparticle;
+ }
+
+ whichparticle++;
+ if(whichparticle >= partCount)
+ whichparticle = 0;
+ }
+
+ // calculate particle positions and colors
+ // first modify constants that affect colors
+ c[6] *= 9.0f / particleSpeed;
+ c[7] *= 9.0f / particleSpeed;
+ c[8] *= 9.0f / particleSpeed;
+ // then update each particle
+ for(i=0; i<partCount; i++)
+ {
+ Particle* part = particles + i;
+
+ // store old positions
+ x = part->x;
+ y = part->y;
+ z = part->z;
+
+ // make new positins
+ part->x = x + (c[0] * y + c[1] * z) * pvel;
+ part->y = y + (c[2] * z + c[3] * x) * pvel;
+ part->z = z + (c[4] * x + c[5] * y) * pvel;
+
+ // calculate colors
+ part->r = fabs((part->x - x) * c[6]);
+ part->g = fabs((part->y - y) * c[7]);
+ part->b = fabs((part->z - z) * c[8]);
+
+ // clamp colors
+ if( part->r > 1.0f )
+ part->r = 1.0f;
+ if( part->g > 1.0f )
+ part->g = 1.0f;
+ if( part->b > 1.0f )
+ part->b = 1.0f;
+ }
+
+ // draw particles
+ switch(_ec->dGeometry)
+ {
+ case 0: // lights
+ for(i=0; i<partCount; i++)
+ {
+ glColor3fv(&particles[i].r);
+ glPushMatrix();
+ glTranslatef(particles[i].x, particles[i].y, particles[i].z);
+ glCallList(1);
+ glPopMatrix();
+#if 0
+ if( i == 0 )
+ printf( "KR %d %g %g %g\n", i,
+ particles[i].x, particles[i].y, particles[i].z);
+#endif
+ }
+ break;
+
+ case 1: // points
+ for(i=0; i<partCount; i++)
+ {
+ temp = particles[i].z + 40.0f;
+ if(temp < 0.01f)
+ temp = 0.01f;
+ glPointSize(pointsize * temp);
+
+ glBegin(GL_POINTS);
+ glColor3fv(&particles[i].r);
+ glVertex3fv(&particles[i].x);
+ glEnd();
+ }
+ break;
+
+ case 2: // lines
+ for(i=0; i<partCount; i++)
+ {
+ temp = particles[i].z + 40.0f;
+ if(temp < 0.01f)
+ temp = 0.01f;
+ glLineWidth(linesize * temp);
+
+ glBegin(GL_LINES);
+ if(linelist[i][1] >= 0)
+ {
+ glColor3fv(&particles[i].r);
+ if(linelist[i][0] == -1)
+ glColor3f(0.0f, 0.0f, 0.0f);
+ glVertex3fv(&particles[i].x);
+
+ glColor3fv(&particles[linelist[i][1]].r);
+ if(linelist[linelist[i][1]][1] == -1)
+ glColor3f(0.0f, 0.0f, 0.0f);
+ glVertex3fv(&particles[linelist[i][1]].x);
+ }
+ glEnd();
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+
+SWindsWidget::SWindsWidget( TQWidget* parent, const char* name )
+ : TQGLWidget(parent, name), _winds(0)
+{
+ setDefaults( Regular );
+
+ _frameTime = 1000 / 60;
+ _timer = new TQTimer( this );
+ connect( _timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(nextFrame()) );
+}
+
+
+SWindsWidget::~SWindsWidget()
+{
+ // Free memory
+ delete[] _winds;
+}
+
+
+void SWindsWidget::paintGL()
+{
+ glLoadIdentity();
+
+ if( ! dBlur )
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ else
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(0.0f, 0.0f, 0.0f, 0.5f - (float(dBlur) * 0.0049f));
+
+ glBegin(GL_QUADS);
+ glVertex3f(-40.0f, -17.0f, 0.0f);
+ glVertex3f(40.0f, -17.0f, 0.0f);
+ glVertex3f(40.0f, 17.0f, 0.0f);
+ glVertex3f(-40.0f, 17.0f, 0.0f);
+ glEnd();
+
+ if(!dGeometry)
+ glBlendFunc(GL_ONE, GL_ONE);
+ else
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Necessary for point and line smoothing (I don't know why)
+ // Maybe it's just my video card...
+ }
+
+ // You should need to draw twice if using blur, once to each buffer.
+ // But wglSwapLayerBuffers appears to copy the back to the
+ // front instead of just switching the pointers to them. It turns
+ // out that both NVidia and 3dfx prefer to use PFD_SWAP_COPY instead
+ // of PFD_SWAP_EXCHANGE in the PIXELFORMATDESCRIPTOR. I don't know why...
+ // So this may not work right on other platforms or all video cards.
+
+ // Update surfaces
+ if( _winds )
+ {
+ _ec = this;
+ int i;
+ for(i=0; i<dWinds; i++)
+ _winds[i].update();
+ }
+
+ glFlush();
+}
+
+
+void SWindsWidget::resizeGL( int w, int h )
+{
+ glViewport(0, 0, w, h );
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(90.0, (float) w / (float) h, 1.0, 10000);
+ glTranslatef(0.0, 0.0, -15.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+
+// Window initialization
+void SWindsWidget::initializeGL()
+{
+ updateParameters();
+ _timer->start( _frameTime, true );
+}
+
+
+#ifdef UNIT_TEST
+void SWindsWidget::keyPressEvent( TQKeyEvent* e )
+{
+ if( e->key() == TQt::Key_0 ) { setDefaults( 0 ); updateParameters(); }
+ if( e->key() == TQt::Key_1 ) { setDefaults( 1 ); updateParameters(); }
+ if( e->key() == TQt::Key_2 ) { setDefaults( 2 ); updateParameters(); }
+ if( e->key() == TQt::Key_3 ) { setDefaults( 3 ); updateParameters(); }
+ if( e->key() == TQt::Key_4 ) { setDefaults( 4 ); updateParameters(); }
+ if( e->key() == TQt::Key_5 ) { setDefaults( 5 ); updateParameters(); }
+}
+#endif
+
+
+void SWindsWidget::nextFrame()
+{
+ updateGL();
+ _timer->start( _frameTime, true );
+}
+
+
+void SWindsWidget::updateParameters()
+{
+ int i, j;
+ float x, y, temp;
+
+ srand((unsigned)time(NULL));
+ rand(); rand(); rand(); rand(); rand();
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if(!dGeometry)
+ glBlendFunc(GL_ONE, GL_ONE);
+ else
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Necessary for point and line smoothing (I don't know why)
+ glEnable(GL_BLEND);
+
+ if( ! dGeometry )
+ {
+ // Init lights
+ for(i=0; i<LIGHTSIZE; i++)
+ {
+ for(j=0; j<LIGHTSIZE; j++)
+ {
+ x = float(i - LIGHTSIZE / 2) / float(LIGHTSIZE / 2);
+ y = float(j - LIGHTSIZE / 2) / float(LIGHTSIZE / 2);
+ temp = 1.0f - float(sqrt((x * x) + (y * y)));
+ if(temp > 1.0f)
+ temp = 1.0f;
+ if(temp < 0.0f)
+ temp = 0.0f;
+ lightTexture[i][j] = (unsigned char) (255.0f * temp);
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ 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);
+ glTexImage2D(GL_TEXTURE_2D, 0, 1, LIGHTSIZE, LIGHTSIZE, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, lightTexture);
+
+ temp = 0.02f * dSize;
+ glNewList(1, GL_COMPILE);
+ glBindTexture(GL_TEXTURE_2D, 1);
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex3f(-temp, -temp, 0.0f);
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex3f(temp, -temp, 0.0f);
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex3f(-temp, temp, 0.0f);
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex3f(temp, temp, 0.0f);
+ glEnd();
+ glEndList();
+ }
+ else if(dGeometry == 1)
+ {
+ // init point smoothing
+ glEnable(GL_POINT_SMOOTH);
+ glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
+
+ glDisable(GL_TEXTURE_2D);
+ }
+ else if(dGeometry == 2)
+ {
+ // init line smoothing
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+
+ glDisable(GL_TEXTURE_2D);
+ }
+
+
+ // Initialize surfaces
+ _ec = this;
+ delete[] _winds;
+ _winds = new wind[dWinds];
+}
+
+
+/**
+ May be called at any time - makes no OpenGL calls.
+*/
+void SWindsWidget::setDefaults(int which)
+{
+ switch(which)
+ {
+ case CosmicStrings:
+ dWinds = 1;
+ dEmitters = 50;
+ dParticles = 3000;
+ dGeometry = 2;
+ dSize = 20;
+ dWindspeed = 10;
+ dEmitterspeed = 10;
+ dParticlespeed = 10;
+ dBlur = 10;
+ break;
+
+ case ColdPricklies:
+ dWinds = 1;
+ dEmitters = 300;
+ dParticles = 3000;
+ dGeometry = 2;
+ dSize = 5;
+ dWindspeed = 20;
+ dEmitterspeed = 100;
+ dParticlespeed = 15;
+ dBlur = 70;
+ break;
+
+ case SpaceFur:
+ dWinds = 2;
+ dEmitters = 400;
+ dParticles = 1600;
+ dGeometry = 2;
+ dSize = 15;
+ dWindspeed = 20;
+ dEmitterspeed = 15;
+ dParticlespeed = 10;
+ dBlur = 0;
+ break;
+
+ case Jiggly:
+ dWinds = 1;
+ dEmitters = 40;
+ dParticles = 1200;
+ dGeometry = 1;
+ dSize = 20;
+ dWindspeed = 100;
+ dEmitterspeed = 20;
+ dParticlespeed = 4;
+ dBlur = 50;
+ break;
+
+ case Undertow:
+ dWinds = 1;
+ dEmitters = 400;
+ dParticles = 1200;
+ dGeometry = 0;
+ dSize = 40;
+ dWindspeed = 20;
+ dEmitterspeed = 1;
+ dParticlespeed = 100;
+ dBlur = 50;
+ break;
+
+ case Regular:
+ default:
+ dWinds = 1;
+ dEmitters = 30;
+ dParticles = 2000;
+ dGeometry = 0;
+ dSize = 50;
+ dWindspeed = 20;
+ dEmitterspeed = 15;
+ dParticlespeed = 10;
+ dBlur = 40;
+ break;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+
+#ifndef UNIT_TEST
+#include <klocale.h>
+#include <kglobal.h>
+#include <kconfig.h>
+
+
+// libtdescreensaver interface
+extern "C"
+{
+ KDE_EXPORT const char* kss_applicationName = "ksolarwinds.kss";
+ KDE_EXPORT const char* kss_description = I18N_NOOP( "Solar Winds" );
+ KDE_EXPORT const char* kss_version = "1.0";
+
+ KDE_EXPORT KScreenSaver* kss_create( WId id )
+ {
+ return new KSWindsScreenSaver( id );
+ }
+
+ KDE_EXPORT TQDialog* kss_setup()
+ {
+ return new KSWindsSetup;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+
+KSWindsScreenSaver::KSWindsScreenSaver( WId id ) : KScreenSaver( id )
+{
+ _flux = new SWindsWidget;
+
+ readSettings();
+
+ embed( _flux );
+ _flux->show();
+}
+
+
+KSWindsScreenSaver::~KSWindsScreenSaver()
+{
+}
+
+
+static int filterRandom( int n )
+{
+ if( (n < 0) || (n >= SWindsWidget::DefaultModes) )
+ {
+ srand((unsigned)time(NULL));
+ n = rand() % SWindsWidget::DefaultModes;
+ }
+ return n;
+}
+
+
+void KSWindsScreenSaver::readSettings()
+{
+ TDEConfig* config = TDEGlobal::config();
+ config->setGroup("Settings");
+
+ _mode = config->readNumEntry( "Mode", SWindsWidget::Regular );
+ _flux->setDefaults( filterRandom(_mode) );
+}
+
+
+/**
+ Any invalid mode will select one at random.
+*/
+void KSWindsScreenSaver::setMode( int id )
+{
+ _mode = id;
+ _flux->setDefaults( filterRandom(id) );
+ _flux->updateParameters();
+}
+
+
+//----------------------------------------------------------------------------
+
+
+#include <tqlayout.h>
+#include <tqlabel.h>
+#include <tqcombobox.h>
+#include <kmessagebox.h>
+
+
+static const char* defaultText[] =
+{
+ I18N_NOOP( "Regular" ),
+ I18N_NOOP( "Cosmic Strings" ),
+ I18N_NOOP( "Cold Pricklies" ),
+ I18N_NOOP( "Space Fur" ),
+ I18N_NOOP( "Jiggly" ),
+ I18N_NOOP( "Undertow" ),
+ I18N_NOOP( "(Random)" ),
+ 0
+};
+
+
+KSWindsSetup::KSWindsSetup( TQWidget* parent, const char* name )
+ : KDialogBase( parent, name, true, i18n( "Setup Solar Wind" ),
+ Ok|Cancel|Help, Ok, true )
+{
+ setButtonText( Help, i18n( "A&bout" ) );
+ TQWidget *main = makeMainWidget();
+
+ TQHBoxLayout* top = new TQHBoxLayout( main, 0, spacingHint() );
+
+ TQVBoxLayout* leftCol = new TQVBoxLayout;
+ top->addLayout( leftCol );
+
+ TQLabel* label = new TQLabel( i18n("Mode:"), main );
+ leftCol->addWidget( label );
+
+ modeW = new TQComboBox( main );
+ int i = 0;
+ while (defaultText[i])
+ modeW->insertItem( i18n(defaultText[i++]) );
+ leftCol->addWidget( modeW );
+
+ leftCol->addStretch();
+
+ // Preview
+ TQWidget* preview;
+ preview = new TQWidget( main );
+ preview->setFixedSize( 220, 165 );
+ preview->setBackgroundColor( black );
+ preview->show(); // otherwise saver does not get correct size
+ _saver = new KSWindsScreenSaver( preview->winId() );
+ top->addWidget(preview);
+
+ // Now that we have _saver...
+ modeW->setCurrentItem( _saver->mode() ); // set before we connect
+ connect( modeW, TQT_SIGNAL(activated(int)), _saver, TQT_SLOT(setMode(int)) );
+}
+
+
+KSWindsSetup::~KSWindsSetup()
+{
+ delete _saver;
+}
+
+
+void KSWindsSetup::slotHelp()
+{
+ KMessageBox::about(this,
+ i18n("<h3>Solar Winds 1.0</h3>\n<p>Copyright (c) 2002 Terence M. Welsh<br>\n<a href=\"http://www.reallyslick.com/\">http://www.reallyslick.com/</a></p>\n\n<p>Ported to KDE by Karl Robillard</p>"),
+ TQString(), KMessageBox::AllowLink);
+}
+
+
+/**
+ Ok pressed - save settings and exit
+*/
+void KSWindsSetup::slotOk()
+{
+ TDEConfig* config = TDEGlobal::config();
+ config->setGroup("Settings");
+
+ TQString val;
+ val.setNum( modeW->currentItem() );
+ config->writeEntry("Mode", val );
+
+ config->sync();
+ accept();
+}
+#endif
+
+
+//----------------------------------------------------------------------------
+
+
+#ifdef UNIT_TEST
+// moc SolarWinds.h -o SolarWinds.moc
+// g++ -g -DUNIT_TEST SolarWinds.cpp -I/usr/lib/qt3/include -lqt -L/usr/lib/qt3/lib -lGLU -lGL
+
+#include <tqapplication.h>
+
+int main( int argc, char** argv )
+{
+ TQApplication app( argc, argv );
+
+ SWindsWidget w;
+ app.setMainWidget( &w );
+ w.show();
+
+ return app.exec();
+}
+#endif
+
+
+//EOF