KURL->KUrl
[kdeartwork.git] / kscreensaver / kdesavers / firesaver.cpp
bloba272e13c71698e11ac402a340294d15c676f9a16
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 <kdialogbase.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 KDialogBase
72 public:
73 KFireSaverSetupKDB( QWidget* parent = 0, const char* name = 0 )
74 : KDialogBase( parent, name, true, i18n("Setup Screen Saver"),
75 Ok | Cancel | Help, Ok, true )
77 setup = new KFireSaverSetup( this );
78 setMainWidget( setup );
79 setButtonText( KDialogBase::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 extern "C"
99 KDE_EXPORT const char *kss_applicationName = "kfiresaver.kss";
100 KDE_EXPORT const char *kss_description = I18N_NOOP( "Fireworks 3D (GL)" );
101 KDE_EXPORT const char *kss_version = "0.7";
103 KDE_EXPORT KScreenSaver *kss_create( WId id )
105 return new KFireSaverKSS( id );
108 KDE_EXPORT QDialog *kss_setup()
110 return new KFireSaverSetupKDB;
114 /* *\
115 \* Factory code for KScreensaver ends *here* */
118 KFireSaver :: KFireSaver( QWidget *parent, const char *name )
119 : QGLWidget( parent, name )
121 // set random seed to initialize drand48() calls
122 timeval tv;
123 gettimeofday(&tv,NULL);
124 srand48( (long)tv.tv_usec );
126 readConfig();
128 particleList.setAutoDelete(true);
129 starList.setAutoDelete(true);
130 imageList.setAutoDelete(true);
131 playObjectList.setAutoDelete(true);
133 //initialize openGL context before managing GL calls
134 makeCurrent();
135 loadTexture( locate("data","kfiresaver/kfs_particle.png"), particleTexture );
136 loadTexture( locate("data","kfiresaver/kfs_particle_flare.png"), flareTexture );
137 loadTexture( locate("data","kfiresaver/kfs_particle_diastar.png"), diastarTexture );
138 starTexture = particleTexture;
140 //generate stars
141 if (parameters.enableStars) {
142 int number = parameters.starsNumber + 1;
143 number *= 10 * number;
144 for (int i=0 ; i<number ; i++)
146 Particle * star = new Particle( Particle::StarParticle );
147 star->initializeValues();
148 star->texture = starTexture;
149 if (parameters.enableStarGradient)
151 GLfloat red = star->colour[0],
152 green = star->colour[1],
153 blue = star->colour[2],
154 tint = 0.5 + star->ypos / FIELDWIDTH,
155 merge = 0.3 + DRAND*0.7,
156 mergen = 1 - merge;
157 star->colour[0] = red * merge + (1.0-tint) * mergen;
158 star->colour[1] = green * merge + tint * mergen;
159 star->colour[2] = blue * merge + tint * mergen;
161 starList.append( star );
165 //generate bottom fire
166 if (parameters.enableBottomFire) {
167 float cRed = (float)parameters.bottomFireColor.red() / 255.0,
168 cGreen = (float)parameters.bottomFireColor.green() / 255.0,
169 cBlue = (float)parameters.bottomFireColor.blue() / 255.0;
170 for (int i=0 ; i<NUMBER_OF_FIREPARTICLES ; i++)
172 Particle* particle = new Particle( Particle::FireParticle );
173 particle->initializeValues();
174 particle->texture = particleTexture;
175 particle->zspeed *= 4.0;
176 particle->colour[0] = cRed * (0.6 + 0.4*DRAND);
177 particle->colour[1] = cGreen * (0.6 + 0.4*DRAND);
178 particle->colour[2] = cBlue * (0.6 + 0.4*DRAND);
179 particleList.append(particle);
183 //get sound files
184 if (parameters.enableSound) {
185 sound_explosion = locate("data","kfiresaver/kfs_explode.ogg");
186 sound_debris = locate("data","kfiresaver/kfs_debris.ogg");
189 //create the writer class that manages flying writings.
190 if ( parameters.enableWritings )
191 writer = new Writer("kfs_letters.desc");
193 showp.forceBicolour =
194 showp.forceColour =
195 showp.forcePower =
196 showp.forceType = false;
197 showp.timeStamp = 0.0;
198 startTimer(MSECPERIOD);
200 //force initialization of "show" variables for the first time
201 timerEvent(NULL);
205 KFireSaver :: ~KFireSaver()
207 freeTexture( particleTexture );
208 freeTexture( starTexture );
209 particleList.clear();
210 starList.clear();
211 imageList.clear();
212 playObjectList.clear();
213 if ( parameters.enableWritings )
214 delete writer;
218 void KFireSaver :: initializeGL()
220 glDisable(GL_DEPTH_TEST);
221 glEnable(GL_BLEND);
222 glBlendFunc(GL_SRC_ALPHA,GL_ONE);
223 glShadeModel( GL_SMOOTH );
225 resizeGL( 640, 480 );
229 void KFireSaver :: resizeGL( int width, int height )
231 glViewport( 0, 0, width, height );
233 glMatrixMode(GL_PROJECTION);
234 glLoadIdentity();
235 glOrtho( -FIELDW_2, FIELDW_2, -FIELDW_2, FIELDW_2, 5.0, 60.0 );
237 glMatrixMode(GL_MODELVIEW);
238 glLoadIdentity();
240 float ratio = (float)width / (float)height,
241 numH = 750 - 90 * parameters.particleSize,
242 numW = 1000 - 120 * parameters.particleSize;
243 if ( ratio >= (4.0/3.0) ) {
244 unitX = FIELDWIDTH / (numH * ratio);
245 unitY = FIELDWIDTH / numH;
246 } else {
247 unitX = FIELDWIDTH / numW;
248 unitY = FIELDWIDTH / (numW / ratio);
251 timeval tv;
252 gettimeofday(&tv,NULL);
253 timeStampFrame = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
255 firstGLFrame = true;
259 void KFireSaver :: paintGL ()
260 /* Main procedure. It does the following:
261 - calculate time diff between current and previous frame
262 - clear the color buffer
263 - simple render of stars
264 - advanced render of particles
265 - render
266 - update physics based on time difference
267 - check die/change conditions
268 - call to explode_firework if a leader dies
269 - if random -> start a new firework
270 - if random -> explode a penquin or kde logo
273 /* calculate TIME ELAPSED between current and previous frame */
275 timeval tv;
276 gettimeofday(&tv,NULL);
277 double timeCurrent = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
278 double timeDiff = (MSECPERIOD / 1000.0);
279 if (parameters.enableRealtime)
281 timeDiff = timeCurrent - timeStampFrame;
282 timeStampFrame = timeCurrent;
283 timeDiff = (timeDiff > 0.5) ? 0.5 : (timeDiff < 0.005) ? 0.005 : timeDiff;
286 /* CLEAR SCREEN: to do it there are 2 ways */
288 glLoadIdentity();
289 glTranslatef( 0, 0, -50.0 );
290 glDisable( GL_TEXTURE_2D );
292 if ( !parameters.enableFade || firstGLFrame )
293 { // quick - clear the OpenGL color buffer
294 glClearColor( 0.0, 0.0, 0.0, 1.0 );
295 glClear( GL_COLOR_BUFFER_BIT );
296 firstGLFrame = false;
298 else
299 { // good looking
300 /* superpose a semi-transparent black rectangle so we
301 can see a sort of 'tail' for each particle drawn. */
302 const GLfloat conv_tab[10] = {
303 0.50, 0.33, 0.22, 0.15, 0.10,
304 0.07, 0.05, 0.03, 0.02, 0.01 };
305 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
306 glColor4f(0.0,0.0,0.0, conv_tab[parameters.fadeAmount]);
307 glBegin( GL_TRIANGLE_STRIP );
308 glVertex2f( FIELDW_2, FIELDW_2 );
309 glVertex2f( -FIELDW_2, FIELDW_2 );
310 glVertex2f( FIELDW_2, -FIELDW_2 );
311 glVertex2f( -FIELDW_2, -FIELDW_2 );
312 glEnd();
313 glBlendFunc(GL_SRC_ALPHA,GL_ONE);
316 /* render STARS */
318 if (parameters.enableStars) {
319 if ( starTexture ) {
320 glEnable( GL_TEXTURE_2D );
321 glBindTexture( GL_TEXTURE_2D, currentTexture = starTexture );
322 } else
323 glDisable( GL_TEXTURE_2D );
325 glBegin( GL_QUADS );
326 bool flickers = parameters.enableStarFlickering;
327 float alpha = flickers ? 0.5 : 1.0;
328 Particle * star = starList.first();
329 for (; star; star = starList.next())
331 if (flickers && DRAND<0.6)
332 continue;
334 GLfloat sizeX = star->pixelSize * unitX,
335 sizeY = star->pixelSize * unitY,
336 pLeft = star->xpos - sizeX,
337 pRight = star->xpos + sizeX,
338 pTop = star->ypos + sizeY,
339 pBottom = star->ypos - sizeY;
340 glColor4f(star->colour[0], star->colour[1], star->colour[2], alpha);
341 glTexCoord2f( 0, 0 ); // Bottom Left
342 glVertex2f( pLeft, pBottom );
343 glTexCoord2f( 0, 1 ); // Top Left
344 glVertex2f( pLeft, pTop );
345 glTexCoord2f( 1, 1 ); // Top Right
346 glVertex2f( pRight, pTop );
347 glTexCoord2f( 1, 0 ); // Bottom Right
348 glVertex2f( pRight, pBottom );
350 glEnd();
353 /* render FIREWORKS */
355 glBegin( GL_QUADS );
356 bool playedExplodeSound = false;
357 bool flashedScreen = false;
358 Particle * particle = particleList.first();
359 for (; particle; particle = particleList.next())
361 //bind the texture for current particle (if not already bound)
362 if ( !particle->texture ) {
363 glEnd();
364 glDisable( GL_TEXTURE_2D );
365 glBegin( GL_QUADS );
366 currentTexture = 0;
367 } else if ( particle->texture != currentTexture ) {
368 glEnd();
369 glEnable( GL_TEXTURE_2D );
370 glBindTexture( GL_TEXTURE_2D, currentTexture = particle->texture );
371 glBegin( GL_QUADS );
374 //perspective projection (done by hand to make it funnier than opengl's :-)
375 float mfactor = PERSP_MAG_FACTOR * particle->ypos;
376 if ( mfactor < -246.0 ) {
377 particleList.remove();
378 particleList.prev();
379 continue;
381 float sfactor = 256.0 / (256.0 + mfactor),
382 posx = sfactor * particle->xpos,
383 posy = sfactor * particle->zpos - 4.0;
384 //size computation (magnify if enableMegaFlares is set)
385 if ( parameters.enableMegaFlares ) {
386 mfactor = parameters.megaFlares*particle->ypos;
387 if ( mfactor < -255.0 || mfactor > 512.0 ) {
388 particleList.remove();
389 particleList.prev();
390 continue;
392 sfactor = 256.0 / (256.0 + mfactor);
393 if ( sfactor > 64 )
394 sfactor = 76.8 - sfactor / 5.0;
396 float size = sfactor * particle->pixelSize,
397 sizeX = size * unitX,
398 sizeY = size * unitY;
400 //determine brightness (alpha component) for the particle
401 if ( particle->useLife ) {
402 float life = particle->life,
403 startLife = particle->startLife;
404 //bright changes with the following curve: "2*k - k^2" (or "k(2-k)")
405 if ( life > startLife )
406 particle->colour[3] = startLife + 1 - life;
407 else
408 particle->colour[3] = life / startLife;
409 //apply flickering if enabled
410 if (particle->flicker < 0) {
411 particle->colour[3] = 0.2;
412 if (++particle->flicker >= 0)
413 particle->flicker = FLICKER_FRAMES_DELAY;
414 } else if (particle->flicker > 0) {
415 if ( life <= startLife )
416 particle->colour[3] = 1.0;
417 if (--particle->flicker <= 0)
418 particle->flicker = -FLICKER_FRAMES_DELAY;
420 glColor4fv( particle->colour );
421 } else
422 glColor3fv( particle->colour );
424 //draw particle
425 float pLeft = posx - sizeX,
426 pTop = posy + sizeY,
427 pRight = posx + sizeX,
428 pBottom = posy - sizeY;
429 glTexCoord2f( 0, 0 ); // Bottom Left
430 glVertex2f( pLeft, pBottom );
431 glTexCoord2f( 0, 1 ); // Top Left
432 glVertex2f( pLeft, pTop );
433 glTexCoord2f( 1, 1 ); // Top Right
434 glVertex2f( pRight, pTop );
435 glTexCoord2f( 1, 0 ); // Bottom Right
436 glVertex2f( pRight, pBottom );
438 //phisically update parameters of the particle
439 particle->updateParameters( timeDiff );
441 //check for particle death / explosion
442 switch (particle->particleType)
444 //a Fireparticle is restarted when in right conditions
445 case Particle::FireParticle:
446 if ( posx < -FIELDW_2 || posx > FIELDW_2 ||
447 (particle->zpos < -10.0 && posy < -FIELDW_2) )
449 particle->initializeValues();
450 if ( DRAND > 0.9995 )
451 particle->zspeed *= 4;
453 break;
455 //a leader explodes when his z speed drops to zero
456 //or, if it uses life, at death
457 case Particle::FireWorkLeaderParticle:
458 if ((particle->zspeed <= 0.0f && !particle->useLife) ||
459 (particle->useLife && particle->life <= 0.0) )
461 // play sound if enabled (and once per frame)
462 if (parameters.enableSound && !playedExplodeSound)
464 playSound(sound_explosion);
465 playedExplodeSound = true;
467 // flash screen if enabled
468 if (parameters.enableFlash && !flashedScreen) {
469 glEnd();
470 glDisable( GL_TEXTURE_2D );
471 glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
472 glBegin( GL_TRIANGLE_STRIP );
473 glVertex2f( FIELDW_2, FIELDW_2 );
474 glVertex2f( -FIELDW_2, FIELDW_2 );
475 glVertex2f( FIELDW_2, -FIELDW_2 );
476 glVertex2f( -FIELDW_2, -FIELDW_2 );
477 glEnd();
478 if ( particleTexture )
479 glEnable( GL_TEXTURE_2D );
480 glBegin( GL_QUADS );
481 flashedScreen = true;
483 // generating children and removing parent
484 int elementIndex = particleList.at();
485 explodeFirework(particle);
486 particleList.remove(elementIndex);
487 particleList.prev();
488 } else if ( parameters.enableTrails && DRAND < 0.4 ) {
489 // leave trail behind the particle (it'a small and slow red debris)
490 Particle * p = new Particle( Particle::FireWorkDebrisParticle );
491 p->initializeValues( 0, particle, 1, 1 );
492 p->texture = particleTexture;
493 p->xspeed /= 4;
494 p->yspeed /= 4;
495 p->zspeed /= 8;
496 p->zacc /= 4;
497 p->pixelSize = 2;
498 p->colour[0] /= 2;
499 int elementIndex = particleList.at();
500 particleList.append( p );
501 particleList.at( elementIndex );
503 break;
505 //remove if dead or outside field
506 default:
507 if (particle->life <= 0.0 || posx<-FIELDW_2 || posx>FIELDW_2 || posy<-FIELDW_2) {
508 particleList.remove();
509 particleList.prev();
511 break;
514 glEnd();
516 /* render WRITINGS */
518 if ( parameters.enableWritings )
520 int chance = (int) (1000.0 * DRAND);
521 if ( !chance ) {
522 static const QString someStrings[] = {
523 i18n("www.kde.org"),
524 i18n("My KDE, please!"),
525 i18n("KoNqUeR the World"),
526 i18n("KFIRESAVER 3D"),
527 i18n("Gimme your eyes..."),
528 i18n("Thank you for using KDE"),
529 i18n("Going insane tonight"),
531 int n = (int)(6.0 * DRAND);
532 writer->spawnWords( someStrings[n], Writer::Fun1 );
534 writer->render( timeDiff );
537 /* generate a new FIREWORK_LEADER */
539 int random = (int) ((float)parameters.fireworksFrequency * DRAND);
540 if (showp.ShowType == Show)
542 //double the chances ('frequency') to raise a new leaderParticle
543 //but envelop it under a sine function
544 float step = (showp.timeStamp - timeCurrent) / showp.timeGap;
545 if (DRAND > sin(M_PI*step))
546 random = -1;
547 if (showp.type == AngelHairs && DRAND < 0.5)
548 random = -1;
550 if ( !random )
552 Particle * particle = new Particle( Particle::FireWorkLeaderParticle );
553 particle->initializeValues();
554 particle->texture = flareTexture;
555 particleList.append( particle );
558 /* explode a logo */
560 int logoImages = imageList.count();
561 if ( logoImages > 0 ) {
562 random = (int) (parameters.logoFrequency * logoImages * 200.0 * DRAND);
563 if ( random < logoImages )
565 if (parameters.enableFlash && !flashedScreen) {
566 glDisable( GL_TEXTURE_2D );
567 glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
568 glBegin( GL_TRIANGLE_STRIP );
569 glVertex2f( FIELDW_2, FIELDW_2 );
570 glVertex2f( -FIELDW_2, FIELDW_2 );
571 glVertex2f( FIELDW_2, -FIELDW_2 );
572 glVertex2f( -FIELDW_2, -FIELDW_2 );
573 glEnd();
575 burnLogo( imageList.at(random) );
581 int KFireSaver :: pickColour()
583 int color = (int) (DRAND * parameters.colorsCount);
584 return parameters.colorsT[ color ];
588 KFireSaver :: enumFireworkType KFireSaver :: pickType()
590 int type = (int) (DRAND * parameters.typesCount);
591 return parameters.typesT[ type ];
595 void KFireSaver :: explodeFirework(Particle* leaderParticle)
597 GLfloat displace[3] = {0.0,0.0,0.0};
598 float tmp1 = 0.0, tmp2 = 0.0, tmp3 = 0.0, tmp4 = 0.0, tmp5 = 0.0;
600 // color of exploded particles
601 bool bicolor = parameters.enableCombos && (showp.forceBicolour || DRAND > 0.95),
602 flickers = false;
603 int cscheme = showp.forceColour ? showp.colour : pickColour(),
604 cscheme2 = showp.forceColour ? showp.colourSec : pickColour();
606 // randomize type of exploding firework
607 enumFireworkType fwType =
608 showp.forceType ? (enumFireworkType) showp.type : pickType();
610 // other options for generated particles
611 int number = (int) ((DRAND + DRAND) * 150.0);
612 float power = showp.forcePower ?
613 showp.powerEnvelop * (0.8 + 0.3*DRAND) :
614 DRAND * 11.0 + 2.0,
615 powermin = DRAND * power;
617 // now some rules ...
618 //a splitter can't split up more than 2 times
619 if (fwType == Splitter && leaderParticle->explosionsDepth > 1) {
620 if (parameters.typesCount == 1)
621 return;
622 if (showp.forceType)
623 fwType = showp.typeSec;
624 if (fwType == Splitter)
625 while ( (fwType = pickType()) == Splitter );
628 // PRE-ADJUST parameters for the firework we're creating
629 switch ( fwType )
631 //no need to handle this. it's the default configuration.
632 case Sphere:
633 break;
635 //explosion whithout emitting particles, only a flash
636 case NoFW:
637 number = 1;
638 power = powermin = 0;
639 break;
641 //splits up into 'number' orange pieces. tmp1 holds base_life
642 case Splitter:
643 cscheme = showp.forceColour ? showp.colour : 1;
644 bicolor = false;
645 number = 3 + (int) (DRAND * 4);
646 power /= 2.0;
647 powermin = power / 2.0;
648 tmp1 = 0.4 + DRAND * 1.5;
649 break;
651 //randomize a couple of angles (phi - theta) for exploding circle
652 case BiCircle:
653 number *= 2;
654 case Circle:
655 power = DRAND * 5.0 + 4.0;
656 tmp1 = DRAND * M_PI;
657 tmp2 = DRAND * M_PI;
658 tmp4 = cos(tmp2); //c2
659 tmp3 = sin(tmp2); //s2
660 tmp2 = cos(tmp1); //c1
661 tmp1 = sin(tmp1); //s1
662 break;
664 //cascade of flickering orange particles
665 case AngelHairs:
666 cscheme = showp.forceColour ? showp.colour : 1;
667 bicolor = false;
668 flickers = true;
669 power = 0.8 + DRAND * 1.9;
670 powermin = DRAND*0.5;
671 number = 100 + (int)(DRAND * 150);
672 displace[0] = -leaderParticle->xspeed/2;
673 displace[1] = -leaderParticle->yspeed/2;
674 displace[2] = power;
675 break;
677 //behave as a standard spherical firework
678 case Spirals:
679 break;
681 //not yet implemented, suppressing particles
682 case SuperNova:
683 case NoRender:
684 number = 0;
685 break;
688 //limit number of particles as we are getting to the capacity saturation
689 float currentParticles = (float) particleList.count();
690 const float particleCapacity = 15000;
691 const float particleGap = 8000;
692 if ( number > 10 && currentParticles > (particleCapacity - particleGap) )
694 //NoFW, Splitter and NoRender aren't limited.
695 number = (int)( (float)number * (particleCapacity - currentParticles) / particleGap );
696 if ( number < 10 )
697 number = 0;
700 int newExplosionsDepth = leaderParticle->explosionsDepth + 1;
701 for (int i=0 ; i<number ; i++)
703 Particle * particle;
704 if ( fwType == Spirals )
705 particle = new TurningParticle( Particle::FireWorkDebrisParticle );
706 else
707 particle = new Particle( Particle::FireWorkDebrisParticle );
709 particle->initializeValues (
710 bicolor && (i>number/2) ? cscheme2 : cscheme,
711 leaderParticle, powermin, power,
712 flickers, displace );
713 particle->texture = particleTexture;
714 particle->explosionsDepth = newExplosionsDepth;
716 // POST-ADJUST particle coefficients adapting to current FireworkType
717 switch ( fwType )
719 //create a big, white particle, simulating explosion
720 case NoFW:
721 if (parameters.enableFade)
722 particle->startLife = particle->life = 0.030;
723 else
724 particle->startLife = particle->life = 0.075;
725 particle->texture = flareTexture;
726 particle->colour[0]=
727 particle->colour[1]=
728 particle->colour[2]=1.0;
729 particle->pixelSize = 50.0 + 75.0 * DRAND;
730 particle->zacc = 0;
731 break;
733 //default. no need to change parameters, only create the
734 //'sphere light' as the first big particle if set.
735 case Sphere:
736 if (i==0 && parameters.enableSphereLight && number > 40) {
737 particle->texture = flareTexture;
738 particle->xspeed = leaderParticle->xspeed;
739 particle->yspeed = leaderParticle->yspeed;
740 particle->zspeed = 0.0;
741 particle->colour[0] /= 2.0;
742 particle->colour[1] /= 2.0;
743 particle->colour[2] /= 2.0;
744 float impression = power * (float)number/5.0;
745 particle->pixelSize = 25.0 * DRAND + impression;
746 if (parameters.enableFade) {
747 particle->startLife = particle->life = 0.040;
748 } else {
749 particle->startLife = 1.3;
750 particle->life = 1.8;
753 break;
756 case Splitter:
757 particle->particleType = Particle::FireWorkLeaderParticle;
758 particle->pixelSize *= 3.0;
759 particle->startLife = particle->life = tmp1 * (0.75 + DRAND/3.0);
760 if (particle->zspeed < 0)
761 particle->zspeed = -particle->zspeed;
762 break;
764 case Circle:
765 case BiCircle:
766 tmp5 = 2 * M_PI * DRAND;
767 //MMX can be useful.. if you know how to use it :-)
768 if ( fwType == BiCircle && i > number/2 ) {
769 GLfloat ey = cos(tmp5),
770 ez = sin(tmp5);
771 particle->xspeed = power * ( tmp3*ez );
772 particle->yspeed = power * ( tmp2*ey - tmp1*tmp4*ez );
773 particle->zspeed = power * ( tmp1*ey + tmp2*tmp4*ez );
774 } else {
775 GLfloat ex = sin(tmp5),
776 ey = cos(tmp5);
777 particle->xspeed = power * ( tmp4*ex );
778 particle->yspeed = power * ( tmp1*tmp3*ex + tmp2*ey );
779 particle->zspeed = power * ( -tmp2*tmp3*ex + tmp1*ey );
781 break;
783 case AngelHairs:
784 particle->zacc = -9.807 * (0.05 + DRAND*0.20);
785 particle->life = 2.0 + DRAND*2.5;
786 particle->startLife = particle->life - 1;
787 if (particle->zspeed < 0)
788 particle->zspeed *= -1;
789 //particle->pixelSize = 5.0;
790 break;
792 case Spirals:
793 particle->texture = diastarTexture;
794 break;
796 //discard cases
797 case SuperNova:
798 case NoRender:
799 break;
801 particleList.append(particle);
805 void KFireSaver :: timerEvent(QTimerEvent*)
807 timeval tv;
808 gettimeofday(&tv,NULL);
809 double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
811 bool chooseType = false,
812 chooseColor = false,
813 chooseOthers = false,
814 updateTimings = false;
815 bool firstTime = showp.timeStamp == 0.0;
816 bool endOfScene = currentTime >= showp.timeStamp;
818 if (firstTime)
819 switch (showp.ShowType)
821 case Monotype:
822 /* first time choose the type, color and attributes will
823 be choosed randomly for every firework which explodes*/
824 showp.forceType = true;
825 chooseType = true;
826 break;
828 case Monochrome:
829 /* first time choose the color, type and attributes will
830 be choosed randomly for every firework which explodes*/
831 showp.forceColour = true;
832 chooseColor =
833 chooseOthers = true;
834 break;
836 default: break;
839 if (endOfScene || firstTime)
840 switch (showp.ShowType)
842 case Show:
843 /* if a scene ended, randomize global parameters for the
844 whole next scene */
845 showp.forceType = true;
846 showp.forceColour = true;
847 showp.forcePower = true;
848 chooseOthers =
849 chooseColor =
850 chooseType = true;
851 updateTimings = true;
852 break;
854 default: break;
857 if ( chooseType )
859 showp.type = pickType();
860 if (parameters.typesCount < 2)
861 showp.typeSec = NoRender;
862 else
863 while ((showp.typeSec = pickType()) == showp.type);
865 if ( chooseColor ) {
866 showp.colour = pickColour();
867 showp.colourSec = pickColour();
869 if ( chooseOthers )
871 showp.powerEnvelop = DRAND * 8.0 + 3.0;
872 if (DRAND > 0.9)
874 showp.forceBicolour = true;
875 showp.colourSec = pickColour();
876 } else
877 showp.forceBicolour = false;
879 if ( firstTime || updateTimings )
881 if (DRAND < 0.2)
882 showp.timeGap = 1.0 + DRAND * 2.0;
883 else
884 showp.timeGap = 3.0 + DRAND * 10.0;
885 showp.timeStamp = currentTime + showp.timeGap;
886 showp.timeGap /= 1.5; //hack to introduce delay in sine func
889 updateGL();
892 void KFireSaver :: burnLogo(QImage * image)
894 if (!image || image->isNull())
895 return;
896 int step = parameters.enableReduceLogo ? 2 : 1,
897 imageW = image->width(),
898 imageH = image->height(),
899 offsetX = imageW / 2,
900 offsetY = imageH / 2;
901 float speed = FIELDW_4 / (imageW > imageH ? imageW : imageH),
902 speedXOffs = 5 * (DRAND - DRAND),
903 speedYOffs = DRAND + DRAND + 1;
904 //if image is too big, lower sample points
905 while ((imageW/step)>96 || (imageH/step)>96)
906 step *= 2;
907 for (int y=0 ; y<imageH ; y+=step)
909 for (int x=0 ; x<imageW ; x+=step)
911 QRgb pixel = image->pixel(x,y);
912 if ( qAlpha(pixel) < 250 )
913 continue;
914 //if ( DRAND > 0.9 )
915 // continue;
917 Particle* particle = new Particle( Particle::LogoParticle );
918 particle->initializeValues();
919 particle->texture = particleTexture;
921 float xI = (x - offsetX) ,
922 yI = (offsetY - y) ;
923 particle->xpos = xI * speed * 0.5;
924 particle->zpos = yI * speed * 0.5 + 5;
925 particle->xspeed = xI * speed + speedXOffs;
926 particle->zspeed = yI * speed + speedYOffs;
928 particle->colour[0] = qRed(pixel) / 255.0f;
929 particle->colour[1] = qGreen(pixel) / 255.0f;
930 particle->colour[2] = qBlue(pixel) / 255.0f;
932 particleList.append(particle);
935 if (parameters.enableSound)
936 playSound(sound_debris);
939 void KFireSaver :: playSound(QString file)
941 //flush inactive players
942 KPlayObject * playObject = playObjectList.first();
943 while ( playObject )
945 if ( playObject->state() != Arts::posPlaying )
947 playObjectList.remove();
948 playObject = playObjectList.current();
949 } else
950 playObject = playObjectList.next();
953 //discart this sound if the player queue is already full (4 channels playing)
954 if ( playObjectList.count() >= 6 )
955 return;
957 // not needed when all of the files are in the distribution
958 //if (!QFile::exists(file))
959 //return;
961 KPlayObjectFactory factory(artsServer.server());
962 playObject = factory.createPlayObject(KUrl(file), true);
964 if (playObject && !playObject->isNull())
966 playObject->play();
967 playObjectList.append(playObject);
971 bool KFireSaver :: loadTexture( QString fileName, unsigned int & textureID )
973 //reset texture ID to the default EMPTY value
974 textureID = 0;
976 //load image
977 QImage tmp;
978 if ( !tmp.load( fileName ) )
979 return false;
981 //convert it to suitable format (flipped RGBA)
982 QImage texture = QGLWidget::convertToGLFormat( tmp );
983 if ( texture.isNull() )
984 return false;
986 //get texture number and bind loaded image to that texture
987 glGenTextures( 1, &textureID );
988 glBindTexture( GL_TEXTURE_2D, textureID );
989 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
990 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
991 glTexImage2D( GL_TEXTURE_2D, 0, 4 /* 3 ??? */, texture.width(), texture.height(),
992 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
993 return true;
996 void KFireSaver :: freeTexture( unsigned int & textureID )
998 if ( textureID > 0 )
999 glDeleteTextures( 1, &textureID );
1000 textureID = 0;
1003 void KFireSaver :: readConfig ()
1005 KConfig config("kfiresaverrc",true,false);
1007 // show
1008 config.setGroup( "Show" );
1009 showp.ShowType = (enum enumShowType)config.readNumEntry( "ShowType", 1 );
1010 parameters.fireworksFrequency = 11 - config.readNumEntry( "FireworksFrequency", 7 );
1011 if ( parameters.fireworksFrequency < 1 )
1012 parameters.fireworksFrequency = 1;
1013 if ( parameters.fireworksFrequency > 11 )
1014 parameters.fireworksFrequency = 11;
1015 parameters.fireworksFrequency *= (parameters.fireworksFrequency + 1); //*karl gauss's sum*
1016 parameters.particleSize = config.readNumEntry( "ParticlesSize", 0 );
1017 if ( parameters.particleSize < -5 )
1018 parameters.particleSize = -5;
1019 if ( parameters.particleSize > 5 )
1020 parameters.particleSize = 5;
1021 if ( parameters.enableBottomFire = config.readBoolEntry( "enable-BottomFire", true ) )
1023 QColor blue = Qt::darkBlue;
1024 parameters.bottomFireColor = config.readColorEntry( "BottomFireColor", &blue );
1026 parameters.enableSound = config.readBoolEntry( "enable-Sounds", false );
1027 parameters.enableNoOverhead = config.readBoolEntry( "enable-NoOverhead", true );
1028 parameters.enableRealtime = config.readBoolEntry( "enable-FrameSkip", true );
1030 // fireworks
1031 config.setGroup( "Fireworks" );
1032 parameters.typesCount = 0;
1033 if ( config.readBoolEntry( "use-Classic", true ) )
1034 parameters.typesT[parameters.typesCount++] = Sphere;
1035 if ( config.readBoolEntry( "use-Explosion", false ) )
1036 parameters.typesT[parameters.typesCount++] = NoFW;
1037 if ( config.readBoolEntry( "use-FlameRing", false ) )
1038 parameters.typesT[parameters.typesCount++] = Circle;
1039 if ( config.readBoolEntry( "use-FlameWorld", false ) )
1040 parameters.typesT[parameters.typesCount++] = BiCircle;
1041 if ( config.readBoolEntry( "use-Fall", false ) )
1042 parameters.typesT[parameters.typesCount++] = AngelHairs;
1043 if ( config.readBoolEntry( "use-Splitter", false ) )
1044 parameters.typesT[parameters.typesCount++] = Splitter;
1045 if ( config.readBoolEntry( "use-Spirals", false ) )
1046 parameters.typesT[parameters.typesCount++] = Spirals;
1047 if ( config.readBoolEntry( "use-SuperNova", false ) )
1048 parameters.typesT[parameters.typesCount++] = SuperNova;
1049 if ( !parameters.typesCount ) {
1050 kWarning() << "KFireSaver3D: Warning, no fireworks enabled in config file" << endl;
1051 kWarning() << " enabling 'Classic Spherical'" << endl;
1052 parameters.typesCount = 1;
1053 parameters.typesT[0] = Sphere;
1055 parameters.typesT[ parameters.typesCount ] =
1056 parameters.typesT[ parameters.typesCount-1 ];
1057 parameters.colorsCount = 0;
1058 if ( config.readBoolEntry( "use-Red", false ) )
1059 parameters.colorsT[parameters.colorsCount++] = 0;
1060 if ( config.readBoolEntry( "use-Orange", true ) )
1061 parameters.colorsT[parameters.colorsCount++] = 1;
1062 if ( config.readBoolEntry( "use-Green", false ) )
1063 parameters.colorsT[parameters.colorsCount++] = 2;
1064 if ( config.readBoolEntry( "use-Blue", false ) )
1065 parameters.colorsT[parameters.colorsCount++] = 3;
1066 if ( config.readBoolEntry( "use-White", true ) )
1067 parameters.colorsT[parameters.colorsCount++] = 4;
1068 if ( config.readBoolEntry( "use-Purple", false ) )
1069 parameters.colorsT[parameters.colorsCount++] = 5;
1070 if ( config.readBoolEntry( "use-DeepGreen", true ) )
1071 parameters.colorsT[parameters.colorsCount++] = 6;
1072 if ( !parameters.colorsCount )
1074 kWarning() << "KFireSaver3D: Warning enable at least one color" << endl;
1075 kWarning() << " enabling 'Blinding White'" << endl;
1076 parameters.colorsCount = 1;
1077 parameters.colorsT[0] = 4;
1079 parameters.colorsT[ parameters.colorsCount ] =
1080 parameters.colorsT[ parameters.colorsCount-1 ];
1081 parameters.enableCombos = config.readBoolEntry( "use-Multicolor", true );
1083 // specials
1084 config.setGroup( "Specials" );
1085 if ( parameters.enableLogos = config.readBoolEntry( "enable-Logos", true ) )
1087 QImage tempImage;
1088 tempImage.setAlphaBuffer( true );
1089 if ( config.readBoolEntry( "LogosTux", true ) )
1090 if ( tempImage.load(locate("data","kfiresaver/kfs_tux.png")) )
1091 imageList.append( new QImage(tempImage) );
1092 if ( config.readBoolEntry( "LogosKonqui", true ) )
1093 if ( tempImage.load(locate("data","kfiresaver/kfs_kde.png")) )
1094 imageList.append( new QImage(tempImage) );
1095 if ( config.readBoolEntry( "LogosKDEIcons", true ) ) {
1096 const QString icons[] = {
1097 "3floppy_unmount", "cdrom_unmount", "hdd_mount", "kmix",
1098 "network", "my-computer", "folder_home", "konqueror",
1099 "kmail", "penguin", "personal" };
1100 for ( int i = 0; i < 11; i++ )
1101 imageList.append( new QImage(DesktopIcon(icons[i],64).convertToImage()) );
1103 parameters.enableReduceLogo = config.readBoolEntry( "LogosReduceDetail", true );
1104 parameters.logoFrequency = 11 - config.readNumEntry( "LogosFrequency", 4 );
1105 if ( parameters.logoFrequency < 1 )
1106 parameters.logoFrequency = 1;
1107 if ( parameters.logoFrequency > 11 )
1108 parameters.logoFrequency = 11;
1110 if ( parameters.enableStars = config.readBoolEntry( "enable-Stars", true ) )
1112 parameters.enableStarFlickering = config.readBoolEntry( "StarsFlicker", false );
1113 parameters.enableStarGradient = config.readBoolEntry( "StarsGradient", true );
1114 parameters.starsNumber = config.readNumEntry( "StarsNumber", 4 );
1115 if ( parameters.starsNumber < 0 )
1116 parameters.starsNumber = 0;
1117 if ( parameters.starsNumber > 10 )
1118 parameters.starsNumber = 10;
1120 parameters.enableWritings = config.readBoolEntry( "enable-Writings", true );
1122 // effects
1123 config.setGroup( "Effects" );
1124 parameters.enableSphereLight = config.readBoolEntry( "enable-SphericalLight", true );
1125 if ( parameters.enableFlash = config.readBoolEntry( "enable-Flash", false ) )
1127 parameters.flashOpacity = config.readNumEntry( "FlashOpacity", 5 );
1128 if ( parameters.flashOpacity < 0 )
1129 parameters.flashOpacity = 0;
1130 if ( parameters.flashOpacity > 10 )
1131 parameters.flashOpacity = 10;
1133 if ( parameters.enableFade = config.readBoolEntry( "enable-Fade", false ) )
1135 parameters.fadeAmount = config.readNumEntry( "FadeIntensity", 3 );
1136 if ( parameters.fadeAmount < 0 )
1137 parameters.fadeAmount = 0;
1138 if ( parameters.fadeAmount > 9 )
1139 parameters.fadeAmount = 9;
1141 if ( parameters.enableMegaFlares = config.readBoolEntry( "enable-Flares", true ) )
1143 parameters.megaFlares = config.readNumEntry( "FlaresDimension", 5 );
1144 if ( parameters.megaFlares < 0 )
1145 parameters.megaFlares = 0;
1146 if ( parameters.megaFlares > 10 )
1147 parameters.megaFlares = 10;
1148 parameters.megaFlares += 4;
1149 parameters.megaFlares *= 2;
1151 parameters.enableTrails = config.readBoolEntry( "enable-Trail", false );