Deprecated--
[kdeartwork.git] / kscreensaver / kdesavers / firesaver.cpp
blob983dfb92aec34fe2706a0a3ac9307e70e2a62c52
1 // This file is part of KFireSaver3D.
3 // KFireSaver3D is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or
6 // (at your option) any later version.
8 // KFireSaver3D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with KFireSaver3D; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 // Author: Enrico Ros, based on the great work of David Sansome (kfiresaver)
19 // Email: asy@libero.it
21 #include <math.h>
22 #include <sys/time.h>
23 #include <kconfig.h>
24 #include <kdebug.h>
25 #include <kstandarddirs.h>
26 #include <klocale.h>
27 #include <kurl.h>
28 #include <kiconloader.h>
29 #include <kmessagebox.h>
30 #include <arts/kmedia2.h>
31 #include <arts/kplayobject.h>
32 #include <arts/kplayobjectfactory.h>
34 #include "firesaversetup.h"
35 #include "firesaverparticle.h"
36 #include "firesaverwriter.h"
37 #include "firesaver.h"
40 /* Factory code for KScreensaver begins *here* *\
41 \* */
43 #include <kscreensaver.h>
44 #include <kdialog.h>
45 //Added by qt3to4:
46 #include <QTimerEvent>
48 class KFireSaverKSS : public KScreenSaver
50 public:
51 KFireSaverKSS( WId id )
52 : KScreenSaver( id )
54 setBackgroundColor( black );
55 erase();
56 saver = new KFireSaver;
57 embed(saver);
58 saver->show();
61 ~KFireSaverKSS()
63 delete saver;
66 private:
67 KFireSaver* saver;
70 class KFireSaverSetupKDB : public KDialog
72 public:
73 KFireSaverSetupKDB( QWidget* parent = 0, const char* name = 0 )
74 : KDialog( parent, name, true, i18n("Setup Screen Saver"),
75 Ok | Cancel | Help, Ok, true )
77 setup = new KFireSaverSetup( this );
78 setMainWidget( setup );
79 setButtonText( KDialog::Help, i18n( "A&bout" ) );
82 private slots:
83 void slotHelp()
85 KMessageBox::about(this, i18n("<h3>KFireSaver 3D 1.0</h3>\n<p>TEST Koral - Enrico Ros::2004</p>") );
87 void slotOk()
89 setup->writeConfig();
90 accept();
93 private:
94 KFireSaverSetup * setup;
97 class KFireSaverInterface : public KScreenSaverInterface
101 public:
102 virtual KAboutData* aboutData() {
103 return new KAboutData( "kfiresaver.kss", I18N_NOOP( "Fireworks 3D (GL)" ), "0.7", I18N_NOOP( "Fireworks 3D (GL)" ) );
107 virtual KScreenSaver* create( WId id )
109 return new KFireSaverKSS( id );
112 virtual QDialog* setup()
114 return new KFireSaverSetupKDB;
118 int main( int argc, char *argv[] )
120 KFireSaverInterface kss;
121 return kScreenSaverMain( argc, argv, kss );
124 /* *\
125 \* Factory code for KScreensaver ends *here* */
128 KFireSaver :: KFireSaver( QWidget *parent, const char *name )
129 : QGLWidget( parent, name )
131 // set random seed to initialize drand48() calls
132 timeval tv;
133 gettimeofday(&tv,NULL);
134 srand48( (long)tv.tv_usec );
136 readConfig();
138 particleList.setAutoDelete(true);
139 starList.setAutoDelete(true);
140 imageList.setAutoDelete(true);
141 playObjectList.setAutoDelete(true);
143 //initialize openGL context before managing GL calls
144 makeCurrent();
145 loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle.png"), particleTexture );
146 loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle_flare.png"), flareTexture );
147 loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle_diastar.png"), diastarTexture );
148 starTexture = particleTexture;
150 //generate stars
151 if (parameters.enableStars) {
152 int number = parameters.starsNumber + 1;
153 number *= 10 * number;
154 for (int i=0 ; i<number ; i++)
156 Particle * star = new Particle( Particle::StarParticle );
157 star->initializeValues();
158 star->texture = starTexture;
159 if (parameters.enableStarGradient)
161 GLfloat red = star->colour[0],
162 green = star->colour[1],
163 blue = star->colour[2],
164 tint = 0.5 + star->ypos / FIELDWIDTH,
165 merge = 0.3 + DRAND*0.7,
166 mergen = 1 - merge;
167 star->colour[0] = red * merge + (1.0-tint) * mergen;
168 star->colour[1] = green * merge + tint * mergen;
169 star->colour[2] = blue * merge + tint * mergen;
171 starList.append( star );
175 //generate bottom fire
176 if (parameters.enableBottomFire) {
177 float cRed = (float)parameters.bottomFireColor.red() / 255.0,
178 cGreen = (float)parameters.bottomFireColor.green() / 255.0,
179 cBlue = (float)parameters.bottomFireColor.blue() / 255.0;
180 for (int i=0 ; i<NUMBER_OF_FIREPARTICLES ; i++)
182 Particle* particle = new Particle( Particle::FireParticle );
183 particle->initializeValues();
184 particle->texture = particleTexture;
185 particle->zspeed *= 4.0;
186 particle->colour[0] = cRed * (0.6 + 0.4*DRAND);
187 particle->colour[1] = cGreen * (0.6 + 0.4*DRAND);
188 particle->colour[2] = cBlue * (0.6 + 0.4*DRAND);
189 particleList.append(particle);
193 //get sound files
194 if (parameters.enableSound) {
195 sound_explosion = locate("data","kfiresaver/kfs_explode.ogg");
196 sound_debris = locate("data","kfiresaver/kfs_debris.ogg");
199 //create the writer class that manages flying writings.
200 if ( parameters.enableWritings )
201 writer = new Writer("kfs_letters.desc");
203 showp.forceBicolour =
204 showp.forceColour =
205 showp.forcePower =
206 showp.forceType = false;
207 showp.timeStamp = 0.0;
208 startTimer(MSECPERIOD);
210 //force initialization of "show" variables for the first time
211 timerEvent(NULL);
215 KFireSaver :: ~KFireSaver()
217 freeTexture( particleTexture );
218 freeTexture( starTexture );
219 particleList.clear();
220 starList.clear();
221 imageList.clear();
222 playObjectList.clear();
223 if ( parameters.enableWritings )
224 delete writer;
228 void KFireSaver :: initializeGL()
230 glDisable(GL_DEPTH_TEST);
231 glEnable(GL_BLEND);
232 glBlendFunc(GL_SRC_ALPHA,GL_ONE);
233 glShadeModel( GL_SMOOTH );
235 resizeGL( 640, 480 );
239 void KFireSaver :: resizeGL( int width, int height )
241 glViewport( 0, 0, width, height );
243 glMatrixMode(GL_PROJECTION);
244 glLoadIdentity();
245 glOrtho( -FIELDW_2, FIELDW_2, -FIELDW_2, FIELDW_2, 5.0, 60.0 );
247 glMatrixMode(GL_MODELVIEW);
248 glLoadIdentity();
250 float ratio = (float)width / (float)height,
251 numH = 750 - 90 * parameters.particleSize,
252 numW = 1000 - 120 * parameters.particleSize;
253 if ( ratio >= (4.0/3.0) ) {
254 unitX = FIELDWIDTH / (numH * ratio);
255 unitY = FIELDWIDTH / numH;
256 } else {
257 unitX = FIELDWIDTH / numW;
258 unitY = FIELDWIDTH / (numW / ratio);
261 timeval tv;
262 gettimeofday(&tv,NULL);
263 timeStampFrame = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
265 firstGLFrame = true;
269 void KFireSaver :: paintGL ()
270 /* Main procedure. It does the following:
271 - calculate time diff between current and previous frame
272 - clear the color buffer
273 - simple render of stars
274 - advanced render of particles
275 - render
276 - update physics based on time difference
277 - check die/change conditions
278 - call to explode_firework if a leader dies
279 - if random -> start a new firework
280 - if random -> explode a penquin or kde logo
283 /* calculate TIME ELAPSED between current and previous frame */
285 timeval tv;
286 gettimeofday(&tv,NULL);
287 double timeCurrent = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
288 double timeDiff = (MSECPERIOD / 1000.0);
289 if (parameters.enableRealtime)
291 timeDiff = timeCurrent - timeStampFrame;
292 timeStampFrame = timeCurrent;
293 timeDiff = (timeDiff > 0.5) ? 0.5 : (timeDiff < 0.005) ? 0.005 : timeDiff;
296 /* CLEAR SCREEN: to do it there are 2 ways */
298 glLoadIdentity();
299 glTranslatef( 0, 0, -50.0 );
300 glDisable( GL_TEXTURE_2D );
302 if ( !parameters.enableFade || firstGLFrame )
303 { // quick - clear the OpenGL color buffer
304 glClearColor( 0.0, 0.0, 0.0, 1.0 );
305 glClear( GL_COLOR_BUFFER_BIT );
306 firstGLFrame = false;
308 else
309 { // good looking
310 /* superpose a semi-transparent black rectangle so we
311 can see a sort of 'tail' for each particle drawn. */
312 const GLfloat conv_tab[10] = {
313 0.50, 0.33, 0.22, 0.15, 0.10,
314 0.07, 0.05, 0.03, 0.02, 0.01 };
315 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
316 glColor4f(0.0,0.0,0.0, conv_tab[parameters.fadeAmount]);
317 glBegin( GL_TRIANGLE_STRIP );
318 glVertex2f( FIELDW_2, FIELDW_2 );
319 glVertex2f( -FIELDW_2, FIELDW_2 );
320 glVertex2f( FIELDW_2, -FIELDW_2 );
321 glVertex2f( -FIELDW_2, -FIELDW_2 );
322 glEnd();
323 glBlendFunc(GL_SRC_ALPHA,GL_ONE);
326 /* render STARS */
328 if (parameters.enableStars) {
329 if ( starTexture ) {
330 glEnable( GL_TEXTURE_2D );
331 glBindTexture( GL_TEXTURE_2D, currentTexture = starTexture );
332 } else
333 glDisable( GL_TEXTURE_2D );
335 glBegin( GL_QUADS );
336 bool flickers = parameters.enableStarFlickering;
337 float alpha = flickers ? 0.5 : 1.0;
338 Particle * star = starList.first();
339 for (; star; star = starList.next())
341 if (flickers && DRAND<0.6)
342 continue;
344 GLfloat sizeX = star->pixelSize * unitX,
345 sizeY = star->pixelSize * unitY,
346 pLeft = star->xpos - sizeX,
347 pRight = star->xpos + sizeX,
348 pTop = star->ypos + sizeY,
349 pBottom = star->ypos - sizeY;
350 glColor4f(star->colour[0], star->colour[1], star->colour[2], alpha);
351 glTexCoord2f( 0, 0 ); // Bottom Left
352 glVertex2f( pLeft, pBottom );
353 glTexCoord2f( 0, 1 ); // Top Left
354 glVertex2f( pLeft, pTop );
355 glTexCoord2f( 1, 1 ); // Top Right
356 glVertex2f( pRight, pTop );
357 glTexCoord2f( 1, 0 ); // Bottom Right
358 glVertex2f( pRight, pBottom );
360 glEnd();
363 /* render FIREWORKS */
365 glBegin( GL_QUADS );
366 bool playedExplodeSound = false;
367 bool flashedScreen = false;
368 Particle * particle = particleList.first();
369 for (; particle; particle = particleList.next())
371 //bind the texture for current particle (if not already bound)
372 if ( !particle->texture ) {
373 glEnd();
374 glDisable( GL_TEXTURE_2D );
375 glBegin( GL_QUADS );
376 currentTexture = 0;
377 } else if ( particle->texture != currentTexture ) {
378 glEnd();
379 glEnable( GL_TEXTURE_2D );
380 glBindTexture( GL_TEXTURE_2D, currentTexture = particle->texture );
381 glBegin( GL_QUADS );
384 //perspective projection (done by hand to make it funnier than opengl's :-)
385 float mfactor = PERSP_MAG_FACTOR * particle->ypos;
386 if ( mfactor < -246.0 ) {
387 particleList.remove();
388 particleList.prev();
389 continue;
391 float sfactor = 256.0 / (256.0 + mfactor),
392 posx = sfactor * particle->xpos,
393 posy = sfactor * particle->zpos - 4.0;
394 //size computation (magnify if enableMegaFlares is set)
395 if ( parameters.enableMegaFlares ) {
396 mfactor = parameters.megaFlares*particle->ypos;
397 if ( mfactor < -255.0 || mfactor > 512.0 ) {
398 particleList.remove();
399 particleList.prev();
400 continue;
402 sfactor = 256.0 / (256.0 + mfactor);
403 if ( sfactor > 64 )
404 sfactor = 76.8 - sfactor / 5.0;
406 float size = sfactor * particle->pixelSize,
407 sizeX = size * unitX,
408 sizeY = size * unitY;
410 //determine brightness (alpha component) for the particle
411 if ( particle->useLife ) {
412 float life = particle->life,
413 startLife = particle->startLife;
414 //bright changes with the following curve: "2*k - k^2" (or "k(2-k)")
415 if ( life > startLife )
416 particle->colour[3] = startLife + 1 - life;
417 else
418 particle->colour[3] = life / startLife;
419 //apply flickering if enabled
420 if (particle->flicker < 0) {
421 particle->colour[3] = 0.2;
422 if (++particle->flicker >= 0)
423 particle->flicker = FLICKER_FRAMES_DELAY;
424 } else if (particle->flicker > 0) {
425 if ( life <= startLife )
426 particle->colour[3] = 1.0;
427 if (--particle->flicker <= 0)
428 particle->flicker = -FLICKER_FRAMES_DELAY;
430 glColor4fv( particle->colour );
431 } else
432 glColor3fv( particle->colour );
434 //draw particle
435 float pLeft = posx - sizeX,
436 pTop = posy + sizeY,
437 pRight = posx + sizeX,
438 pBottom = posy - sizeY;
439 glTexCoord2f( 0, 0 ); // Bottom Left
440 glVertex2f( pLeft, pBottom );
441 glTexCoord2f( 0, 1 ); // Top Left
442 glVertex2f( pLeft, pTop );
443 glTexCoord2f( 1, 1 ); // Top Right
444 glVertex2f( pRight, pTop );
445 glTexCoord2f( 1, 0 ); // Bottom Right
446 glVertex2f( pRight, pBottom );
448 //phisically update parameters of the particle
449 particle->updateParameters( timeDiff );
451 //check for particle death / explosion
452 switch (particle->particleType)
454 //a Fireparticle is restarted when in right conditions
455 case Particle::FireParticle:
456 if ( posx < -FIELDW_2 || posx > FIELDW_2 ||
457 (particle->zpos < -10.0 && posy < -FIELDW_2) )
459 particle->initializeValues();
460 if ( DRAND > 0.9995 )
461 particle->zspeed *= 4;
463 break;
465 //a leader explodes when his z speed drops to zero
466 //or, if it uses life, at death
467 case Particle::FireWorkLeaderParticle:
468 if ((particle->zspeed <= 0.0f && !particle->useLife) ||
469 (particle->useLife && particle->life <= 0.0) )
471 // play sound if enabled (and once per frame)
472 if (parameters.enableSound && !playedExplodeSound)
474 playSound(sound_explosion);
475 playedExplodeSound = true;
477 // flash screen if enabled
478 if (parameters.enableFlash && !flashedScreen) {
479 glEnd();
480 glDisable( GL_TEXTURE_2D );
481 glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
482 glBegin( GL_TRIANGLE_STRIP );
483 glVertex2f( FIELDW_2, FIELDW_2 );
484 glVertex2f( -FIELDW_2, FIELDW_2 );
485 glVertex2f( FIELDW_2, -FIELDW_2 );
486 glVertex2f( -FIELDW_2, -FIELDW_2 );
487 glEnd();
488 if ( particleTexture )
489 glEnable( GL_TEXTURE_2D );
490 glBegin( GL_QUADS );
491 flashedScreen = true;
493 // generating children and removing parent
494 int elementIndex = particleList.at();
495 explodeFirework(particle);
496 particleList.remove(elementIndex);
497 particleList.prev();
498 } else if ( parameters.enableTrails && DRAND < 0.4 ) {
499 // leave trail behind the particle (it'a small and slow red debris)
500 Particle * p = new Particle( Particle::FireWorkDebrisParticle );
501 p->initializeValues( 0, particle, 1, 1 );
502 p->texture = particleTexture;
503 p->xspeed /= 4;
504 p->yspeed /= 4;
505 p->zspeed /= 8;
506 p->zacc /= 4;
507 p->pixelSize = 2;
508 p->colour[0] /= 2;
509 int elementIndex = particleList.at();
510 particleList.append( p );
511 particleList.at( elementIndex );
513 break;
515 //remove if dead or outside field
516 default:
517 if (particle->life <= 0.0 || posx<-FIELDW_2 || posx>FIELDW_2 || posy<-FIELDW_2) {
518 particleList.remove();
519 particleList.prev();
521 break;
524 glEnd();
526 /* render WRITINGS */
528 if ( parameters.enableWritings )
530 int chance = (int) (1000.0 * DRAND);
531 if ( !chance ) {
532 static const QString someStrings[] = {
533 i18n("www.kde.org"),
534 i18n("Conquer your desktop!"),
535 i18n("KFIRESAVER 3D"),
536 i18n("Thank you for using KDE"),
538 int n = (int)(4.0 * DRAND);
539 writer->spawnWords( someStrings[n], Writer::Fun1 );
541 writer->render( timeDiff );
544 /* generate a new FIREWORK_LEADER */
546 int random = (int) ((float)parameters.fireworksFrequency * DRAND);
547 if (showp.ShowType == Show)
549 //double the chances ('frequency') to raise a new leaderParticle
550 //but envelop it under a sine function
551 float step = (showp.timeStamp - timeCurrent) / showp.timeGap;
552 if (DRAND > sin(M_PI*step))
553 random = -1;
554 if (showp.type == AngelHairs && DRAND < 0.5)
555 random = -1;
557 if ( !random )
559 Particle * particle = new Particle( Particle::FireWorkLeaderParticle );
560 particle->initializeValues();
561 particle->texture = flareTexture;
562 particleList.append( particle );
565 /* explode a logo */
567 int logoImages = imageList.count();
568 if ( logoImages > 0 ) {
569 random = (int) (parameters.logoFrequency * logoImages * 200.0 * DRAND);
570 if ( random < logoImages )
572 if (parameters.enableFlash && !flashedScreen) {
573 glDisable( GL_TEXTURE_2D );
574 glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
575 glBegin( GL_TRIANGLE_STRIP );
576 glVertex2f( FIELDW_2, FIELDW_2 );
577 glVertex2f( -FIELDW_2, FIELDW_2 );
578 glVertex2f( FIELDW_2, -FIELDW_2 );
579 glVertex2f( -FIELDW_2, -FIELDW_2 );
580 glEnd();
582 burnLogo( imageList.at(random) );
588 int KFireSaver :: pickColour()
590 int color = (int) (DRAND * parameters.colorsCount);
591 return parameters.colorsT[ color ];
595 KFireSaver :: enumFireworkType KFireSaver :: pickType()
597 int type = (int) (DRAND * parameters.typesCount);
598 return parameters.typesT[ type ];
602 void KFireSaver :: explodeFirework(Particle* leaderParticle)
604 GLfloat displace[3] = {0.0,0.0,0.0};
605 float tmp1 = 0.0, tmp2 = 0.0, tmp3 = 0.0, tmp4 = 0.0, tmp5 = 0.0;
607 // color of exploded particles
608 bool bicolor = parameters.enableCombos && (showp.forceBicolour || DRAND > 0.95),
609 flickers = false;
610 int cscheme = showp.forceColour ? showp.colour : pickColour(),
611 cscheme2 = showp.forceColour ? showp.colourSec : pickColour();
613 // randomize type of exploding firework
614 enumFireworkType fwType =
615 showp.forceType ? (enumFireworkType) showp.type : pickType();
617 // other options for generated particles
618 int number = (int) ((DRAND + DRAND) * 150.0);
619 float power = showp.forcePower ?
620 showp.powerEnvelop * (0.8 + 0.3*DRAND) :
621 DRAND * 11.0 + 2.0,
622 powermin = DRAND * power;
624 // now some rules ...
625 //a splitter can't split up more than 2 times
626 if (fwType == Splitter && leaderParticle->explosionsDepth > 1) {
627 if (parameters.typesCount == 1)
628 return;
629 if (showp.forceType)
630 fwType = showp.typeSec;
631 if (fwType == Splitter)
632 while ( (fwType = pickType()) == Splitter );
635 // PRE-ADJUST parameters for the firework we're creating
636 switch ( fwType )
638 //no need to handle this. it's the default configuration.
639 case Sphere:
640 break;
642 //explosion whithout emitting particles, only a flash
643 case NoFW:
644 number = 1;
645 power = powermin = 0;
646 break;
648 //splits up into 'number' orange pieces. tmp1 holds base_life
649 case Splitter:
650 cscheme = showp.forceColour ? showp.colour : 1;
651 bicolor = false;
652 number = 3 + (int) (DRAND * 4);
653 power /= 2.0;
654 powermin = power / 2.0;
655 tmp1 = 0.4 + DRAND * 1.5;
656 break;
658 //randomize a couple of angles (phi - theta) for exploding circle
659 case BiCircle:
660 number *= 2;
661 case Circle:
662 power = DRAND * 5.0 + 4.0;
663 tmp1 = DRAND * M_PI;
664 tmp2 = DRAND * M_PI;
665 tmp4 = cos(tmp2); //c2
666 tmp3 = sin(tmp2); //s2
667 tmp2 = cos(tmp1); //c1
668 tmp1 = sin(tmp1); //s1
669 break;
671 //cascade of flickering orange particles
672 case AngelHairs:
673 cscheme = showp.forceColour ? showp.colour : 1;
674 bicolor = false;
675 flickers = true;
676 power = 0.8 + DRAND * 1.9;
677 powermin = DRAND*0.5;
678 number = 100 + (int)(DRAND * 150);
679 displace[0] = -leaderParticle->xspeed/2;
680 displace[1] = -leaderParticle->yspeed/2;
681 displace[2] = power;
682 break;
684 //behave as a standard spherical firework
685 case Spirals:
686 break;
688 //not yet implemented, suppressing particles
689 case SuperNova:
690 case NoRender:
691 number = 0;
692 break;
695 //limit number of particles as we are getting to the capacity saturation
696 float currentParticles = (float) particleList.count();
697 const float particleCapacity = 15000;
698 const float particleGap = 8000;
699 if ( number > 10 && currentParticles > (particleCapacity - particleGap) )
701 //NoFW, Splitter and NoRender aren't limited.
702 number = (int)( (float)number * (particleCapacity - currentParticles) / particleGap );
703 if ( number < 10 )
704 number = 0;
707 int newExplosionsDepth = leaderParticle->explosionsDepth + 1;
708 for (int i=0 ; i<number ; i++)
710 Particle * particle;
711 if ( fwType == Spirals )
712 particle = new TurningParticle( Particle::FireWorkDebrisParticle );
713 else
714 particle = new Particle( Particle::FireWorkDebrisParticle );
716 particle->initializeValues (
717 bicolor && (i>number/2) ? cscheme2 : cscheme,
718 leaderParticle, powermin, power,
719 flickers, displace );
720 particle->texture = particleTexture;
721 particle->explosionsDepth = newExplosionsDepth;
723 // POST-ADJUST particle coefficients adapting to current FireworkType
724 switch ( fwType )
726 //create a big, white particle, simulating explosion
727 case NoFW:
728 if (parameters.enableFade)
729 particle->startLife = particle->life = 0.030;
730 else
731 particle->startLife = particle->life = 0.075;
732 particle->texture = flareTexture;
733 particle->colour[0]=
734 particle->colour[1]=
735 particle->colour[2]=1.0;
736 particle->pixelSize = 50.0 + 75.0 * DRAND;
737 particle->zacc = 0;
738 break;
740 //default. no need to change parameters, only create the
741 //'sphere light' as the first big particle if set.
742 case Sphere:
743 if (i==0 && parameters.enableSphereLight && number > 40) {
744 particle->texture = flareTexture;
745 particle->xspeed = leaderParticle->xspeed;
746 particle->yspeed = leaderParticle->yspeed;
747 particle->zspeed = 0.0;
748 particle->colour[0] /= 2.0;
749 particle->colour[1] /= 2.0;
750 particle->colour[2] /= 2.0;
751 float impression = power * (float)number/5.0;
752 particle->pixelSize = 25.0 * DRAND + impression;
753 if (parameters.enableFade) {
754 particle->startLife = particle->life = 0.040;
755 } else {
756 particle->startLife = 1.3;
757 particle->life = 1.8;
760 break;
763 case Splitter:
764 particle->particleType = Particle::FireWorkLeaderParticle;
765 particle->pixelSize *= 3.0;
766 particle->startLife = particle->life = tmp1 * (0.75 + DRAND/3.0);
767 if (particle->zspeed < 0)
768 particle->zspeed = -particle->zspeed;
769 break;
771 case Circle:
772 case BiCircle:
773 tmp5 = 2 * M_PI * DRAND;
774 //MMX can be useful.. if you know how to use it :-)
775 if ( fwType == BiCircle && i > number/2 ) {
776 GLfloat ey = cos(tmp5),
777 ez = sin(tmp5);
778 particle->xspeed = power * ( tmp3*ez );
779 particle->yspeed = power * ( tmp2*ey - tmp1*tmp4*ez );
780 particle->zspeed = power * ( tmp1*ey + tmp2*tmp4*ez );
781 } else {
782 GLfloat ex = sin(tmp5),
783 ey = cos(tmp5);
784 particle->xspeed = power * ( tmp4*ex );
785 particle->yspeed = power * ( tmp1*tmp3*ex + tmp2*ey );
786 particle->zspeed = power * ( -tmp2*tmp3*ex + tmp1*ey );
788 break;
790 case AngelHairs:
791 particle->zacc = -9.807 * (0.05 + DRAND*0.20);
792 particle->life = 2.0 + DRAND*2.5;
793 particle->startLife = particle->life - 1;
794 if (particle->zspeed < 0)
795 particle->zspeed *= -1;
796 //particle->pixelSize = 5.0;
797 break;
799 case Spirals:
800 particle->texture = diastarTexture;
801 break;
803 //discard cases
804 case SuperNova:
805 case NoRender:
806 break;
808 particleList.append(particle);
812 void KFireSaver :: timerEvent(QTimerEvent*)
814 timeval tv;
815 gettimeofday(&tv,NULL);
816 double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
818 bool chooseType = false,
819 chooseColor = false,
820 chooseOthers = false,
821 updateTimings = false;
822 bool firstTime = showp.timeStamp == 0.0;
823 bool endOfScene = currentTime >= showp.timeStamp;
825 if (firstTime)
826 switch (showp.ShowType)
828 case Monotype:
829 /* first time choose the type, color and attributes will
830 be choosed randomly for every firework which explodes*/
831 showp.forceType = true;
832 chooseType = true;
833 break;
835 case Monochrome:
836 /* first time choose the color, type and attributes will
837 be choosed randomly for every firework which explodes*/
838 showp.forceColour = true;
839 chooseColor =
840 chooseOthers = true;
841 break;
843 default: break;
846 if (endOfScene || firstTime)
847 switch (showp.ShowType)
849 case Show:
850 /* if a scene ended, randomize global parameters for the
851 whole next scene */
852 showp.forceType = true;
853 showp.forceColour = true;
854 showp.forcePower = true;
855 chooseOthers =
856 chooseColor =
857 chooseType = true;
858 updateTimings = true;
859 break;
861 default: break;
864 if ( chooseType )
866 showp.type = pickType();
867 if (parameters.typesCount < 2)
868 showp.typeSec = NoRender;
869 else
870 while ((showp.typeSec = pickType()) == showp.type);
872 if ( chooseColor ) {
873 showp.colour = pickColour();
874 showp.colourSec = pickColour();
876 if ( chooseOthers )
878 showp.powerEnvelop = DRAND * 8.0 + 3.0;
879 if (DRAND > 0.9)
881 showp.forceBicolour = true;
882 showp.colourSec = pickColour();
883 } else
884 showp.forceBicolour = false;
886 if ( firstTime || updateTimings )
888 if (DRAND < 0.2)
889 showp.timeGap = 1.0 + DRAND * 2.0;
890 else
891 showp.timeGap = 3.0 + DRAND * 10.0;
892 showp.timeStamp = currentTime + showp.timeGap;
893 showp.timeGap /= 1.5; //hack to introduce delay in sine func
896 updateGL();
899 void KFireSaver :: burnLogo(QImage * image)
901 if (!image || image->isNull())
902 return;
903 int step = parameters.enableReduceLogo ? 2 : 1,
904 imageW = image->width(),
905 imageH = image->height(),
906 offsetX = imageW / 2,
907 offsetY = imageH / 2;
908 float speed = FIELDW_4 / (imageW > imageH ? imageW : imageH),
909 speedXOffs = 5 * (DRAND - DRAND),
910 speedYOffs = DRAND + DRAND + 1;
911 //if image is too big, lower sample points
912 while ((imageW/step)>96 || (imageH/step)>96)
913 step *= 2;
914 for (int y=0 ; y<imageH ; y+=step)
916 for (int x=0 ; x<imageW ; x+=step)
918 QRgb pixel = image->pixel(x,y);
919 if ( qAlpha(pixel) < 250 )
920 continue;
921 //if ( DRAND > 0.9 )
922 // continue;
924 Particle* particle = new Particle( Particle::LogoParticle );
925 particle->initializeValues();
926 particle->texture = particleTexture;
928 float xI = (x - offsetX) ,
929 yI = (offsetY - y) ;
930 particle->xpos = xI * speed * 0.5;
931 particle->zpos = yI * speed * 0.5 + 5;
932 particle->xspeed = xI * speed + speedXOffs;
933 particle->zspeed = yI * speed + speedYOffs;
935 particle->colour[0] = qRed(pixel) / 255.0f;
936 particle->colour[1] = qGreen(pixel) / 255.0f;
937 particle->colour[2] = qBlue(pixel) / 255.0f;
939 particleList.append(particle);
942 if (parameters.enableSound)
943 playSound(sound_debris);
946 void KFireSaver :: playSound(QString file)
948 //flush inactive players
949 KPlayObject * playObject = playObjectList.first();
950 while ( playObject )
952 if ( playObject->state() != Arts::posPlaying )
954 playObjectList.remove();
955 playObject = playObjectList.current();
956 } else
957 playObject = playObjectList.next();
960 //discart this sound if the player queue is already full (4 channels playing)
961 if ( playObjectList.count() >= 6 )
962 return;
964 // not needed when all of the files are in the distribution
965 //if (!QFile::exists(file))
966 //return;
968 KPlayObjectFactory factory(artsServer.server());
969 playObject = factory.createPlayObject(KUrl(file), true);
971 if (playObject && !playObject->isNull())
973 playObject->play();
974 playObjectList.append(playObject);
978 bool KFireSaver :: loadTexture( QString fileName, unsigned int & textureID )
980 //reset texture ID to the default EMPTY value
981 textureID = 0;
983 //load image
984 QImage tmp;
985 if ( !tmp.load( fileName ) )
986 return false;
988 //convert it to suitable format (flipped RGBA)
989 QImage texture = QGLWidget::convertToGLFormat( tmp );
990 if ( texture.isNull() )
991 return false;
993 //get texture number and bind loaded image to that texture
994 glGenTextures( 1, &textureID );
995 glBindTexture( GL_TEXTURE_2D, textureID );
996 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
997 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
998 glTexImage2D( GL_TEXTURE_2D, 0, 4 /* 3 ??? */, texture.width(), texture.height(),
999 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
1000 return true;
1003 void KFireSaver :: freeTexture( unsigned int & textureID )
1005 if ( textureID > 0 )
1006 glDeleteTextures( 1, &textureID );
1007 textureID = 0;
1010 void KFireSaver :: readConfig ()
1012 KConfig config("kfiresaverrc",true,false);
1014 // show
1015 config.setGroup( "Show" );
1016 showp.ShowType = (enum enumShowType)config.readEntry( "ShowType", 1 );
1017 parameters.fireworksFrequency = 11 - config.readEntry( "FireworksFrequency", 7 );
1018 if ( parameters.fireworksFrequency < 1 )
1019 parameters.fireworksFrequency = 1;
1020 if ( parameters.fireworksFrequency > 11 )
1021 parameters.fireworksFrequency = 11;
1022 parameters.fireworksFrequency *= (parameters.fireworksFrequency + 1); //*karl gauss's sum*
1023 parameters.particleSize = config.readEntry( "ParticlesSize", 0 );
1024 if ( parameters.particleSize < -5 )
1025 parameters.particleSize = -5;
1026 if ( parameters.particleSize > 5 )
1027 parameters.particleSize = 5;
1028 if ( parameters.enableBottomFire = config.readEntry( "enable-BottomFire", true ) )
1030 QColor blue = Qt::darkBlue;
1031 parameters.bottomFireColor = config.readEntry( "BottomFireColor", blue );
1033 parameters.enableSound = config.readEntry( "enable-Sounds", false );
1034 parameters.enableNoOverhead = config.readEntry( "enable-NoOverhead", true );
1035 parameters.enableRealtime = config.readEntry( "enable-FrameSkip", true );
1037 // fireworks
1038 config.setGroup( "Fireworks" );
1039 parameters.typesCount = 0;
1040 if ( config.readEntry( "use-Classic", true ) )
1041 parameters.typesT[parameters.typesCount++] = Sphere;
1042 if ( config.readEntry( "use-Explosion", false ) )
1043 parameters.typesT[parameters.typesCount++] = NoFW;
1044 if ( config.readEntry( "use-FlameRing", false ) )
1045 parameters.typesT[parameters.typesCount++] = Circle;
1046 if ( config.readEntry( "use-FlameWorld", false ) )
1047 parameters.typesT[parameters.typesCount++] = BiCircle;
1048 if ( config.readEntry( "use-Fall", false ) )
1049 parameters.typesT[parameters.typesCount++] = AngelHairs;
1050 if ( config.readEntry( "use-Splitter", false ) )
1051 parameters.typesT[parameters.typesCount++] = Splitter;
1052 if ( config.readEntry( "use-Spirals", false ) )
1053 parameters.typesT[parameters.typesCount++] = Spirals;
1054 if ( config.readEntry( "use-SuperNova", false ) )
1055 parameters.typesT[parameters.typesCount++] = SuperNova;
1056 if ( !parameters.typesCount ) {
1057 kWarning() << "KFireSaver3D: Warning, no fireworks enabled in config file" << endl;
1058 kWarning() << " enabling 'Classic Spherical'" << endl;
1059 parameters.typesCount = 1;
1060 parameters.typesT[0] = Sphere;
1062 parameters.typesT[ parameters.typesCount ] =
1063 parameters.typesT[ parameters.typesCount-1 ];
1064 parameters.colorsCount = 0;
1065 if ( config.readEntry( "use-Red", false ) )
1066 parameters.colorsT[parameters.colorsCount++] = 0;
1067 if ( config.readEntry( "use-Orange", true ) )
1068 parameters.colorsT[parameters.colorsCount++] = 1;
1069 if ( config.readEntry( "use-Green", false ) )
1070 parameters.colorsT[parameters.colorsCount++] = 2;
1071 if ( config.readEntry( "use-Blue", false ) )
1072 parameters.colorsT[parameters.colorsCount++] = 3;
1073 if ( config.readEntry( "use-White", true ) )
1074 parameters.colorsT[parameters.colorsCount++] = 4;
1075 if ( config.readEntry( "use-Purple", false ) )
1076 parameters.colorsT[parameters.colorsCount++] = 5;
1077 if ( config.readEntry( "use-DeepGreen", true ) )
1078 parameters.colorsT[parameters.colorsCount++] = 6;
1079 if ( !parameters.colorsCount )
1081 kWarning() << "KFireSaver3D: Warning enable at least one color" << endl;
1082 kWarning() << " enabling 'Blinding White'" << endl;
1083 parameters.colorsCount = 1;
1084 parameters.colorsT[0] = 4;
1086 parameters.colorsT[ parameters.colorsCount ] =
1087 parameters.colorsT[ parameters.colorsCount-1 ];
1088 parameters.enableCombos = config.readEntry( "use-Multicolor", true );
1090 // specials
1091 config.setGroup( "Specials" );
1092 if ( parameters.enableLogos = config.readEntry( "enable-Logos", true ) )
1094 QImage tempImage;
1095 tempImage.setAlphaBuffer( true );
1096 if ( config.readEntry( "LogosTux", true ) )
1097 if ( tempImage.load(locate("data","kfiresaver/kfs_tux.png")) )
1098 imageList.append( new QImage(tempImage) );
1099 if ( config.readEntry( "LogosKonqui", true ) )
1100 if ( tempImage.load(locate("data","kfiresaver/kfs_kde.png")) )
1101 imageList.append( new QImage(tempImage) );
1102 if ( config.readEntry( "LogosKDEIcons", true ) ) {
1103 const QString icons[] = {
1104 "3floppy_unmount", "cdrom_unmount", "hdd_mount", "kmix",
1105 "network", "my-computer", "folder_home", "konqueror",
1106 "kmail", "penguin", "personal" };
1107 for ( int i = 0; i < 11; i++ )
1108 imageList.append( new QImage(DesktopIcon(icons[i],64).convertToImage()) );
1110 parameters.enableReduceLogo = config.readEntry( "LogosReduceDetail", true );
1111 parameters.logoFrequency = 11 - config.readEntry( "LogosFrequency", 4 );
1112 if ( parameters.logoFrequency < 1 )
1113 parameters.logoFrequency = 1;
1114 if ( parameters.logoFrequency > 11 )
1115 parameters.logoFrequency = 11;
1117 if ( parameters.enableStars = config.readEntry( "enable-Stars", true ) )
1119 parameters.enableStarFlickering = config.readEntry( "StarsFlicker", false );
1120 parameters.enableStarGradient = config.readEntry( "StarsGradient", true );
1121 parameters.starsNumber = config.readEntry( "StarsNumber", 4 );
1122 if ( parameters.starsNumber < 0 )
1123 parameters.starsNumber = 0;
1124 if ( parameters.starsNumber > 10 )
1125 parameters.starsNumber = 10;
1127 parameters.enableWritings = config.readEntry( "enable-Writings", true );
1129 // effects
1130 config.setGroup( "Effects" );
1131 parameters.enableSphereLight = config.readEntry( "enable-SphericalLight", true );
1132 if ( parameters.enableFlash = config.readEntry( "enable-Flash", false ) )
1134 parameters.flashOpacity = config.readEntry( "FlashOpacity", 5 );
1135 if ( parameters.flashOpacity < 0 )
1136 parameters.flashOpacity = 0;
1137 if ( parameters.flashOpacity > 10 )
1138 parameters.flashOpacity = 10;
1140 if ( parameters.enableFade = config.readEntry( "enable-Fade", false ) )
1142 parameters.fadeAmount = config.readEntry( "FadeIntensity", 3 );
1143 if ( parameters.fadeAmount < 0 )
1144 parameters.fadeAmount = 0;
1145 if ( parameters.fadeAmount > 9 )
1146 parameters.fadeAmount = 9;
1148 if ( parameters.enableMegaFlares = config.readEntry( "enable-Flares", true ) )
1150 parameters.megaFlares = config.readEntry( "FlaresDimension", 5 );
1151 if ( parameters.megaFlares < 0 )
1152 parameters.megaFlares = 0;
1153 if ( parameters.megaFlares > 10 )
1154 parameters.megaFlares = 10;
1155 parameters.megaFlares += 4;
1156 parameters.megaFlares *= 2;
1158 parameters.enableTrails = config.readEntry( "enable-Trail", false );