1 //============================================================================
3 // Terence Welsh Screensaver - Solar Winds
4 // http://www.reallyslick.com/
6 // Ported to KDE by Karl Robillard
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 //============================================================================
33 #include <QVBoxLayout>
34 #include <QHBoxLayout>
36 #include "SolarWinds.h"
37 #include "SolarWinds.moc"
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;
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
124 linelist
= new int*[partCount
];
125 for(i
=0; i
<partCount
; i
++)
127 linelist
[i
] = new int[2];
131 lastparticle
= new int[emitCount
];
132 for(i
=0; i
<emitCount
; 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] );
155 for(i
=0; i
<partCount
; i
++)
156 delete[] linelist
[i
];
158 delete[] lastparticle
;
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
;
176 for(i
=0; i
<NUMCONSTS
; 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
)
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
;
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;
209 linelist
[whichparticle
][1] = lastparticle
[i
];
210 linelist
[lastparticle
[i
]][0] = whichparticle
;
211 lastparticle
[i
] = whichparticle
;
215 if(whichparticle
>= partCount
)
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
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
;
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]);
254 switch(_ec
->dGeometry
)
257 for(i
=0; i
<partCount
; i
++)
259 glColor3fv(&particles
[i
].r
);
261 glTranslatef(particles
[i
].x
, particles
[i
].y
, particles
[i
].z
);
266 printf( "KR %d %g %g %g\n", i
,
267 particles
[i
].x
, particles
[i
].y
, particles
[i
].z
);
273 for(i
=0; i
<partCount
; i
++)
275 temp
= particles
[i
].z
+ 40.0f
;
278 glPointSize(pointsize
* temp
);
281 glColor3fv(&particles
[i
].r
);
282 glVertex3fv(&particles
[i
].x
);
288 for(i
=0; i
<partCount
; i
++)
290 temp
= particles
[i
].z
+ 40.0f
;
293 glLineWidth(linesize
* temp
);
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
);
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()
335 void SWindsWidget::paintGL()
341 glClear(GL_COLOR_BUFFER_BIT
);
345 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
346 glColor4f(0.0f
, 0.0f
, 0.0f
, 0.5f
- (float(dBlur
) * 0.0049f
));
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
);
356 glBlendFunc(GL_ONE
, GL_ONE
);
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.
374 for(i
=0; i
<dWinds
; i
++)
382 void SWindsWidget::resizeGL( int w
, int h
)
384 glViewport(0, 0, w
, h
);
386 glMatrixMode(GL_PROJECTION
);
388 gluPerspective(90.0, (float) w
/ (float) h
, 1.0, 10000);
389 glTranslatef(0.0, 0.0, -15.0);
390 glMatrixMode(GL_MODELVIEW
);
395 // Window initialization
396 void SWindsWidget::initializeGL()
399 _timer
->start( _frameTime
, true );
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(); }
416 void SWindsWidget::nextFrame()
419 _timer
->start( _frameTime
, true );
423 void SWindsWidget::updateParameters()
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
);
435 glBlendFunc(GL_ONE
, GL_ONE
);
437 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
); // Necessary for point and line smoothing (I don't know why)
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
)));
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
);
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
502 _winds
= new wind
[dWinds
];
507 May be called at any time - makes no OpenGL calls.
509 void SWindsWidget::setDefaults(int which
)
569 dParticlespeed
= 100;
589 //----------------------------------------------------------------------------
598 // libkscreensaver interface
599 class KSolarWindsSaverInterface
: public KScreenSaverInterface
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
;
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
;
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
)
673 _flux
->setDefaults( filterRandom(id
) );
674 _flux
->updateParameters();
678 //----------------------------------------------------------------------------
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
)
703 setButtonText( Help
, i18n( "A&bout" ) );
704 setButtons(Ok
|Cancel
|Help
);
705 setDefaultButton(Ok
);
706 setCaption(i18n( "Setup Solar Wind" ));
708 showButtonSeparator(true);
709 QWidget
*main
= new QWidget(this);
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
);
722 while (defaultText
[i
])
723 modeW
->insertItem( i18n(defaultText
[i
++]) );
724 leftCol
->addWidget( modeW
);
726 leftCol
->addStretch();
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()
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");
767 val
.setNum( modeW
->currentItem() );
768 config
->writeEntry("Mode", val
);
776 //----------------------------------------------------------------------------
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
);
790 app
.setMainWidget( &w
);