2 * Terence Welsh Screensaver - Flux
3 * http://www.reallyslick.com/
5 * Ported to KDE by Karl Robillard
6 * Copyright (C) 2002 Terence M. Welsh
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 [ ] Regular and others are messed up after Sparkler.
26 Insane seems to reset them.
41 #define PIx2 6.28318530718f
42 #define DEG2RAD 0.0174532925f
45 void hsl2rgb(float h
, float s
, float l
, float &r
, float &g
, float &b
)
48 if(h
< 0.166667){ // full red, some green
54 if(h
< 0.5){ // full green
56 if(h
< 0.333333){ // some red
57 r
= 1.0f
- ((h
- 0.166667f
) * 6.0f
);
61 b
= (h
- 0.333333f
) * 6.0f
;
66 if(h
< 0.833333){ // full blue
68 if(h
< 0.666667){ // some green
69 g
= 1.0f
- ((h
- 0.5f
) * 6.0f
);
73 r
= (h
- 0.666667f
) * 6.0f
;
77 else{ // full red, some blue
79 b
= 1.0f
- ((h
- 0.833333f
) * 6.0f
);
85 // saturation influence
86 r
= 1.0f
- (s
* (1.0f
- r
));
87 g
= 1.0f
- (s
* (1.0f
- g
));
88 b
= 1.0f
- (s
* (1.0f
- b
));
90 // luminosity influence
97 // Useful random number macros
98 // Don't forget to initialize with srand()
99 inline int myRandi(int x
){
100 return((rand() * x
) / RAND_MAX
);
102 inline float myRandf(float x
){
103 return(float(rand() * x
) / float(RAND_MAX
));
107 //----------------------------------------------------------------------------
110 // Flux context to allow many instances.
111 static FluxWidget
* _fc
= 0;
113 static int whichparticle
;
116 // This class is poorly named. It's actually a whole trail of particles.
123 float update(float *c
);
136 // Offsets are somewhat like default positions for the head of each
137 // particle trail. Offsets spread out the particle trails and keep
138 // them from all overlapping.
139 offset
[0] = cos(PIx2
* float(whichparticle
) / float(_fc
->dParticles
));
140 offset
[1] = float(whichparticle
) / float(_fc
->dParticles
) - 0.5f
;
141 offset
[2] = sin(PIx2
* float(whichparticle
) / float(_fc
->dParticles
));
144 // Initialize memory and set initial positions out of view of the camera
145 trails
= _fc
->dTrail
;
146 vertices
= new float*[ trails
];
149 for(i
=0; i
<trails
; i
++)
151 vertices
[i
] = new float[5]; // 0,1,2 = position, 3 = hue, 4 = saturation
152 vertices
[i
][0] = 0.0f
;
153 vertices
[i
][1] = 3.0f
;
154 vertices
[i
][2] = 0.0f
;
155 vertices
[i
][3] = 0.0f
;
156 vertices
[i
][4] = 0.0f
;
163 particle::~particle()
165 for(int i
=0; i
<trails
; i
++)
166 delete[] vertices
[i
];
171 float particle::update(float *c
)
175 float cx
, cy
, cz
; // Containment variables
177 static float expander
= 1.0f
+ 0.0005f
* float(_fc
->dExpansion
);
178 static float blower
= 0.001f
* float(_fc
->dWind
);
179 //static float otherxyz[3];
182 // Record old position
185 oldpos
[0] = vertices
[oldc
][0];
186 oldpos
[1] = vertices
[oldc
][1];
187 oldpos
[2] = vertices
[oldc
][2];
190 if(counter
>= _fc
->dTrail
)
193 // Here's the iterative math for calculating new vertex positions
194 // first calculate limiting terms which keep vertices from constantly
195 // flying off to infinity
196 cx
= vertices
[oldc
][0] * (1.0f
- 1.0f
/ (vertices
[oldc
][0] * vertices
[oldc
][0] + 1.0f
));
197 cy
= vertices
[oldc
][1] * (1.0f
- 1.0f
/ (vertices
[oldc
][1] * vertices
[oldc
][1] + 1.0f
));
198 cz
= vertices
[oldc
][2] * (1.0f
- 1.0f
/ (vertices
[oldc
][2] * vertices
[oldc
][2] + 1.0f
));
199 // then calculate new positions
200 vertices
[counter
][0] = vertices
[oldc
][0] + c
[6] * offset
[0] - cx
201 + c
[2] * vertices
[oldc
][1]
202 + c
[5] * vertices
[oldc
][2];
203 vertices
[counter
][1] = vertices
[oldc
][1] + c
[6] * offset
[1] - cy
204 + c
[1] * vertices
[oldc
][2]
205 + c
[4] * vertices
[oldc
][0];
206 vertices
[counter
][2] = vertices
[oldc
][2] + c
[6] * offset
[2] - cz
207 + c
[0] * vertices
[oldc
][0]
208 + c
[3] * vertices
[oldc
][1];
211 vertices
[counter
][3] = cx
* cx
+ cy
* cy
+ cz
* cz
;
212 if(vertices
[counter
][3] > 1.0f
)
213 vertices
[counter
][3] = 1.0f
;
214 vertices
[counter
][3] += c
[7];
215 // Limit the hue (0 - 1)
216 if(vertices
[counter
][3] > 1.0f
)
217 vertices
[counter
][3] -= 1.0f
;
218 if(vertices
[counter
][3] < 0.0f
)
219 vertices
[counter
][3] += 1.0f
;
221 vertices
[counter
][4] = c
[0] + vertices
[counter
][3];
222 // Limit the saturation (0 - 1)
223 if(vertices
[counter
][4] < 0.0f
)
224 vertices
[counter
][4] = -vertices
[counter
][4];
225 vertices
[counter
][4] -= float(int(vertices
[counter
][4]));
226 vertices
[counter
][4] = 1.0f
- (vertices
[counter
][4] * vertices
[counter
][4]);
228 // Bring particles back if they escape
230 if((vertices
[0][0] > 1000000000.0f
) || (vertices
[0][0] < -1000000000.0f
)
231 || (vertices
[0][1] > 1000000000.0f
) || (vertices
[0][1] < -1000000000.0f
)
232 || (vertices
[2][2] > 1000000000.0f
) || (vertices
[0][2] < -1000000000.0f
)){
233 vertices
[0][0] = myRandf(2.0f
) - 1.0f
;
234 vertices
[0][1] = myRandf(2.0f
) - 1.0f
;
235 vertices
[0][2] = myRandf(2.0f
) - 1.0f
;
239 // Draw every vertex in particle trail
242 luminosity
= _fc
->lumdiff
;
243 for(i
=0; i
<_fc
->dTrail
; i
++){
249 // assign color to particle
250 hsl2rgb(vertices
[p
][3], vertices
[p
][4], luminosity
, rgb
[0], rgb
[1], rgb
[2]);
254 if(_fc
->dGeometry
== 1) // Spheres
255 glTranslatef(vertices
[p
][0], vertices
[p
][1], vertices
[p
][2]);
256 else{ // Points or lights
257 depth
= _fc
->cosCameraAngle
* vertices
[p
][2] - _fc
->sinCameraAngle
* vertices
[p
][0];
258 glTranslatef(_fc
->cosCameraAngle
* vertices
[p
][0] + _fc
->sinCameraAngle
259 * vertices
[p
][2], vertices
[p
][1], depth
);
261 if(_fc
->dGeometry
){ // Spheres or lights
262 switch(_fc
->dTrail
- growth
){
264 glScalef(0.259f
, 0.259f
, 0.259f
);
267 glScalef(0.5f
, 0.5f
, 0.5f
);
270 glScalef(0.707f
, 0.707f
, 0.707f
);
273 glScalef(0.866f
, 0.866f
, 0.866f
);
276 glScalef(0.966f
, 0.966f
, 0.966f
);
279 switch(_fc
->dGeometry
){
281 switch(_fc
->dTrail
- growth
){
283 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.001036f
));
286 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.002f
));
289 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.002828f
));
292 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.003464f
));
295 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.003864f
));
298 glPointSize(float(_fc
->dSize
* (depth
+ 200.0f
) * 0.004f
));
301 glVertex3f(0.0f
,0.0f
,0.0f
);
309 vertices
[p
][0] *= expander
;
310 vertices
[p
][1] *= expander
;
311 vertices
[p
][2] *= expander
;
312 vertices
[p
][2] += blower
;
313 luminosity
+= _fc
->lumdiff
;
316 // Find distance between new position and old position and return it
317 oldpos
[0] -= vertices
[counter
][0];
318 oldpos
[1] -= vertices
[counter
][1];
319 oldpos
[2] -= vertices
[counter
][2];
320 return(float(sqrt(oldpos
[0] * oldpos
[0] + oldpos
[1] * oldpos
[1] + oldpos
[2] * oldpos
[2])));
324 // This class is a set of particle trails and constants that enter
325 // into their equations of motion.
336 float c
[NUMCONSTS
]; // constants
337 float cv
[NUMCONSTS
]; // constants' change velocities
338 int currentSmartConstant
;
347 particles
= new particle
[_fc
->dParticles
];
350 float instability
= _fc
->dInstability
;
352 for(i
=0; i
<NUMCONSTS
; i
++)
354 c
[i
] = myRandf(2.0f
) - 1.0f
;
355 cv
[i
] = myRandf(0.000005f
* instability
* instability
)
356 + 0.000001f
* instability
* instability
;
359 currentSmartConstant
= 0;
374 // randomize constants
378 for(i
=0; i
<NUMCONSTS
; i
++)
379 c
[i
] = myRandf(2.0f
) - 1.0f
;
380 int temp
= 101 - _fc
->dRandomize
;
382 randomize
= temp
+ myRandi(temp
);
387 for(i
=0; i
<NUMCONSTS
; i
++){
399 // update all particles in this flux field
401 for(i
=0; i
<_fc
->dParticles
; i
++)
402 dist
= particles
[i
].update(c
);
404 // use dist from last particle to activate smart constants
407 const float upper
= 0.4f
;
408 const float lower
= 0.2f
;
410 if(dist
> upper
&& dist
> oldDistance
)
412 if(dist
< lower
&& dist
< oldDistance
)
415 cv
[currentSmartConstant
] = -cv
[currentSmartConstant
];
416 currentSmartConstant
++;
417 if(currentSmartConstant
>= _fc
->dSmart
)
418 currentSmartConstant
= 0;
425 //----------------------------------------------------------------------------
428 FluxWidget::FluxWidget( QWidget
* parent
)
429 : QGLWidget(parent
), _fluxes(0)
431 setDefaults( Regular
);
433 _frameTime
= 1000 / 60;
434 _timer
= new QTimer( this );
435 connect( _timer
, SIGNAL(timeout()), this, SLOT(nextFrame()) );
439 FluxWidget::~FluxWidget()
446 void FluxWidget::paintGL()
451 if(dBlur
) // partially
454 glGetIntegerv(GL_VIEWPORT
, viewport
);
455 float viewRatio
= float(viewport
[2]) / float(viewport
[3]);
457 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
459 glDisable(GL_DEPTH_TEST
);
460 glColor4f(0.0f
, 0.0f
, 0.0f
, 0.5f
- (float(sqrt(sqrt(double(dBlur
)))) * 0.15495f
));
461 glBegin(GL_TRIANGLE_STRIP
);
462 glVertex3f(-3.0f
* viewRatio
, -3.0f
, 0.0f
);
463 glVertex3f(3.0f
* viewRatio
, -3.0f
, 0.0f
);
464 glVertex3f(-3.0f
* viewRatio
, 3.0f
, 0.0f
);
465 glVertex3f(3.0f
* viewRatio
, 3.0f
, 0.0f
);
470 glClear(GL_COLOR_BUFFER_BIT
);
473 cameraAngle
+= 0.01f
* float(dRotation
);
474 if(cameraAngle
>= 360.0f
)
475 cameraAngle
-= 360.0f
;
476 if(dGeometry
== 1) // Only rotate for spheres
477 glRotatef(cameraAngle
, 0.0f
, 1.0f
, 0.0f
);
480 cosCameraAngle
= cos(cameraAngle
* DEG2RAD
);
481 sinCameraAngle
= sin(cameraAngle
* DEG2RAD
);
484 // set up blend modes for rendering particles
487 case 0: // Blending for points
488 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
490 glEnable(GL_POINT_SMOOTH
);
491 glHint(GL_POINT_SMOOTH_HINT
, GL_NICEST
);
494 case 1: // No blending for spheres, but we need z-buffering
496 glEnable(GL_DEPTH_TEST
);
497 glClear(GL_DEPTH_BUFFER_BIT
);
500 case 2: // Blending for lights
501 glBlendFunc(GL_ONE
, GL_ONE
);
510 for(i
=0; i
<dFluxes
; i
++)
518 void FluxWidget::resizeGL( int w
, int h
)
520 glViewport(0, 0, w
, h
);
522 glMatrixMode(GL_PROJECTION
);
524 gluPerspective(100.0, (float) w
/ (float) h
, 0.01, 200);
525 glTranslatef(0.0, 0.0, -2.5);
526 glMatrixMode(GL_MODELVIEW
);
531 // Window initialization
532 void FluxWidget::initializeGL()
534 //resizeGL( width(), height() );
537 _timer
->setSingleShot(true);
538 _timer
->start( _frameTime
);
543 void FluxWidget::keyPressEvent( QKeyEvent
* e
)
545 if( e
->key() == Qt::Key_0
) { setDefaults( 0 ); updateParameters(); }
546 if( e
->key() == Qt::Key_1
) { setDefaults( 1 ); updateParameters(); }
547 if( e
->key() == Qt::Key_2
) { setDefaults( 2 ); updateParameters(); }
548 if( e
->key() == Qt::Key_3
) { setDefaults( 3 ); updateParameters(); }
549 if( e
->key() == Qt::Key_4
) { setDefaults( 4 ); updateParameters(); }
550 if( e
->key() == Qt::Key_5
) { setDefaults( 5 ); updateParameters(); }
555 void FluxWidget::nextFrame()
558 _timer
->setSingleShot(true);
559 _timer
->start( _frameTime
);
564 May be called at any time - makes no OpenGL calls.
566 void FluxWidget::setDefaults( int which
)
660 Called after dGeometry, dTrail, or dFluxes is changed
661 (such as with setDefaults).
663 void FluxWidget::updateParameters()
668 srand((unsigned)time(NULL
));
669 rand(); rand(); rand(); rand(); rand();
674 glEnable(GL_CULL_FACE
);
675 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
676 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
680 glDisable(GL_LIGHTING
);
681 glDisable(GL_COLOR_MATERIAL
);
682 glDisable(GL_TEXTURE_2D
);
684 glEnable(GL_POINT_SMOOTH
);
685 //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
687 else if(dGeometry
== 1) // Spheres and their lighting
689 glNewList(1, GL_COMPILE
);
690 GLUquadricObj
* qobj
= gluNewQuadric();
691 gluSphere(qobj
, 0.005f
* dSize
, dComplexity
+ 2, dComplexity
+ 1);
692 gluDeleteQuadric(qobj
);
695 glDisable(GL_TEXTURE_2D
);
696 glEnable(GL_LIGHTING
);
699 float ambient
[4] = {0.0f
, 0.0f
, 0.0f
, 0.0f
};
700 float diffuse
[4] = {1.0f
, 1.0f
, 1.0f
, 0.0f
};
701 float specular
[4] = {1.0f
, 1.0f
, 1.0f
, 0.0f
};
702 float position
[4] = {500.0f
, 500.0f
, 500.0f
, 0.0f
};
704 glLightfv(GL_LIGHT0
, GL_AMBIENT
, ambient
);
705 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, diffuse
);
706 glLightfv(GL_LIGHT0
, GL_SPECULAR
, specular
);
707 glLightfv(GL_LIGHT0
, GL_POSITION
, position
);
708 glEnable(GL_COLOR_MATERIAL
);
709 glColorMaterial(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
711 else if(dGeometry
== 2) // Init lights
713 for(i
=0; i
<LIGHTSIZE
; i
++)
715 for(j
=0; j
<LIGHTSIZE
; j
++)
717 x
= float(i
- LIGHTSIZE
/ 2) / float(LIGHTSIZE
/ 2);
718 y
= float(j
- LIGHTSIZE
/ 2) / float(LIGHTSIZE
/ 2);
719 temp
= 1.0f
- float(sqrt((x
* x
) + (y
* y
)));
724 lightTexture
[i
][j
] = (unsigned char) (255.0f
* temp
* temp
);
728 glDisable(GL_LIGHTING
);
729 glDisable(GL_COLOR_MATERIAL
);
731 glEnable(GL_TEXTURE_2D
);
732 glBindTexture(GL_TEXTURE_2D
, 1);
733 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
734 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
735 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
736 glTexImage2D(GL_TEXTURE_2D
, 0, 1, LIGHTSIZE
, LIGHTSIZE
, 0,
737 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, lightTexture
);
739 temp
= float(dSize
) * 0.005f
;
740 glNewList(1, GL_COMPILE
);
741 glBindTexture(GL_TEXTURE_2D
, 1);
742 glBegin(GL_TRIANGLES
);
743 glTexCoord2f(0.0f
, 0.0f
);
744 glVertex3f(-temp
, -temp
, 0.0f
);
745 glTexCoord2f(1.0f
, 0.0f
);
746 glVertex3f(temp
, -temp
, 0.0f
);
747 glTexCoord2f(1.0f
, 1.0f
);
748 glVertex3f(temp
, temp
, 0.0f
);
749 glTexCoord2f(0.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
, 1.0f
);
754 glVertex3f(-temp
, temp
, 0.0f
);
759 // Initialize luminosity difference
760 lumdiff
= 1.0f
/ float(dTrail
);
764 _fluxes
= new flux
[dFluxes
];
768 //----------------------------------------------------------------------------
777 // libkscreensaver interface
778 class KFluxSaverInterface
: public KScreenSaverInterface
783 virtual KAboutData
* aboutData() {
784 return new KAboutData( "kflux.kss", 0, ki18n( "Flux" ), "1.0", ki18n( "Flux" ) );
788 virtual KScreenSaver
* create( WId id
)
790 return new KFluxScreenSaver( id
);
793 virtual QDialog
* setup()
795 return new KFluxSetup
;
799 int main( int argc
, char *argv
[] )
801 KFluxSaverInterface kss
;
802 return kScreenSaverMain( argc
, argv
, kss
);
806 //----------------------------------------------------------------------------
809 KFluxScreenSaver::KFluxScreenSaver( WId id
) : KScreenSaver( id
)
811 _flux
= new FluxWidget
;
820 KFluxScreenSaver::~KFluxScreenSaver()
825 static int filterRandom( int n
)
827 if( (n
< 0) || (n
>= FluxWidget::DefaultModes
) )
829 srand((unsigned)time(NULL
));
830 n
= rand() % FluxWidget::DefaultModes
;
836 void KFluxScreenSaver::readSettings()
838 KConfigGroup
config(KGlobal::config(), "Settings");
840 _mode
= config
.readEntry( "Mode", (int)FluxWidget::Regular
);
841 _flux
->setDefaults( filterRandom(_mode
) );
846 Any invalid mode will select one at random.
848 void KFluxScreenSaver::setMode( int id
)
851 _flux
->setDefaults( filterRandom(id
) );
852 _flux
->updateParameters();
856 //----------------------------------------------------------------------------
860 #include <qpushbutton.h>
862 #include <qcombobox.h>
863 #include <kmessagebox.h>
866 static const char* defaultText
[] =
868 I18N_NOOP( "Regular" ),
869 I18N_NOOP( "Hypnotic" ),
870 I18N_NOOP( "Insane" ),
871 I18N_NOOP( "Sparklers" ),
872 I18N_NOOP( "Paradigm" ),
873 I18N_NOOP( "Galactic" ),
874 I18N_NOOP( "(Random)" ),
879 KFluxSetup::KFluxSetup( QWidget
* parent
)
882 setButtonText( Help
, i18n( "A&bout" ) );
883 setButtons(Ok
|Cancel
|Help
);
884 setDefaultButton(Ok
);
886 showButtonSeparator(true);
887 QWidget
*main
= new QWidget(this);
890 QHBoxLayout
* top
= new QHBoxLayout( main
);
891 top
->setSpacing( spacingHint() );
892 QVBoxLayout
* leftCol
= new QVBoxLayout
;
893 top
->addLayout( leftCol
);
896 QLabel
* label
= new QLabel( i18n("Mode:"), main
);
897 leftCol
->addWidget( label
);
899 modeW
= new QComboBox( main
);
901 while (defaultText
[i
])
902 modeW
->addItem( i18n(defaultText
[i
++]) );
903 leftCol
->addWidget( modeW
);
905 leftCol
->addStretch();
909 preview
= new QWidget( main
);
910 preview
->setFixedSize( 220, 165 );
913 palette
.setColor( preview
->backgroundRole(), Qt::black
);
914 preview
->setPalette( palette
);
916 preview
->show(); // otherwise saver does not get correct size
917 _saver
= new KFluxScreenSaver( preview
->winId() );
918 top
->addWidget(preview
);
920 // Now that we have _saver...
921 modeW
->setCurrentIndex( _saver
->mode() ); // set before we connect
922 connect( modeW
, SIGNAL(activated(int)), _saver
, SLOT(setMode(int)) );
923 connect( this, SIGNAL(okClicked()), SLOT(slotOk()));
924 connect(this, SIGNAL(helpClicked()),SLOT(slotHelp()));
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(), KMessageBox::AllowLink
);
943 Ok pressed - save settings and exit
945 void KFluxSetup::slotOk()
947 KConfigGroup
config(KGlobal::config(), "Settings");
950 val
.setNum( modeW
->currentIndex() );
951 config
.writeEntry("Mode", val
);
959 //----------------------------------------------------------------------------
963 // moc Flux.h -o Flux.moc
964 // g++ -g -DUNIT_TEST Flux.cpp -I/usr/lib/qt3/include -lqt -L/usr/lib/qt3/lib -lGLU -lGL
966 #include <qapplication.h>
968 int main( int argc
, char** argv
)
970 QApplication
app( argc
, argv
);
973 w
.setDefaults( FluxWidget::Sparklers
);
974 app
.setMainWidget( &w
);