1 //============================================================================
3 // Terence Welsh Screensaver - Flux
4 // http://www.reallyslick.com/
6 // Ported to KDE by Karl Robillard
9 * Copyright (C) 2002 Terence M. Welsh
11 * Flux 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 * Flux 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 //============================================================================
30 [ ] Regular and others are messed up after Sparkler.
31 Insane seems to reset them.
41 #include <QVBoxLayout>
42 #include <QHBoxLayout>
49 #define PIx2 6.28318530718f
50 #define DEG2RAD 0.0174532925f
53 void hsl2rgb(float h
, float s
, float l
, float &r
, float &g
, float &b
)
56 if(h
< 0.166667){ // full red, some green
62 if(h
< 0.5){ // full green
64 if(h
< 0.333333){ // some red
65 r
= 1.0f
- ((h
- 0.166667f
) * 6.0f
);
69 b
= (h
- 0.333333f
) * 6.0f
;
74 if(h
< 0.833333){ // full blue
76 if(h
< 0.666667){ // some green
77 g
= 1.0f
- ((h
- 0.5f
) * 6.0f
);
81 r
= (h
- 0.666667f
) * 6.0f
;
85 else{ // full red, some blue
87 b
= 1.0f
- ((h
- 0.833333f
) * 6.0f
);
93 // saturation influence
94 r
= 1.0f
- (s
* (1.0f
- r
));
95 g
= 1.0f
- (s
* (1.0f
- g
));
96 b
= 1.0f
- (s
* (1.0f
- b
));
98 // luminosity influence
105 // Useful random number macros
106 // Don't forget to initialize with srand()
107 inline int myRandi(int x
){
108 return((rand() * x
) / RAND_MAX
);
110 inline float myRandf(float x
){
111 return(float(rand() * x
) / float(RAND_MAX
));
115 //----------------------------------------------------------------------------
118 // Flux context to allow many instances.
119 static FluxWidget
* _fc
= 0;
121 static int whichparticle
;
124 // This class is poorly named. It's actually a whole trail of particles.
131 float update(float *c
);
144 // Offsets are somewhat like default positions for the head of each
145 // particle trail. Offsets spread out the particle trails and keep
146 // them from all overlapping.
147 offset
[0] = cos(PIx2
* float(whichparticle
) / float(_fc
->dParticles
));
148 offset
[1] = float(whichparticle
) / float(_fc
->dParticles
) - 0.5f
;
149 offset
[2] = sin(PIx2
* float(whichparticle
) / float(_fc
->dParticles
));
152 // Initialize memory and set initial positions out of view of the camera
153 trails
= _fc
->dTrail
;
154 vertices
= new float*[ trails
];
157 for(i
=0; i
<trails
; i
++)
159 vertices
[i
] = new float[5]; // 0,1,2 = position, 3 = hue, 4 = saturation
160 vertices
[i
][0] = 0.0f
;
161 vertices
[i
][1] = 3.0f
;
162 vertices
[i
][2] = 0.0f
;
163 vertices
[i
][3] = 0.0f
;
164 vertices
[i
][4] = 0.0f
;
171 particle::~particle()
173 for(int i
=0; i
<trails
; i
++)
174 delete[] vertices
[i
];
179 float particle::update(float *c
)
183 float cx
, cy
, cz
; // Containment variables
185 static float expander
= 1.0f
+ 0.0005f
* float(_fc
->dExpansion
);
186 static float blower
= 0.001f
* float(_fc
->dWind
);
187 //static float otherxyz[3];
190 // Record old position
193 oldpos
[0] = vertices
[oldc
][0];
194 oldpos
[1] = vertices
[oldc
][1];
195 oldpos
[2] = vertices
[oldc
][2];
198 if(counter
>= _fc
->dTrail
)
201 // Here's the iterative math for calculating new vertex positions
202 // first calculate limiting terms which keep vertices from constantly
203 // flying off to infinity
204 cx
= vertices
[oldc
][0] * (1.0f
- 1.0f
/ (vertices
[oldc
][0] * vertices
[oldc
][0] + 1.0f
));
205 cy
= vertices
[oldc
][1] * (1.0f
- 1.0f
/ (vertices
[oldc
][1] * vertices
[oldc
][1] + 1.0f
));
206 cz
= vertices
[oldc
][2] * (1.0f
- 1.0f
/ (vertices
[oldc
][2] * vertices
[oldc
][2] + 1.0f
));
207 // then calculate new positions
208 vertices
[counter
][0] = vertices
[oldc
][0] + c
[6] * offset
[0] - cx
209 + c
[2] * vertices
[oldc
][1]
210 + c
[5] * vertices
[oldc
][2];
211 vertices
[counter
][1] = vertices
[oldc
][1] + c
[6] * offset
[1] - cy
212 + c
[1] * vertices
[oldc
][2]
213 + c
[4] * vertices
[oldc
][0];
214 vertices
[counter
][2] = vertices
[oldc
][2] + c
[6] * offset
[2] - cz
215 + c
[0] * vertices
[oldc
][0]
216 + c
[3] * vertices
[oldc
][1];
219 vertices
[counter
][3] = cx
* cx
+ cy
* cy
+ cz
* cz
;
220 if(vertices
[counter
][3] > 1.0f
)
221 vertices
[counter
][3] = 1.0f
;
222 vertices
[counter
][3] += c
[7];
223 // Limit the hue (0 - 1)
224 if(vertices
[counter
][3] > 1.0f
)
225 vertices
[counter
][3] -= 1.0f
;
226 if(vertices
[counter
][3] < 0.0f
)
227 vertices
[counter
][3] += 1.0f
;
229 vertices
[counter
][4] = c
[0] + vertices
[counter
][3];
230 // Limit the saturation (0 - 1)
231 if(vertices
[counter
][4] < 0.0f
)
232 vertices
[counter
][4] = -vertices
[counter
][4];
233 vertices
[counter
][4] -= float(int(vertices
[counter
][4]));
234 vertices
[counter
][4] = 1.0f
- (vertices
[counter
][4] * vertices
[counter
][4]);
236 // Bring particles back if they escape
238 if((vertices
[0][0] > 1000000000.0f
) || (vertices
[0][0] < -1000000000.0f
)
239 || (vertices
[0][1] > 1000000000.0f
) || (vertices
[0][1] < -1000000000.0f
)
240 || (vertices
[2][2] > 1000000000.0f
) || (vertices
[0][2] < -1000000000.0f
)){
241 vertices
[0][0] = myRandf(2.0f
) - 1.0f
;
242 vertices
[0][1] = myRandf(2.0f
) - 1.0f
;
243 vertices
[0][2] = myRandf(2.0f
) - 1.0f
;
247 // Draw every vertex in particle trail
250 luminosity
= _fc
->lumdiff
;
251 for(i
=0; i
<_fc
->dTrail
; i
++){
257 // assign color to particle
258 hsl2rgb(vertices
[p
][3], vertices
[p
][4], luminosity
, rgb
[0], rgb
[1], rgb
[2]);
262 if(_fc
->dGeometry
== 1) // Spheres
263 glTranslatef(vertices
[p
][0], vertices
[p
][1], vertices
[p
][2]);
264 else{ // Points or lights
265 depth
= _fc
->cosCameraAngle
* vertices
[p
][2] - _fc
->sinCameraAngle
* vertices
[p
][0];
266 glTranslatef(_fc
->cosCameraAngle
* vertices
[p
][0] + _fc
->sinCameraAngle
267 * vertices
[p
][2], vertices
[p
][1], depth
);
269 if(_fc
->dGeometry
){ // Spheres or lights
270 switch(_fc
->dTrail
- growth
){
272 glScalef(0.259f
, 0.259f
, 0.259f
);
275 glScalef(0.5f
, 0.5f
, 0.5f
);
278 glScalef(0.707f
, 0.707f
, 0.707f
);
281 glScalef(0.866f
, 0.866f
, 0.866f
);
284 glScalef(0.966f
, 0.966f
, 0.966f
);
287 switch(_fc
->dGeometry
){
289 switch(_fc
->dTrail
- growth
){
291 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.001036f
));
294 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.002f
));
297 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.002828f
));
300 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.003464f
));
303 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.003864f
));
306 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.004f
));
309 glVertex3f(0.0f
,0.0f
,0.0f
);
317 vertices
[p
][0] *= expander
;
318 vertices
[p
][1] *= expander
;
319 vertices
[p
][2] *= expander
;
320 vertices
[p
][2] += blower
;
321 luminosity
+= _fc
->lumdiff
;
324 // Find distance between new position and old position and return it
325 oldpos
[0] -= vertices
[counter
][0];
326 oldpos
[1] -= vertices
[counter
][1];
327 oldpos
[2] -= vertices
[counter
][2];
328 return(float(sqrt(oldpos
[0] * oldpos
[0] + oldpos
[1] * oldpos
[1] + oldpos
[2] * oldpos
[2])));
332 // This class is a set of particle trails and constants that enter
333 // into their equations of motion.
344 float c
[NUMCONSTS
]; // constants
345 float cv
[NUMCONSTS
]; // constants' change velocities
346 int currentSmartConstant
;
355 particles
= new particle
[_fc
->dParticles
];
358 float instability
= _fc
->dInstability
;
360 for(i
=0; i
<NUMCONSTS
; i
++)
362 c
[i
] = myRandf(2.0f
) - 1.0f
;
363 cv
[i
] = myRandf(0.000005f
* instability
* instability
)
364 + 0.000001f
* instability
* instability
;
367 currentSmartConstant
= 0;
382 // randomize constants
386 for(i
=0; i
<NUMCONSTS
; i
++)
387 c
[i
] = myRandf(2.0f
) - 1.0f
;
388 int temp
= 101 - _fc
->dRandomize
;
390 randomize
= temp
+ myRandi(temp
);
395 for(i
=0; i
<NUMCONSTS
; i
++){
407 // update all particles in this flux field
409 for(i
=0; i
<_fc
->dParticles
; i
++)
410 dist
= particles
[i
].update(c
);
412 // use dist from last particle to activate smart constants
415 const float upper
= 0.4f
;
416 const float lower
= 0.2f
;
418 if(dist
> upper
&& dist
> oldDistance
)
420 if(dist
< lower
&& dist
< oldDistance
)
423 cv
[currentSmartConstant
] = -cv
[currentSmartConstant
];
424 currentSmartConstant
++;
425 if(currentSmartConstant
>= _fc
->dSmart
)
426 currentSmartConstant
= 0;
433 //----------------------------------------------------------------------------
436 FluxWidget::FluxWidget( QWidget
* parent
, const char* name
)
437 : QGLWidget(parent
, name
), _fluxes(0)
439 setDefaults( Regular
);
441 _frameTime
= 1000 / 60;
442 _timer
= new QTimer( this );
443 connect( _timer
, SIGNAL(timeout()), this, SLOT(nextFrame()) );
447 FluxWidget::~FluxWidget()
454 void FluxWidget::paintGL()
459 if(dBlur
) // partially
461 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
463 glDisable(GL_DEPTH_TEST
);
464 glColor4f(0.0f
, 0.0f
, 0.0f
, 0.5f
- (float(sqrt(sqrt(double(dBlur
)))) * 0.15495f
));
465 glBegin(GL_TRIANGLE_STRIP
);
466 glVertex3f(-5.0f
, -4.0f
, 0.0f
);
467 glVertex3f(5.0f
, -4.0f
, 0.0f
);
468 glVertex3f(-5.0f
, 4.0f
, 0.0f
);
469 glVertex3f(5.0f
, 4.0f
, 0.0f
);
474 glClear(GL_COLOR_BUFFER_BIT
);
477 cameraAngle
+= 0.01f
* float(dRotation
);
478 if(cameraAngle
>= 360.0f
)
479 cameraAngle
-= 360.0f
;
480 if(dGeometry
== 1) // Only rotate for spheres
481 glRotatef(cameraAngle
, 0.0f
, 1.0f
, 0.0f
);
484 cosCameraAngle
= cos(cameraAngle
* DEG2RAD
);
485 sinCameraAngle
= sin(cameraAngle
* DEG2RAD
);
488 // set up blend modes for rendering particles
491 case 0: // Blending for points
492 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
494 glEnable(GL_POINT_SMOOTH
);
495 glHint(GL_POINT_SMOOTH_HINT
, GL_NICEST
);
498 case 1: // No blending for spheres, but we need z-buffering
500 glEnable(GL_DEPTH_TEST
);
501 glClear(GL_DEPTH_BUFFER_BIT
);
504 case 2: // Blending for lights
505 glBlendFunc(GL_ONE
, GL_ONE
);
514 for(i
=0; i
<dFluxes
; i
++)
522 void FluxWidget::resizeGL( int w
, int h
)
524 glViewport(0, 0, w
, h
);
526 glMatrixMode(GL_PROJECTION
);
528 gluPerspective(100.0, (float) w
/ (float) h
, 0.01, 200);
529 glTranslatef(0.0, 0.0, -2.5);
530 glMatrixMode(GL_MODELVIEW
);
535 // Window initialization
536 void FluxWidget::initializeGL()
538 //resizeGL( width(), height() );
541 _timer
->setSingleShot(true);
542 _timer
->start( _frameTime
);
547 void FluxWidget::keyPressEvent( QKeyEvent
* e
)
549 if( e
->key() == Qt::Key_0
) { setDefaults( 0 ); updateParameters(); }
550 if( e
->key() == Qt::Key_1
) { setDefaults( 1 ); updateParameters(); }
551 if( e
->key() == Qt::Key_2
) { setDefaults( 2 ); updateParameters(); }
552 if( e
->key() == Qt::Key_3
) { setDefaults( 3 ); updateParameters(); }
553 if( e
->key() == Qt::Key_4
) { setDefaults( 4 ); updateParameters(); }
554 if( e
->key() == Qt::Key_5
) { setDefaults( 5 ); updateParameters(); }
559 void FluxWidget::nextFrame()
562 _timer
->setSingleShot(true);
563 _timer
->start( _frameTime
);
568 May be called at any time - makes no OpenGL calls.
570 void FluxWidget::setDefaults( int which
)
664 Called after dGeometry, dTrail, or dFluxes is changed
665 (such as with setDefaults).
667 void FluxWidget::updateParameters()
672 srand((unsigned)time(NULL
));
673 rand(); rand(); rand(); rand(); rand();
678 glEnable(GL_CULL_FACE
);
679 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
680 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
684 glDisable(GL_LIGHTING
);
685 glDisable(GL_COLOR_MATERIAL
);
686 glDisable(GL_TEXTURE_2D
);
688 glEnable(GL_POINT_SMOOTH
);
689 //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
691 else if(dGeometry
== 1) // Spheres and their lighting
693 glNewList(1, GL_COMPILE
);
694 GLUquadricObj
* qobj
= gluNewQuadric();
695 gluSphere(qobj
, 0.005f
* dSize
, dComplexity
+ 2, dComplexity
+ 1);
696 gluDeleteQuadric(qobj
);
699 glDisable(GL_TEXTURE_2D
);
700 glEnable(GL_LIGHTING
);
703 float ambient
[4] = {0.0f
, 0.0f
, 0.0f
, 0.0f
};
704 float diffuse
[4] = {1.0f
, 1.0f
, 1.0f
, 0.0f
};
705 float specular
[4] = {1.0f
, 1.0f
, 1.0f
, 0.0f
};
706 float position
[4] = {500.0f
, 500.0f
, 500.0f
, 0.0f
};
708 glLightfv(GL_LIGHT0
, GL_AMBIENT
, ambient
);
709 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, diffuse
);
710 glLightfv(GL_LIGHT0
, GL_SPECULAR
, specular
);
711 glLightfv(GL_LIGHT0
, GL_POSITION
, position
);
712 glEnable(GL_COLOR_MATERIAL
);
713 glColorMaterial(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
715 else if(dGeometry
== 2) // Init lights
717 for(i
=0; i
<LIGHTSIZE
; i
++)
719 for(j
=0; j
<LIGHTSIZE
; j
++)
721 x
= float(i
- LIGHTSIZE
/ 2) / float(LIGHTSIZE
/ 2);
722 y
= float(j
- LIGHTSIZE
/ 2) / float(LIGHTSIZE
/ 2);
723 temp
= 1.0f
- float(sqrt((x
* x
) + (y
* y
)));
728 lightTexture
[i
][j
] = (unsigned char) (255.0f
* temp
* temp
);
732 glDisable(GL_LIGHTING
);
733 glDisable(GL_COLOR_MATERIAL
);
735 glEnable(GL_TEXTURE_2D
);
736 glBindTexture(GL_TEXTURE_2D
, 1);
737 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
738 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
739 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
740 glTexImage2D(GL_TEXTURE_2D
, 0, 1, LIGHTSIZE
, LIGHTSIZE
, 0,
741 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, lightTexture
);
743 temp
= float(dSize
) * 0.005f
;
744 glNewList(1, GL_COMPILE
);
745 glBindTexture(GL_TEXTURE_2D
, 1);
746 glBegin(GL_TRIANGLES
);
747 glTexCoord2f(0.0f
, 0.0f
);
748 glVertex3f(-temp
, -temp
, 0.0f
);
749 glTexCoord2f(1.0f
, 0.0f
);
750 glVertex3f(temp
, -temp
, 0.0f
);
751 glTexCoord2f(1.0f
, 1.0f
);
752 glVertex3f(temp
, temp
, 0.0f
);
753 glTexCoord2f(0.0f
, 0.0f
);
754 glVertex3f(-temp
, -temp
, 0.0f
);
755 glTexCoord2f(1.0f
, 1.0f
);
756 glVertex3f(temp
, temp
, 0.0f
);
757 glTexCoord2f(0.0f
, 1.0f
);
758 glVertex3f(-temp
, temp
, 0.0f
);
763 // Initialize luminosity difference
764 lumdiff
= 1.0f
/ float(dTrail
);
768 _fluxes
= new flux
[dFluxes
];
772 //----------------------------------------------------------------------------
781 // libkscreensaver interface
782 class KFluxSaverInterface
: public KScreenSaverInterface
787 virtual KAboutData
* aboutData() {
788 return new KAboutData( "kflux.kss", I18N_NOOP( "Flux" ), "1.0", I18N_NOOP( "Flux" ) );
792 virtual KScreenSaver
* create( WId id
)
794 return new KFluxScreenSaver( id
);
797 virtual QDialog
* setup()
799 return new KFluxSetup
;
803 int main( int argc
, char *argv
[] )
805 KFluxSaverInterface kss
;
806 return kScreenSaverMain( argc
, argv
, kss
);
810 //----------------------------------------------------------------------------
813 KFluxScreenSaver::KFluxScreenSaver( WId id
) : KScreenSaver( id
)
815 _flux
= new FluxWidget
;
824 KFluxScreenSaver::~KFluxScreenSaver()
829 static int filterRandom( int n
)
831 if( (n
< 0) || (n
>= FluxWidget::DefaultModes
) )
833 srand((unsigned)time(NULL
));
834 n
= rand() % FluxWidget::DefaultModes
;
840 void KFluxScreenSaver::readSettings()
842 KConfig
* config
= KGlobal::config();
843 config
->setGroup("Settings");
845 _mode
= config
->readEntry( "Mode", (int)FluxWidget::Regular
);
846 _flux
->setDefaults( filterRandom(_mode
) );
851 Any invalid mode will select one at random.
853 void KFluxScreenSaver::setMode( int id
)
856 _flux
->setDefaults( filterRandom(id
) );
857 _flux
->updateParameters();
861 //----------------------------------------------------------------------------
865 #include <qpushbutton.h>
867 #include <qcombobox.h>
868 #include <kbuttonbox.h>
869 #include <kmessagebox.h>
872 static const char* defaultText
[] =
874 I18N_NOOP( "Regular" ),
875 I18N_NOOP( "Hypnotic" ),
876 I18N_NOOP( "Insane" ),
877 I18N_NOOP( "Sparklers" ),
878 I18N_NOOP( "Paradigm" ),
879 I18N_NOOP( "Galactic" ),
880 I18N_NOOP( "(Random)" ),
885 KFluxSetup::KFluxSetup( QWidget
* parent
, const char* name
)
888 setButtonText( Help
, i18n( "A&bout" ) );
889 setButtons(Ok
|Cancel
|Help
);
890 setDefaultButton(Ok
);
892 showButtonSeparator(true);
893 QWidget
*main
= new QWidget(this);
896 QHBoxLayout
* top
= new QHBoxLayout( main
, 0, spacingHint() );
897 QVBoxLayout
* leftCol
= new QVBoxLayout
;
898 top
->addLayout( leftCol
);
901 QLabel
* label
= new QLabel( i18n("Mode:"), main
);
902 leftCol
->addWidget( label
);
904 modeW
= new QComboBox( main
);
906 while (defaultText
[i
])
907 modeW
->insertItem( i18n(defaultText
[i
++]) );
908 leftCol
->addWidget( modeW
);
910 leftCol
->addStretch();
914 preview
= new QWidget( main
);
915 preview
->setFixedSize( 220, 165 );
916 preview
->setBackgroundColor( Qt::black
);
917 preview
->show(); // otherwise saver does not get correct size
918 _saver
= new KFluxScreenSaver( preview
->winId() );
919 top
->addWidget(preview
);
921 // Now that we have _saver...
922 modeW
->setCurrentIndex( _saver
->mode() ); // set before we connect
923 connect( modeW
, SIGNAL(activated(int)), _saver
, SLOT(setMode(int)) );
924 connect( this, SIGNAL(okClicked()), SLOT(slotOk()));
928 KFluxSetup::~KFluxSetup()
934 void KFluxSetup::slotHelp()
936 KMessageBox::about(this,
937 i18n("<h3>Flux 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>"),
938 QString::null
, KMessageBox::AllowLink
);
943 Ok pressed - save settings and exit
945 void KFluxSetup::slotOk()
947 KConfig
* config
= KGlobal::config();
948 config
->setGroup("Settings");
951 val
.setNum( modeW
->currentIndex() );
952 config
->writeEntry("Mode", val
);
960 //----------------------------------------------------------------------------
964 // moc Flux.h -o Flux.moc
965 // g++ -g -DUNIT_TEST Flux.cpp -I/usr/lib/qt3/include -lqt -L/usr/lib/qt3/lib -lGLU -lGL
967 #include <qapplication.h>
969 int main( int argc
, char** argv
)
971 QApplication
app( argc
, argv
);
974 w
.setDefaults( FluxWidget::Sparklers
);
975 app
.setMainWidget( &w
);