Compile GL screensaver
[kdeartwork.git] / kscreensaver / kdesavers / SolarWinds.cpp
blob7e2be522b57e5c00ced79df6cd106900fe968c58
1 //============================================================================
2 //
3 // Terence Welsh Screensaver - Solar Winds
4 // http://www.reallyslick.com/
5 //
6 // Ported to KDE by Karl Robillard
7 //
8 /*
9 * Copyright (C) 2002 Terence M. Welsh
11 * Solar Winds is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * Solar Winds is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //============================================================================
27 #include <stdio.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include <qtimer.h>
32 //Added by qt3to4:
33 #include <QVBoxLayout>
34 #include <QHBoxLayout>
35 #include <QKeyEvent>
36 #include "SolarWinds.h"
37 #include "SolarWinds.moc"
40 #define NUMCONSTS 9
41 #define PIx2 6.28318530718f
42 #define DEG2RAD 0.0174532925f
45 // Useful random number macro
46 // Don't forget to initialize with srand()
48 // This is the original myRandf() which does not work on Linux.
49 // I grabed the inline version from Flux.cpp.
50 //#define myRandf(x) (float(rand()) * (x * 0.0000305185095f))
52 inline float myRandf(float x){
53 return(float(rand() * x) / float(RAND_MAX));
57 // Context pointer to allow many instances.
58 static SWindsWidget* _ec = 0;
61 struct Emitter
63 float x, y, z;
67 struct Particle
69 float x, y, z;
70 float r, g, b;
74 class wind
76 public:
78 wind();
79 ~wind();
80 void update();
82 Emitter* emitters;
83 Particle* particles;
84 int** linelist;
85 int* lastparticle;
86 int whichparticle;
87 int partCount;
88 int emitCount;
89 bool useLines;
90 float c[NUMCONSTS];
91 float ct[NUMCONSTS];
92 float cv[NUMCONSTS];
96 wind::wind()
98 int i;
100 partCount = _ec->dParticles;
101 emitCount = _ec->dEmitters;
102 useLines = (_ec->dGeometry == 2);
104 emitters = new Emitter[emitCount];
105 for(i=0; i<emitCount; i++)
107 emitters[i].x = myRandf(60.0f) - 30.0f;
108 emitters[i].y = myRandf(60.0f) - 30.0f;
109 emitters[i].z = myRandf(30.0f) - 15.0f;
112 particles = new Particle[partCount];
113 for(i=0; i<partCount; i++)
115 particles[i].x = 0.0f;
116 particles[i].y = 0.0f;
117 particles[i].z = 100.0f; // start particles behind viewer
120 whichparticle = 0;
122 if( useLines )
124 linelist = new int*[partCount];
125 for(i=0; i<partCount; i++)
127 linelist[i] = new int[2];
128 linelist[i][0] = -1;
129 linelist[i][1] = -1;
131 lastparticle = new int[emitCount];
132 for(i=0; i<emitCount; i++)
133 lastparticle[i] = i;
136 float windspeed = (float) (_ec->dWindspeed);
137 for(i=0; i<NUMCONSTS; i++)
139 ct[i] = myRandf(PIx2);
140 cv[i] = myRandf(0.00005f * windspeed * windspeed)
141 + 0.00001f * windspeed * windspeed;
142 //printf( "KR ct %g cv %g\n", ct[i], cv[i] );
147 wind::~wind()
149 delete[] emitters;
150 delete[] particles;
152 if( useLines )
154 int i;
155 for(i=0; i<partCount; i++)
156 delete[] linelist[i];
157 delete[] linelist;
158 delete[] lastparticle;
163 void wind::update()
165 int i;
166 float x, y, z;
167 float temp;
168 float particleSpeed = (float) _ec->dParticlespeed;
170 float evel = float(_ec->dEmitterspeed) * 0.01f;
171 float pvel = particleSpeed * 0.01f;
172 float pointsize = 0.04f * _ec->dSize;
173 float linesize = 0.005f * _ec->dSize;
175 // update constants
176 for(i=0; i<NUMCONSTS; i++)
178 ct[i] += cv[i];
179 if(ct[i] > PIx2)
180 ct[i] -= PIx2;
181 c[i] = cos(ct[i]);
184 // calculate emissions
185 for(i=0; i<emitCount; i++)
187 emitters[i].z += evel; // emitter moves toward viewer
188 if(emitters[i].z > 15.0f)
190 // reset emitter
191 emitters[i].x = myRandf(60.0f) - 30.0f;
192 emitters[i].y = myRandf(60.0f) - 30.0f;
193 emitters[i].z = -15.0f;
196 particles[whichparticle].x = emitters[i].x;
197 particles[whichparticle].y = emitters[i].y;
198 particles[whichparticle].z = emitters[i].z;
200 if( useLines )
202 // link particles to form lines
203 if(linelist[whichparticle][0] >= 0)
204 linelist[linelist[whichparticle][0]][1] = -1;
205 linelist[whichparticle][0] = -1;
206 if(emitters[i].z == -15.0f)
207 linelist[whichparticle][1] = -1;
208 else
209 linelist[whichparticle][1] = lastparticle[i];
210 linelist[lastparticle[i]][0] = whichparticle;
211 lastparticle[i] = whichparticle;
214 whichparticle++;
215 if(whichparticle >= partCount)
216 whichparticle = 0;
219 // calculate particle positions and colors
220 // first modify constants that affect colors
221 c[6] *= 9.0f / particleSpeed;
222 c[7] *= 9.0f / particleSpeed;
223 c[8] *= 9.0f / particleSpeed;
224 // then update each particle
225 for(i=0; i<partCount; i++)
227 Particle* part = particles + i;
229 // store old positions
230 x = part->x;
231 y = part->y;
232 z = part->z;
234 // make new positins
235 part->x = x + (c[0] * y + c[1] * z) * pvel;
236 part->y = y + (c[2] * z + c[3] * x) * pvel;
237 part->z = z + (c[4] * x + c[5] * y) * pvel;
239 // calculate colors
240 part->r = fabs((part->x - x) * c[6]);
241 part->g = fabs((part->y - y) * c[7]);
242 part->b = fabs((part->z - z) * c[8]);
244 // clamp colors
245 if( part->r > 1.0f )
246 part->r = 1.0f;
247 if( part->g > 1.0f )
248 part->g = 1.0f;
249 if( part->b > 1.0f )
250 part->b = 1.0f;
253 // draw particles
254 switch(_ec->dGeometry)
256 case 0: // lights
257 for(i=0; i<partCount; i++)
259 glColor3fv(&particles[i].r);
260 glPushMatrix();
261 glTranslatef(particles[i].x, particles[i].y, particles[i].z);
262 glCallList(1);
263 glPopMatrix();
264 #if 0
265 if( i == 0 )
266 printf( "KR %d %g %g %g\n", i,
267 particles[i].x, particles[i].y, particles[i].z);
268 #endif
270 break;
272 case 1: // points
273 for(i=0; i<partCount; i++)
275 temp = particles[i].z + 40.0f;
276 if(temp < 0.01f)
277 temp = 0.01f;
278 glPointSize(pointsize * temp);
280 glBegin(GL_POINTS);
281 glColor3fv(&particles[i].r);
282 glVertex3fv(&particles[i].x);
283 glEnd();
285 break;
287 case 2: // lines
288 for(i=0; i<partCount; i++)
290 temp = particles[i].z + 40.0f;
291 if(temp < 0.01f)
292 temp = 0.01f;
293 glLineWidth(linesize * temp);
295 glBegin(GL_LINES);
296 if(linelist[i][1] >= 0)
298 glColor3fv(&particles[i].r);
299 if(linelist[i][0] == -1)
300 glColor3f(0.0f, 0.0f, 0.0f);
301 glVertex3fv(&particles[i].x);
303 glColor3fv(&particles[linelist[i][1]].r);
304 if(linelist[linelist[i][1]][1] == -1)
305 glColor3f(0.0f, 0.0f, 0.0f);
306 glVertex3fv(&particles[linelist[i][1]].x);
308 glEnd();
314 //----------------------------------------------------------------------------
317 SWindsWidget::SWindsWidget( QWidget* parent, const char* name )
318 : QGLWidget(parent, name), _winds(0)
320 setDefaults( Regular );
322 _frameTime = 1000 / 60;
323 _timer = new QTimer( this );
324 connect( _timer, SIGNAL(timeout()), this, SLOT(nextFrame()) );
328 SWindsWidget::~SWindsWidget()
330 // Free memory
331 delete[] _winds;
335 void SWindsWidget::paintGL()
337 glLoadIdentity();
339 if( ! dBlur )
341 glClear(GL_COLOR_BUFFER_BIT);
343 else
345 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
346 glColor4f(0.0f, 0.0f, 0.0f, 0.5f - (float(dBlur) * 0.0049f));
348 glBegin(GL_QUADS);
349 glVertex3f(-40.0f, -17.0f, 0.0f);
350 glVertex3f(40.0f, -17.0f, 0.0f);
351 glVertex3f(40.0f, 17.0f, 0.0f);
352 glVertex3f(-40.0f, 17.0f, 0.0f);
353 glEnd();
355 if(!dGeometry)
356 glBlendFunc(GL_ONE, GL_ONE);
357 else
358 glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Necessary for point and line smoothing (I don't know why)
359 // Maybe it's just my video card...
362 // You should need to draw twice if using blur, once to each buffer.
363 // But wglSwapLayerBuffers appears to copy the back to the
364 // front instead of just switching the pointers to them. It turns
365 // out that both NVidia and 3dfx prefer to use PFD_SWAP_COPY instead
366 // of PFD_SWAP_EXCHANGE in the PIXELFORMATDESCRIPTOR. I don't know why...
367 // So this may not work right on other platforms or all video cards.
369 // Update surfaces
370 if( _winds )
372 _ec = this;
373 int i;
374 for(i=0; i<dWinds; i++)
375 _winds[i].update();
378 glFlush();
382 void SWindsWidget::resizeGL( int w, int h )
384 glViewport(0, 0, w, h );
386 glMatrixMode(GL_PROJECTION);
387 glLoadIdentity();
388 gluPerspective(90.0, (float) w / (float) h, 1.0, 10000);
389 glTranslatef(0.0, 0.0, -15.0);
390 glMatrixMode(GL_MODELVIEW);
391 glLoadIdentity();
395 // Window initialization
396 void SWindsWidget::initializeGL()
398 updateParameters();
399 _timer->start( _frameTime, true );
403 #ifdef UNIT_TEST
404 void SWindsWidget::keyPressEvent( QKeyEvent* e )
406 if( e->key() == Qt::Key_0 ) { setDefaults( 0 ); updateParameters(); }
407 if( e->key() == Qt::Key_1 ) { setDefaults( 1 ); updateParameters(); }
408 if( e->key() == Qt::Key_2 ) { setDefaults( 2 ); updateParameters(); }
409 if( e->key() == Qt::Key_3 ) { setDefaults( 3 ); updateParameters(); }
410 if( e->key() == Qt::Key_4 ) { setDefaults( 4 ); updateParameters(); }
411 if( e->key() == Qt::Key_5 ) { setDefaults( 5 ); updateParameters(); }
413 #endif
416 void SWindsWidget::nextFrame()
418 updateGL();
419 _timer->start( _frameTime, true );
423 void SWindsWidget::updateParameters()
425 int i, j;
426 float x, y, temp;
428 srand((unsigned)time(NULL));
429 rand(); rand(); rand(); rand(); rand();
431 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
432 glClear(GL_COLOR_BUFFER_BIT);
434 if(!dGeometry)
435 glBlendFunc(GL_ONE, GL_ONE);
436 else
437 glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Necessary for point and line smoothing (I don't know why)
438 glEnable(GL_BLEND);
440 if( ! dGeometry )
442 // Init lights
443 for(i=0; i<LIGHTSIZE; i++)
445 for(j=0; j<LIGHTSIZE; j++)
447 x = float(i - LIGHTSIZE / 2) / float(LIGHTSIZE / 2);
448 y = float(j - LIGHTSIZE / 2) / float(LIGHTSIZE / 2);
449 temp = 1.0f - float(sqrt((x * x) + (y * y)));
450 if(temp > 1.0f)
451 temp = 1.0f;
452 if(temp < 0.0f)
453 temp = 0.0f;
454 lightTexture[i][j] = (unsigned char) (255.0f * temp);
458 glEnable(GL_TEXTURE_2D);
459 glBindTexture(GL_TEXTURE_2D, 1);
460 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
461 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
462 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
463 glTexImage2D(GL_TEXTURE_2D, 0, 1, LIGHTSIZE, LIGHTSIZE, 0,
464 GL_LUMINANCE, GL_UNSIGNED_BYTE, lightTexture);
466 temp = 0.02f * dSize;
467 glNewList(1, GL_COMPILE);
468 glBindTexture(GL_TEXTURE_2D, 1);
469 glBegin(GL_TRIANGLE_STRIP);
470 glTexCoord2f(0.0f, 0.0f);
471 glVertex3f(-temp, -temp, 0.0f);
472 glTexCoord2f(1.0f, 0.0f);
473 glVertex3f(temp, -temp, 0.0f);
474 glTexCoord2f(0.0f, 1.0f);
475 glVertex3f(-temp, temp, 0.0f);
476 glTexCoord2f(1.0f, 1.0f);
477 glVertex3f(temp, temp, 0.0f);
478 glEnd();
479 glEndList();
481 else if(dGeometry == 1)
483 // init point smoothing
484 glEnable(GL_POINT_SMOOTH);
485 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
487 glDisable(GL_TEXTURE_2D);
489 else if(dGeometry == 2)
491 // init line smoothing
492 glEnable(GL_LINE_SMOOTH);
493 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
495 glDisable(GL_TEXTURE_2D);
499 // Initialize surfaces
500 _ec = this;
501 delete[] _winds;
502 _winds = new wind[dWinds];
507 May be called at any time - makes no OpenGL calls.
509 void SWindsWidget::setDefaults(int which)
511 switch(which)
513 case CosmicStrings:
514 dWinds = 1;
515 dEmitters = 50;
516 dParticles = 3000;
517 dGeometry = 2;
518 dSize = 20;
519 dWindspeed = 10;
520 dEmitterspeed = 10;
521 dParticlespeed = 10;
522 dBlur = 10;
523 break;
525 case ColdPricklies:
526 dWinds = 1;
527 dEmitters = 300;
528 dParticles = 3000;
529 dGeometry = 2;
530 dSize = 5;
531 dWindspeed = 20;
532 dEmitterspeed = 100;
533 dParticlespeed = 15;
534 dBlur = 70;
535 break;
537 case SpaceFur:
538 dWinds = 2;
539 dEmitters = 400;
540 dParticles = 1600;
541 dGeometry = 2;
542 dSize = 15;
543 dWindspeed = 20;
544 dEmitterspeed = 15;
545 dParticlespeed = 10;
546 dBlur = 0;
547 break;
549 case Jiggly:
550 dWinds = 1;
551 dEmitters = 40;
552 dParticles = 1200;
553 dGeometry = 1;
554 dSize = 20;
555 dWindspeed = 100;
556 dEmitterspeed = 20;
557 dParticlespeed = 4;
558 dBlur = 50;
559 break;
561 case Undertow:
562 dWinds = 1;
563 dEmitters = 400;
564 dParticles = 1200;
565 dGeometry = 0;
566 dSize = 40;
567 dWindspeed = 20;
568 dEmitterspeed = 1;
569 dParticlespeed = 100;
570 dBlur = 50;
571 break;
573 case Regular:
574 default:
575 dWinds = 1;
576 dEmitters = 30;
577 dParticles = 2000;
578 dGeometry = 0;
579 dSize = 50;
580 dWindspeed = 20;
581 dEmitterspeed = 15;
582 dParticlespeed = 10;
583 dBlur = 40;
584 break;
589 //----------------------------------------------------------------------------
592 #ifndef UNIT_TEST
593 #include <klocale.h>
594 #include <kglobal.h>
595 #include <kconfig.h>
598 // libkscreensaver interface
599 class KSolarWindsSaverInterface : public KScreenSaverInterface
603 public:
604 virtual KAboutData* aboutData() {
605 return new KAboutData( "ksolarwinds.kss", I18N_NOOP( "Solar Winds" ), "1.0", I18N_NOOP( "Solar Winds" ) );
609 virtual KScreenSaver* create( WId id )
611 return new KSWindsScreenSaver( id );
614 virtual QDialog* setup()
616 return new KSWindsSetup;
620 int main( int argc, char *argv[] )
622 KSolarWindsSaverInterface kss;
623 return kScreenSaverMain( argc, argv, kss );
627 //----------------------------------------------------------------------------
630 KSWindsScreenSaver::KSWindsScreenSaver( WId id ) : KScreenSaver( id )
632 _flux = new SWindsWidget;
634 readSettings();
636 embed( _flux );
637 _flux->show();
641 KSWindsScreenSaver::~KSWindsScreenSaver()
646 static int filterRandom( int n )
648 if( (n < 0) || (n >= SWindsWidget::DefaultModes) )
650 srand((unsigned)time(NULL));
651 n = rand() % SWindsWidget::DefaultModes;
653 return n;
657 void KSWindsScreenSaver::readSettings()
659 KConfig* config = KGlobal::config();
660 config->setGroup("Settings");
662 _mode = config->readNumEntry( "Mode", SWindsWidget::Regular );
663 _flux->setDefaults( filterRandom(_mode) );
668 Any invalid mode will select one at random.
670 void KSWindsScreenSaver::setMode( int id )
672 _mode = id;
673 _flux->setDefaults( filterRandom(id) );
674 _flux->updateParameters();
678 //----------------------------------------------------------------------------
681 #include <qlayout.h>
682 #include <qlabel.h>
683 #include <qcombobox.h>
684 #include <kmessagebox.h>
687 static const char* defaultText[] =
689 I18N_NOOP( "Regular" ),
690 I18N_NOOP( "Cosmic Strings" ),
691 I18N_NOOP( "Cold Pricklies" ),
692 I18N_NOOP( "Space Fur" ),
693 I18N_NOOP( "Jiggly" ),
694 I18N_NOOP( "Undertow" ),
695 I18N_NOOP( "(Random)" ),
700 KSWindsSetup::KSWindsSetup( QWidget* parent, const char* name )
701 : KDialog( parent)
703 setButtonText( Help, i18n( "A&bout" ) );
704 setButtons(Ok|Cancel|Help);
705 setDefaultButton(Ok);
706 setCaption(i18n( "Setup Solar Wind" ));
707 setModal(true);
708 showButtonSeparator(true);
709 QWidget *main = new QWidget(this);
710 setMainWidget(main);
712 QHBoxLayout* top = new QHBoxLayout( main, 0, spacingHint() );
714 QVBoxLayout* leftCol = new QVBoxLayout;
715 top->addLayout( leftCol );
717 QLabel* label = new QLabel( i18n("Mode:"), main );
718 leftCol->addWidget( label );
720 modeW = new QComboBox( main );
721 int i = 0;
722 while (defaultText[i])
723 modeW->insertItem( i18n(defaultText[i++]) );
724 leftCol->addWidget( modeW );
726 leftCol->addStretch();
728 // Preview
729 QWidget* preview;
730 preview = new QWidget( main );
731 preview->setFixedSize( 220, 165 );
732 preview->setBackgroundColor( Qt::black );
733 preview->show(); // otherwise saver does not get correct size
734 _saver = new KSWindsScreenSaver( preview->winId() );
735 top->addWidget(preview);
737 // Now that we have _saver...
738 modeW->setCurrentItem( _saver->mode() ); // set before we connect
739 connect( modeW, SIGNAL(activated(int)), _saver, SLOT(setMode(int)) );
740 connect( this, SIGNAL(okClicked()), this, SLOT(slotOk()));
744 KSWindsSetup::~KSWindsSetup()
746 delete _saver;
750 void KSWindsSetup::slotHelp()
752 KMessageBox::about(this,
753 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>"),
754 QString::null, KMessageBox::AllowLink);
759 Ok pressed - save settings and exit
761 void KSWindsSetup::slotOk()
763 KConfig* config = KGlobal::config();
764 config->setGroup("Settings");
766 QString val;
767 val.setNum( modeW->currentItem() );
768 config->writeEntry("Mode", val );
770 config->sync();
771 accept();
773 #endif
776 //----------------------------------------------------------------------------
779 #ifdef UNIT_TEST
780 // moc SolarWinds.h -o SolarWinds.moc
781 // g++ -g -DUNIT_TEST SolarWinds.cpp -I/usr/lib/qt3/include -lqt -L/usr/lib/qt3/lib -lGLU -lGL
783 #include <qapplication.h>
785 int main( int argc, char** argv )
787 QApplication app( argc, argv );
789 SWindsWidget w;
790 app.setMainWidget( &w );
791 w.show();
793 return app.exec();
795 #endif
798 //EOF