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
25 #include <kstandarddirs.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* *\
43 #include <kscreensaver.h>
44 #include <kdialogbase.h>
46 #include <QTimerEvent>
48 class KFireSaverKSS
: public KScreenSaver
51 KFireSaverKSS( WId id
)
54 setBackgroundColor( black
);
56 saver
= new KFireSaver
;
70 class KFireSaverSetupKDB
: public KDialogBase
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" ) );
85 KMessageBox::about(this, i18n("<h3>KFireSaver 3D 1.0</h3>\n<p>TEST Koral - Enrico Ros::2004</p>") );
94 KFireSaverSetup
* setup
;
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
;
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
123 gettimeofday(&tv
,NULL
);
124 srand48( (long)tv
.tv_usec
);
128 particleList
.setAutoDelete(true);
129 starList
.setAutoDelete(true);
130 imageList
.setAutoDelete(true);
131 playObjectList
.setAutoDelete(true);
133 //initialize openGL context before managing GL calls
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
;
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,
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
);
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
=
196 showp
.forceType
= false;
197 showp
.timeStamp
= 0.0;
198 startTimer(MSECPERIOD
);
200 //force initialization of "show" variables for the first time
205 KFireSaver :: ~KFireSaver()
207 freeTexture( particleTexture
);
208 freeTexture( starTexture
);
209 particleList
.clear();
212 playObjectList
.clear();
213 if ( parameters
.enableWritings
)
218 void KFireSaver :: initializeGL()
220 glDisable(GL_DEPTH_TEST
);
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
);
235 glOrtho( -FIELDW_2
, FIELDW_2
, -FIELDW_2
, FIELDW_2
, 5.0, 60.0 );
237 glMatrixMode(GL_MODELVIEW
);
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
;
247 unitX
= FIELDWIDTH
/ numW
;
248 unitY
= FIELDWIDTH
/ (numW
/ ratio
);
252 gettimeofday(&tv
,NULL
);
253 timeStampFrame
= (double)tv
.tv_sec
+ (double)tv
.tv_usec
/1000000.0;
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
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 */
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 */
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;
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
);
313 glBlendFunc(GL_SRC_ALPHA
,GL_ONE
);
318 if (parameters
.enableStars
) {
320 glEnable( GL_TEXTURE_2D
);
321 glBindTexture( GL_TEXTURE_2D
, currentTexture
= starTexture
);
323 glDisable( GL_TEXTURE_2D
);
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)
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
);
353 /* render FIREWORKS */
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
) {
364 glDisable( GL_TEXTURE_2D
);
367 } else if ( particle
->texture
!= currentTexture
) {
369 glEnable( GL_TEXTURE_2D
);
370 glBindTexture( GL_TEXTURE_2D
, currentTexture
= particle
->texture
);
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();
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();
392 sfactor
= 256.0 / (256.0 + mfactor
);
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
;
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
);
422 glColor3fv( particle
->colour
);
425 float pLeft
= posx
- sizeX
,
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;
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
) {
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
);
478 if ( particleTexture
)
479 glEnable( GL_TEXTURE_2D
);
481 flashedScreen
= true;
483 // generating children and removing parent
484 int elementIndex
= particleList
.at();
485 explodeFirework(particle
);
486 particleList
.remove(elementIndex
);
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
;
499 int elementIndex
= particleList
.at();
500 particleList
.append( p
);
501 particleList
.at( elementIndex
);
505 //remove if dead or outside field
507 if (particle
->life
<= 0.0 || posx
<-FIELDW_2
|| posx
>FIELDW_2
|| posy
<-FIELDW_2
) {
508 particleList
.remove();
516 /* render WRITINGS */
518 if ( parameters
.enableWritings
)
520 int chance
= (int) (1000.0 * DRAND
);
522 static const QString someStrings
[] = {
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
))
547 if (showp
.type
== AngelHairs
&& DRAND
< 0.5)
552 Particle
* particle
= new Particle( Particle::FireWorkLeaderParticle
);
553 particle
->initializeValues();
554 particle
->texture
= flareTexture
;
555 particleList
.append( particle
);
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
);
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),
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
) :
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)
623 fwType
= showp
.typeSec
;
624 if (fwType
== Splitter
)
625 while ( (fwType
= pickType()) == Splitter
);
628 // PRE-ADJUST parameters for the firework we're creating
631 //no need to handle this. it's the default configuration.
635 //explosion whithout emitting particles, only a flash
638 power
= powermin
= 0;
641 //splits up into 'number' orange pieces. tmp1 holds base_life
643 cscheme
= showp
.forceColour
? showp
.colour
: 1;
645 number
= 3 + (int) (DRAND
* 4);
647 powermin
= power
/ 2.0;
648 tmp1
= 0.4 + DRAND
* 1.5;
651 //randomize a couple of angles (phi - theta) for exploding circle
655 power
= DRAND
* 5.0 + 4.0;
658 tmp4
= cos(tmp2
); //c2
659 tmp3
= sin(tmp2
); //s2
660 tmp2
= cos(tmp1
); //c1
661 tmp1
= sin(tmp1
); //s1
664 //cascade of flickering orange particles
666 cscheme
= showp
.forceColour
? showp
.colour
: 1;
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;
677 //behave as a standard spherical firework
681 //not yet implemented, suppressing particles
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
);
700 int newExplosionsDepth
= leaderParticle
->explosionsDepth
+ 1;
701 for (int i
=0 ; i
<number
; i
++)
704 if ( fwType
== Spirals
)
705 particle
= new TurningParticle( Particle::FireWorkDebrisParticle
);
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
719 //create a big, white particle, simulating explosion
721 if (parameters
.enableFade
)
722 particle
->startLife
= particle
->life
= 0.030;
724 particle
->startLife
= particle
->life
= 0.075;
725 particle
->texture
= flareTexture
;
728 particle
->colour
[2]=1.0;
729 particle
->pixelSize
= 50.0 + 75.0 * DRAND
;
733 //default. no need to change parameters, only create the
734 //'sphere light' as the first big particle if set.
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;
749 particle
->startLife
= 1.3;
750 particle
->life
= 1.8;
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
;
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
),
771 particle
->xspeed
= power
* ( tmp3
*ez
);
772 particle
->yspeed
= power
* ( tmp2
*ey
- tmp1
*tmp4
*ez
);
773 particle
->zspeed
= power
* ( tmp1
*ey
+ tmp2
*tmp4
*ez
);
775 GLfloat ex
= sin(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
);
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;
793 particle
->texture
= diastarTexture
;
801 particleList
.append(particle
);
805 void KFireSaver :: timerEvent(QTimerEvent
*)
808 gettimeofday(&tv
,NULL
);
809 double currentTime
= (double)tv
.tv_sec
+ (double)tv
.tv_usec
/1000000.0;
811 bool chooseType
= false,
813 chooseOthers
= false,
814 updateTimings
= false;
815 bool firstTime
= showp
.timeStamp
== 0.0;
816 bool endOfScene
= currentTime
>= showp
.timeStamp
;
819 switch (showp
.ShowType
)
822 /* first time choose the type, color and attributes will
823 be choosed randomly for every firework which explodes*/
824 showp
.forceType
= true;
829 /* first time choose the color, type and attributes will
830 be choosed randomly for every firework which explodes*/
831 showp
.forceColour
= true;
839 if (endOfScene
|| firstTime
)
840 switch (showp
.ShowType
)
843 /* if a scene ended, randomize global parameters for the
845 showp
.forceType
= true;
846 showp
.forceColour
= true;
847 showp
.forcePower
= true;
851 updateTimings
= true;
859 showp
.type
= pickType();
860 if (parameters
.typesCount
< 2)
861 showp
.typeSec
= NoRender
;
863 while ((showp
.typeSec
= pickType()) == showp
.type
);
866 showp
.colour
= pickColour();
867 showp
.colourSec
= pickColour();
871 showp
.powerEnvelop
= DRAND
* 8.0 + 3.0;
874 showp
.forceBicolour
= true;
875 showp
.colourSec
= pickColour();
877 showp
.forceBicolour
= false;
879 if ( firstTime
|| updateTimings
)
882 showp
.timeGap
= 1.0 + DRAND
* 2.0;
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
892 void KFireSaver :: burnLogo(QImage
* image
)
894 if (!image
|| image
->isNull())
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)
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 )
917 Particle
* particle
= new Particle( Particle::LogoParticle
);
918 particle
->initializeValues();
919 particle
->texture
= particleTexture
;
921 float xI
= (x
- offsetX
) ,
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();
945 if ( playObject
->state() != Arts::posPlaying
)
947 playObjectList
.remove();
948 playObject
= playObjectList
.current();
950 playObject
= playObjectList
.next();
953 //discart this sound if the player queue is already full (4 channels playing)
954 if ( playObjectList
.count() >= 6 )
957 // not needed when all of the files are in the distribution
958 //if (!QFile::exists(file))
961 KPlayObjectFactory
factory(artsServer
.server());
962 playObject
= factory
.createPlayObject(KUrl(file
), true);
964 if (playObject
&& !playObject
->isNull())
967 playObjectList
.append(playObject
);
971 bool KFireSaver :: loadTexture( QString fileName
, unsigned int & textureID
)
973 //reset texture ID to the default EMPTY value
978 if ( !tmp
.load( fileName
) )
981 //convert it to suitable format (flipped RGBA)
982 QImage texture
= QGLWidget::convertToGLFormat( tmp
);
983 if ( texture
.isNull() )
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() );
996 void KFireSaver :: freeTexture( unsigned int & textureID
)
999 glDeleteTextures( 1, &textureID
);
1003 void KFireSaver :: readConfig ()
1005 KConfig
config("kfiresaverrc",true,false);
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 );
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 );
1084 config
.setGroup( "Specials" );
1085 if ( parameters
.enableLogos
= config
.readBoolEntry( "enable-Logos", true ) )
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 );
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 );