Back to index

kdeartwork  4.3.2
firesaver.cpp
Go to the documentation of this file.
00001 //     This file is part of KFireSaver3D.
00002 
00003 //     KFireSaver3D is free software; you can redistribute it and/or modify
00004 //     it under the terms of the GNU General Public License as published by
00005 //     the Free Software Foundation; either version 2 of the License, or
00006 //     (at your option) any later version.
00007 
00008 //     KFireSaver3D is distributed in the hope that it will be useful,
00009 //     but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 //     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 //     GNU General Public License for more details.
00012 
00013 //     You should have received a copy of the GNU General Public License
00014 //     along with KFireSaver3D; if not, write to the Free Software
00015 //     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00016 
00017 
00018 //     Author: Enrico Ros, based on the great work of David Sansome (kfiresaver)
00019 //     Email:  asy@libero.it
00020 
00021 #include <math.h>
00022 #include <sys/time.h>
00023 #include <kconfig.h>
00024 #include <kdebug.h>
00025 #include <kstandarddirs.h>
00026 #include <klocale.h>
00027 #include <kurl.h>
00028 #include <kiconloader.h>
00029 #include <kmessagebox.h>
00030 #include <arts/kmedia2.h>
00031 #include <arts/kplayobject.h>
00032 #include <arts/kplayobjectfactory.h>
00033 
00034 #include "firesaversetup.h"
00035 #include "firesaverparticle.h"
00036 #include "firesaverwriter.h"
00037 #include "firesaver.h"
00038 
00039 
00040 /* Factory code for KScreensaver begins *here* *\
00041 \*                                             */
00042 
00043 #include <kscreensaver.h>
00044 #include <kdialog.h>
00045 
00046 class KFireSaverKSS : public KScreenSaver
00047 {
00048     public:
00049        KFireSaverKSS( WId id )
00050            : KScreenSaver( id )
00051        {
00052            setBackgroundColor( black );
00053            erase();
00054            saver = new KFireSaver;
00055            embed(saver);
00056            saver->show();
00057        }
00058 
00059        ~KFireSaverKSS()
00060        {
00061            delete saver;
00062        }
00063 
00064     private:
00065        KFireSaver* saver;
00066 };
00067 
00068 class KFireSaverSetupKDB : public KDialog
00069 {
00070     public:
00071        KFireSaverSetupKDB( QWidget* parent = 0 )
00072            : KDialog( parent, name, true, i18n("Setup Screen Saver"),
00073            Ok | Cancel | Help, Ok, true )
00074        {
00075               setup = new KFireSaverSetup( this );
00076               setMainWidget( setup );
00077               setButtonText( KDialog::Help, i18n( "A&bout" ) );
00078        }
00079 
00080     private slots:
00081        void slotHelp()
00082        {
00083               KMessageBox::about(this, i18n("<h3>KFireSaver 3D 1.0</h3>\n<p>TEST Koral - Enrico Ros::2004</p>") );
00084        }
00085        void slotOk()
00086        {
00087               setup->writeConfig();
00088               accept();
00089        }
00090 
00091     private:
00092        KFireSaverSetup * setup;
00093 };
00094 
00095 class KFireSaverInterface : public KScreenSaverInterface
00096 {
00097 
00098 
00099 public:
00100     virtual KAboutData* aboutData() {
00101         return new KAboutData( "kfiresaver.kss", "klock", ki18n( "Fireworks 3D (GL)" ), "0.7", ki18n( "Fireworks 3D (GL)" ) );
00102     }
00103 
00104 
00105     virtual KScreenSaver* create( WId id )
00106     {
00107        return new KFireSaverKSS( id );
00108     }
00109 
00110     virtual QDialog* setup()
00111     {
00112        return new KFireSaverSetupKDB;
00113     }
00114 };
00115 
00116 int main( int argc, char *argv[] )
00117 {
00118     KFireSaverInterface kss;
00119     return kScreenSaverMain( argc, argv, kss );
00120 }
00121 
00122 /*                                             *\
00123 \* Factory code for KScreensaver ends *here*   */
00124 
00125 
00126 KFireSaver :: KFireSaver( QWidget *parent )
00127        : QGLWidget( parent )
00128 {
00129        // set random seed to initialize drand48() calls
00130        timeval tv;
00131        gettimeofday(&tv,NULL);
00132        srand48( (long)tv.tv_usec );
00133 
00134        readConfig();
00135 
00136        //initialize openGL context before managing GL calls
00137        makeCurrent();
00138        loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle.png"), particleTexture );
00139        loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle_flare.png"), flareTexture );
00140        loadTexture( KStandardDirs::locate("data","kfiresaver/kfs_particle_diastar.png"), diastarTexture );
00141        starTexture = particleTexture;
00142 
00143        //generate stars
00144        if (parameters.enableStars) {
00145               int number = parameters.starsNumber + 1;
00146               number *= 10 * number;
00147               for (int i=0 ; i<number ; i++)
00148               {
00149                      Particle * star = new Particle( Particle::StarParticle );
00150                      star->initializeValues();
00151                      star->texture = starTexture;
00152                      if (parameters.enableStarGradient)
00153                      {
00154                             GLfloat red = star->colour[0],
00155                                    green = star->colour[1],
00156                                    blue = star->colour[2],
00157                                    tint = 0.5 + star->ypos / FIELDWIDTH,
00158                                    merge = 0.3 + DRAND*0.7,
00159                                    mergen = 1 - merge;
00160                             star->colour[0] = red * merge + (1.0-tint) * mergen;
00161                             star->colour[1] = green * merge + tint * mergen;
00162                             star->colour[2] = blue * merge + tint * mergen;
00163                      }
00164                      starList.append( star );
00165               }
00166        }
00167 
00168        //generate bottom fire
00169        if (parameters.enableBottomFire) {
00170               float cRed = (float)parameters.bottomFireColor.red() / 255.0,
00171                     cGreen = (float)parameters.bottomFireColor.green() / 255.0,
00172                     cBlue = (float)parameters.bottomFireColor.blue() / 255.0;
00173               for (int i=0 ; i<NUMBER_OF_FIREPARTICLES ; i++)
00174               {
00175                      Particle* particle = new Particle( Particle::FireParticle );
00176                      particle->initializeValues();
00177                      particle->texture = particleTexture;
00178                      particle->zspeed *= 4.0;
00179                      particle->colour[0] = cRed * (0.6 + 0.4*DRAND);
00180                      particle->colour[1] = cGreen * (0.6 + 0.4*DRAND);
00181                      particle->colour[2] = cBlue * (0.6 + 0.4*DRAND);
00182                      particleList.append(particle);
00183               }
00184        }
00185 
00186        //get sound files
00187        if (parameters.enableSound) {
00188               sound_explosion = locate("data","kfiresaver/kfs_explode.ogg");
00189               sound_debris = locate("data","kfiresaver/kfs_debris.ogg");
00190        }
00191 
00192        //create the writer class that manages flying writings.
00193        if ( parameters.enableWritings )
00194               writer = new Writer("kfs_letters.desc");
00195 
00196        showp.forceBicolour =
00197        showp.forceColour =
00198        showp.forcePower =
00199        showp.forceType = false;
00200        showp.timeStamp = 0.0;
00201        startTimer(MSECPERIOD);
00202 
00203        //force initialization of "show" variables for the first time
00204        timerEvent(NULL);
00205 }
00206 
00207 
00208 KFireSaver :: ~KFireSaver()
00209 {
00210        freeTexture( particleTexture );
00211        freeTexture( starTexture );
00212        particleList.clear();
00213        starList.clear();
00214        imageList.clear();
00215        playObjectList.clear();
00216        if ( parameters.enableWritings )
00217               delete writer;
00218 
00219        qDeleteAll(particleList);
00220        qDeleteAll(starList);
00221        qDeleteAll(imageList);
00222        qDeleteAll(playObjectList);
00223 }
00224 
00225 
00226 void KFireSaver :: initializeGL()
00227 {
00228        glDisable(GL_DEPTH_TEST);
00229        glEnable(GL_BLEND);
00230        glBlendFunc(GL_SRC_ALPHA,GL_ONE);
00231        glShadeModel( GL_SMOOTH );
00232 
00233        resizeGL( 640, 480 );
00234 }
00235 
00236 
00237 void KFireSaver :: resizeGL( int width, int height )
00238 {
00239        glViewport( 0, 0, width, height );
00240 
00241        glMatrixMode(GL_PROJECTION);
00242         glLoadIdentity();
00243         glOrtho( -FIELDW_2, FIELDW_2, -FIELDW_2, FIELDW_2, 5.0, 60.0 );
00244 
00245        glMatrixMode(GL_MODELVIEW);
00246         glLoadIdentity();
00247 
00248        float ratio = (float)width / (float)height,
00249              numH = 750 - 90 * parameters.particleSize,
00250              numW = 1000 - 120 * parameters.particleSize;
00251        if ( ratio >= (4.0/3.0) ) {
00252               unitX = FIELDWIDTH / (numH * ratio);
00253               unitY = FIELDWIDTH / numH;
00254        } else {
00255               unitX = FIELDWIDTH / numW;
00256               unitY = FIELDWIDTH / (numW / ratio);
00257        }
00258 
00259        timeval tv;
00260        gettimeofday(&tv,NULL);
00261        timeStampFrame = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
00262 
00263        firstGLFrame = true;
00264 }
00265 
00266 
00267 void KFireSaver :: paintGL ()
00268 /* Main procedure. It does the following:
00269    - calculate time diff between current and previous frame
00270    - clear the color buffer
00271    - simple render of stars
00272    - advanced render of particles
00273      - render
00274      - update physics based on time difference
00275      - check die/change conditions
00276        - call to explode_firework if a leader dies
00277    - if random -> start a new firework
00278    - if random -> explode a penquin or kde logo
00279 */
00280 {
00281        /* calculate TIME ELAPSED between current and previous frame */
00282 
00283        timeval tv;
00284        gettimeofday(&tv,NULL);
00285        double timeCurrent = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
00286        double timeDiff = (MSECPERIOD / 1000.0);
00287        if (parameters.enableRealtime)
00288        {
00289               timeDiff = timeCurrent - timeStampFrame;
00290               timeStampFrame = timeCurrent;
00291               timeDiff = (timeDiff > 0.5) ? 0.5 : (timeDiff < 0.005) ? 0.005 : timeDiff;
00292        }
00293 
00294        /* CLEAR SCREEN: to do it there are 2 ways */
00295 
00296        glLoadIdentity();
00297        glTranslatef( 0, 0, -50.0 );
00298        glDisable( GL_TEXTURE_2D );
00299 
00300        if ( !parameters.enableFade || firstGLFrame )
00301        {      // quick - clear the OpenGL color buffer
00302               glClearColor( 0.0, 0.0, 0.0, 1.0 );
00303               glClear( GL_COLOR_BUFFER_BIT );
00304               firstGLFrame = false;
00305        }
00306        else
00307        {      // good looking
00308               /* superpose a semi-transparent black rectangle so we
00309                  can see a sort of 'tail' for each particle drawn. */
00310               const GLfloat conv_tab[10] = {
00311                      0.50, 0.33, 0.22, 0.15, 0.10,
00312                      0.07, 0.05, 0.03, 0.02, 0.01 };
00313               glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
00314               glColor4f(0.0,0.0,0.0, conv_tab[parameters.fadeAmount]);
00315               glBegin( GL_TRIANGLE_STRIP );
00316                 glVertex2f( FIELDW_2, FIELDW_2 );
00317                 glVertex2f( -FIELDW_2, FIELDW_2 );
00318                 glVertex2f( FIELDW_2, -FIELDW_2 );
00319                 glVertex2f( -FIELDW_2, -FIELDW_2 );
00320               glEnd();
00321               glBlendFunc(GL_SRC_ALPHA,GL_ONE);
00322        }
00323 
00324        /* render STARS */
00325 
00326        if (parameters.enableStars) {
00327               if ( starTexture ) {
00328                      glEnable( GL_TEXTURE_2D );
00329                      glBindTexture( GL_TEXTURE_2D, currentTexture = starTexture );
00330               } else
00331                      glDisable( GL_TEXTURE_2D );
00332 
00333               glBegin( GL_QUADS );
00334               bool   flickers = parameters.enableStarFlickering;
00335               float  alpha = flickers ? 0.5 : 1.0;
00336               Particle * star = starList.first();
00337               for (; star; star = starList.next())
00338               {
00339                      if (flickers && DRAND<0.6)
00340                             continue;
00341 
00342                      GLfloat sizeX = star->pixelSize * unitX,
00343                             sizeY = star->pixelSize * unitY,
00344                             pLeft = star->xpos - sizeX,
00345                             pRight = star->xpos + sizeX,
00346                             pTop = star->ypos + sizeY,
00347                             pBottom = star->ypos - sizeY;
00348                      glColor4f(star->colour[0], star->colour[1], star->colour[2], alpha);
00349                      glTexCoord2f( 0, 0 );       // Bottom Left
00350                      glVertex2f( pLeft, pBottom );
00351                      glTexCoord2f( 0, 1 );       // Top Left
00352                      glVertex2f( pLeft, pTop );
00353                      glTexCoord2f( 1, 1 );       // Top Right
00354                      glVertex2f( pRight, pTop );
00355                      glTexCoord2f( 1, 0 );       // Bottom Right
00356                      glVertex2f( pRight, pBottom );
00357               }
00358               glEnd();
00359        }
00360 
00361        /* render FIREWORKS */
00362 
00363        glBegin( GL_QUADS );
00364        bool playedExplodeSound = false;
00365        bool flashedScreen = false;
00366        Particle * particle = particleList.first();
00367        for (; particle; particle = particleList.next())
00368        {
00369               //bind the texture for current particle (if not already bound)
00370               if ( !particle->texture ) {
00371                      glEnd();
00372                      glDisable( GL_TEXTURE_2D );
00373                      glBegin( GL_QUADS );
00374                      currentTexture = 0;
00375               } else if ( particle->texture != currentTexture ) {
00376                      glEnd();
00377                      glEnable( GL_TEXTURE_2D );
00378                      glBindTexture( GL_TEXTURE_2D, currentTexture = particle->texture );
00379                      glBegin( GL_QUADS );
00380               }
00381 
00382               //perspective projection (done by hand to make it funnier than opengl's :-)
00383               float mfactor = PERSP_MAG_FACTOR * particle->ypos;
00384               if ( mfactor < -246.0 ) {
00385                      particleList.remove();
00386                      particleList.prev();
00387                      continue;
00388               }
00389               float sfactor = 256.0 / (256.0 + mfactor),
00390                     posx = sfactor * particle->xpos,
00391                     posy = sfactor * particle->zpos - 4.0;
00392               //size computation (magnify if enableMegaFlares is set)
00393               if ( parameters.enableMegaFlares ) {
00394                      mfactor = parameters.megaFlares*particle->ypos;
00395                      if ( mfactor < -255.0 || mfactor > 512.0 ) {
00396                             particleList.remove();
00397                             particleList.prev();
00398                             continue;
00399                      }
00400                      sfactor = 256.0 / (256.0 + mfactor);
00401                      if ( sfactor > 64 )
00402                             sfactor = 76.8 - sfactor / 5.0;
00403               }
00404               float size = sfactor * particle->pixelSize,
00405                     sizeX = size * unitX,
00406                     sizeY = size * unitY;
00407 
00408               //determine brightness (alpha component) for the particle
00409               if ( particle->useLife ) {
00410                      float life = particle->life,
00411                            startLife = particle->startLife;
00412                      //bright changes with the following curve: "2*k - k^2" (or "k(2-k)")
00413                      if ( life > startLife )
00414                             particle->colour[3] = startLife + 1 - life;
00415                      else
00416                             particle->colour[3] = life / startLife;
00417                      //apply flickering if enabled
00418                      if (particle->flicker < 0) {
00419                             particle->colour[3] = 0.2;
00420                             if (++particle->flicker >= 0)
00421                                    particle->flicker = FLICKER_FRAMES_DELAY;
00422                      } else if (particle->flicker > 0) {
00423                             if ( life <= startLife )
00424                                    particle->colour[3] = 1.0;
00425                             if (--particle->flicker <= 0)
00426                                    particle->flicker = -FLICKER_FRAMES_DELAY;
00427                      }
00428                      glColor4fv( particle->colour );
00429               } else
00430                      glColor3fv( particle->colour );
00431 
00432               //draw particle
00433               float pLeft = posx - sizeX,
00434                     pTop = posy + sizeY,
00435                     pRight = posx + sizeX,
00436                     pBottom = posy - sizeY;
00437               glTexCoord2f( 0, 0 );       // Bottom Left
00438               glVertex2f( pLeft, pBottom );
00439               glTexCoord2f( 0, 1 );       // Top Left
00440               glVertex2f( pLeft, pTop );
00441               glTexCoord2f( 1, 1 );       // Top Right
00442               glVertex2f( pRight, pTop );
00443               glTexCoord2f( 1, 0 );       // Bottom Right
00444               glVertex2f( pRight, pBottom );
00445 
00446               //phisically update parameters of the particle
00447               particle->updateParameters( timeDiff );
00448 
00449               //check for particle death / explosion
00450               switch (particle->particleType)
00451               {
00452                      //a Fireparticle is restarted when in right conditions
00453                   case Particle::FireParticle:
00454                      if ( posx < -FIELDW_2 || posx > FIELDW_2 ||
00455                             (particle->zpos < -10.0 && posy < -FIELDW_2) )
00456                      {
00457                             particle->initializeValues();
00458                             if ( DRAND > 0.9995 )
00459                                    particle->zspeed *= 4;
00460                      }
00461                      break;
00462 
00463                      //a leader explodes when his z speed drops to zero
00464                      //or, if it uses life, at death
00465                   case Particle::FireWorkLeaderParticle:
00466                      if ((particle->zspeed <= 0.0f && !particle->useLife) ||
00467                          (particle->useLife && particle->life <= 0.0) )
00468                      {
00469                             // play sound if enabled (and once per frame)
00470                             if (parameters.enableSound && !playedExplodeSound)
00471                             {
00472                                    playSound(sound_explosion);
00473                                    playedExplodeSound = true;
00474                             }
00475                             // flash screen if enabled
00476                             if (parameters.enableFlash && !flashedScreen) {
00477                                    glEnd();
00478                                    glDisable( GL_TEXTURE_2D );
00479                                    glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
00480                                    glBegin( GL_TRIANGLE_STRIP );
00481                                    glVertex2f( FIELDW_2, FIELDW_2 );
00482                                    glVertex2f( -FIELDW_2, FIELDW_2 );
00483                                    glVertex2f( FIELDW_2, -FIELDW_2 );
00484                                    glVertex2f( -FIELDW_2, -FIELDW_2 );
00485                                    glEnd();
00486                                    if ( particleTexture )
00487                                           glEnable( GL_TEXTURE_2D );
00488                                    glBegin( GL_QUADS );
00489                                    flashedScreen = true;
00490                             }
00491                             // generating children and removing parent
00492                             int elementIndex = particleList.at();
00493                             explodeFirework(particle);
00494                             particleList.remove(elementIndex);
00495                             particleList.prev();
00496                      } else if ( parameters.enableTrails && DRAND < 0.4 ) {
00497                             // leave trail behind the particle (it'a small and slow red debris)
00498                             Particle * p = new Particle( Particle::FireWorkDebrisParticle );
00499                             p->initializeValues( 0, particle, 1, 1 );
00500                             p->texture = particleTexture;
00501                             p->xspeed /= 4;
00502                             p->yspeed /= 4;
00503                             p->zspeed /= 8;
00504                             p->zacc /= 4;
00505                             p->pixelSize = 2;
00506                             p->colour[0] /= 2;
00507                             int elementIndex = particleList.at();
00508                             particleList.append( p );
00509                             particleList.at( elementIndex );
00510                      }
00511                      break;
00512 
00513                      //remove if dead or outside field
00514                   default:
00515                      if (particle->life <= 0.0 || posx<-FIELDW_2 || posx>FIELDW_2 || posy<-FIELDW_2) {
00516                             particleList.remove();
00517                             particleList.prev();
00518                      }
00519                      break;
00520               }
00521        }
00522        glEnd();
00523 
00524        /* render WRITINGS */
00525 
00526        if ( parameters.enableWritings )
00527        {
00528               int chance = (int) (1000.0 * DRAND);
00529               if ( !chance ) {
00530                      static const QString someStrings[] = {
00531                             i18n("www.kde.org"),
00532                             i18n("Conquer your desktop!"),
00533                             i18n("KFIRESAVER 3D"),
00534                             i18n("Thank you for using KDE"),
00535                      };
00536                      int n = (int)(4.0 * DRAND);
00537                      writer->spawnWords( someStrings[n], Writer::Fun1 );
00538               }
00539               writer->render( timeDiff );
00540        }
00541 
00542        /* generate a new FIREWORK_LEADER */
00543 
00544        int random = (int) ((float)parameters.fireworksFrequency * DRAND);
00545        if (showp.ShowType == Show)
00546        {
00547               //double the chances ('frequency') to raise a new leaderParticle
00548               //but envelop it under a sine function
00549               float step = (showp.timeStamp - timeCurrent) / showp.timeGap;
00550               if (DRAND > sin(M_PI*step))
00551                      random = -1;
00552               if (showp.type == AngelHairs && DRAND < 0.5)
00553                      random = -1;
00554        }
00555        if ( !random )
00556        {
00557               Particle * particle = new Particle( Particle::FireWorkLeaderParticle );
00558               particle->initializeValues();
00559               particle->texture = flareTexture;
00560               particleList.append( particle );
00561        }
00562 
00563        /* explode a logo */
00564 
00565        int logoImages = imageList.count();
00566        if ( logoImages > 0 ) {
00567               random = (int) (parameters.logoFrequency * logoImages * 200.0 * DRAND);
00568               if ( random < logoImages )
00569               {
00570                      if (parameters.enableFlash && !flashedScreen) {
00571                             glDisable( GL_TEXTURE_2D );
00572                             glColor4f( 1,1,1, parameters.flashOpacity / 10.0 );
00573                             glBegin( GL_TRIANGLE_STRIP );
00574                             glVertex2f( FIELDW_2, FIELDW_2 );
00575                             glVertex2f( -FIELDW_2, FIELDW_2 );
00576                             glVertex2f( FIELDW_2, -FIELDW_2 );
00577                             glVertex2f( -FIELDW_2, -FIELDW_2 );
00578                             glEnd();
00579                      }
00580                      burnLogo( imageList.at(random) );
00581               }
00582        }
00583 }
00584 
00585 
00586 int KFireSaver :: pickColour()
00587 {
00588        int color = (int) (DRAND * parameters.colorsCount);
00589        return parameters.colorsT[ color ];
00590 }
00591 
00592 
00593 KFireSaver :: enumFireworkType KFireSaver :: pickType()
00594 {
00595        int type = (int) (DRAND * parameters.typesCount);
00596        return parameters.typesT[ type ];
00597 }
00598 
00599 
00600 void KFireSaver :: explodeFirework(Particle* leaderParticle)
00601 {
00602        GLfloat displace[3] = {0.0,0.0,0.0};
00603        float tmp1 = 0.0, tmp2 = 0.0, tmp3 = 0.0, tmp4 = 0.0, tmp5 = 0.0;
00604 
00605        // color of exploded particles
00606        bool bicolor = parameters.enableCombos && (showp.forceBicolour || DRAND > 0.95),
00607             flickers = false;
00608        int cscheme = showp.forceColour ? showp.colour : pickColour(),
00609            cscheme2 = showp.forceColour ? showp.colourSec : pickColour();
00610 
00611        // randomize type of exploding firework
00612        enumFireworkType fwType =
00613               showp.forceType ? (enumFireworkType) showp.type : pickType();
00614 
00615        // other options for generated particles
00616        int number = (int) ((DRAND + DRAND) * 150.0);
00617        float power = showp.forcePower ?
00618               showp.powerEnvelop * (0.8 + 0.3*DRAND) :
00619               DRAND * 11.0 + 2.0,
00620              powermin = DRAND * power;
00621 
00622        // now some rules ...
00623         //a splitter can't split up more than 2 times
00624        if (fwType == Splitter && leaderParticle->explosionsDepth > 1) {
00625               if (parameters.typesCount == 1)
00626                      return;
00627               if (showp.forceType)
00628                      fwType = showp.typeSec;
00629               if (fwType == Splitter)
00630                      while ( (fwType = pickType()) == Splitter );
00631        }
00632 
00633        // PRE-ADJUST parameters for the firework we're creating
00634        switch ( fwType )
00635        {
00636               //no need to handle this. it's the default configuration.
00637            case Sphere:
00638               break;
00639 
00640               //explosion whithout emitting particles, only a flash
00641            case NoFW:
00642               number = 1;
00643               power = powermin = 0;
00644               break;
00645 
00646               //splits up into 'number' orange pieces. tmp1 holds base_life
00647            case Splitter:
00648               cscheme = showp.forceColour ? showp.colour : 1;
00649               bicolor = false;
00650               number = 3 + (int) (DRAND * 4);
00651               power /= 2.0;
00652               powermin = power / 2.0;
00653               tmp1 = 0.4 + DRAND * 1.5;
00654               break;
00655 
00656               //randomize a couple of angles (phi - theta) for exploding circle
00657            case BiCircle:
00658               number *= 2;
00659            case Circle:
00660               power = DRAND * 5.0 + 4.0;
00661               tmp1 = DRAND * M_PI;
00662               tmp2 = DRAND * M_PI;
00663               tmp4 = cos(tmp2);    //c2
00664               tmp3 = sin(tmp2);    //s2
00665               tmp2 = cos(tmp1);    //c1
00666               tmp1 = sin(tmp1);    //s1
00667               break;
00668 
00669               //cascade of flickering orange particles
00670            case AngelHairs:
00671               cscheme = showp.forceColour ? showp.colour : 1;
00672               bicolor = false;
00673               flickers = true;
00674               power = 0.8 + DRAND * 1.9;
00675               powermin = DRAND*0.5;
00676               number = 100 + (int)(DRAND * 150);
00677               displace[0] = -leaderParticle->xspeed/2;
00678               displace[1] = -leaderParticle->yspeed/2;
00679               displace[2] = power;
00680               break;
00681 
00682               //behave as a standard spherical firework
00683            case Spirals:
00684               break;
00685 
00686               //not yet implemented, suppressing particles
00687            case SuperNova:
00688            case NoRender:
00689               number = 0;
00690               break;
00691        }
00692 
00693        //limit number of particles as we are getting to the capacity saturation
00694        float currentParticles = (float) particleList.count();
00695        const float particleCapacity = 15000;
00696        const float particleGap = 8000;
00697        if ( number > 10 && currentParticles > (particleCapacity - particleGap) )
00698        {
00699               //NoFW, Splitter and NoRender aren't limited.
00700               number = (int)( (float)number * (particleCapacity - currentParticles) / particleGap );
00701               if ( number < 10 )
00702                      number = 0;
00703        }
00704 
00705        int newExplosionsDepth = leaderParticle->explosionsDepth + 1;
00706        for (int i=0 ; i<number ; i++)
00707        {
00708               Particle * particle;
00709               if ( fwType == Spirals )
00710                      particle = new TurningParticle( Particle::FireWorkDebrisParticle );
00711               else
00712                      particle = new Particle( Particle::FireWorkDebrisParticle );
00713 
00714               particle->initializeValues (
00715                      bicolor && (i>number/2) ? cscheme2 : cscheme,
00716                      leaderParticle, powermin, power,
00717                      flickers, displace );
00718               particle->texture = particleTexture;
00719               particle->explosionsDepth = newExplosionsDepth;
00720 
00721               // POST-ADJUST particle coefficients adapting to current FireworkType
00722               switch ( fwType )
00723               {
00724                      //create a big, white particle, simulating explosion
00725                   case NoFW:
00726                      if (parameters.enableFade)
00727                             particle->startLife = particle->life = 0.030;
00728                      else
00729                             particle->startLife = particle->life = 0.075;
00730                      particle->texture = flareTexture;
00731                      particle->colour[0]=
00732                      particle->colour[1]=
00733                      particle->colour[2]=1.0;
00734                      particle->pixelSize = 50.0 + 75.0 * DRAND;
00735                      particle->zacc = 0;
00736                      break;
00737 
00738                      //default. no need to change parameters, only create the
00739                      //'sphere light' as the first big particle if set.
00740                   case Sphere:
00741                      if (i==0 && parameters.enableSphereLight && number > 40) {
00742                             particle->texture = flareTexture;
00743                             particle->xspeed = leaderParticle->xspeed;
00744                             particle->yspeed = leaderParticle->yspeed;
00745                             particle->zspeed = 0.0;
00746                             particle->colour[0] /= 2.0;
00747                             particle->colour[1] /= 2.0;
00748                             particle->colour[2] /= 2.0;
00749                             float impression = power * (float)number/5.0;
00750                             particle->pixelSize = 25.0 * DRAND + impression;
00751                             if (parameters.enableFade) {
00752                                    particle->startLife = particle->life = 0.040;
00753                             } else {
00754                                    particle->startLife = 1.3;
00755                                    particle->life = 1.8;
00756                             }
00757                      }
00758                      break;
00759 
00760                      //
00761                   case Splitter:
00762                      particle->particleType = Particle::FireWorkLeaderParticle;
00763                      particle->pixelSize *= 3.0;
00764                      particle->startLife = particle->life = tmp1 * (0.75 + DRAND/3.0);
00765                      if (particle->zspeed < 0)
00766                             particle->zspeed = -particle->zspeed;
00767                      break;
00768 
00769                   case Circle:
00770                   case BiCircle:
00771                      tmp5 = 2 * M_PI * DRAND;
00772                      //MMX can be useful.. if you know how to use it :-)
00773                      if ( fwType == BiCircle && i > number/2 ) {
00774                             GLfloat ey = cos(tmp5),
00775                                    ez = sin(tmp5);
00776                             particle->xspeed = power * (                tmp3*ez );
00777                             particle->yspeed = power * ( tmp2*ey - tmp1*tmp4*ez );
00778                             particle->zspeed = power * ( tmp1*ey + tmp2*tmp4*ez );
00779                      } else {
00780                             GLfloat ex = sin(tmp5),
00781                                    ey = cos(tmp5);
00782                             particle->xspeed = power * (       tmp4*ex           );
00783                             particle->yspeed = power * (  tmp1*tmp3*ex + tmp2*ey );
00784                             particle->zspeed = power * ( -tmp2*tmp3*ex + tmp1*ey );
00785                      }
00786                      break;
00787 
00788                   case AngelHairs:
00789                      particle->zacc = -9.807 * (0.05 + DRAND*0.20);
00790                      particle->life = 2.0 + DRAND*2.5;
00791                      particle->startLife = particle->life - 1;
00792                      if (particle->zspeed < 0)
00793                             particle->zspeed *= -1;
00794                      //particle->pixelSize = 5.0;
00795                      break;
00796 
00797                   case Spirals:
00798                      particle->texture = diastarTexture;
00799                      break;
00800 
00801                      //discard cases
00802                   case SuperNova:
00803                   case NoRender:
00804                      break;
00805               }
00806               particleList.append(particle);
00807        }
00808 }
00809 
00810 void KFireSaver :: timerEvent(QTimerEvent*)
00811 {
00812        timeval tv;
00813        gettimeofday(&tv,NULL);
00814        double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
00815 
00816        bool   chooseType = false,
00817               chooseColor = false,
00818               chooseOthers = false,
00819               updateTimings = false;
00820        bool   firstTime = showp.timeStamp == 0.0;
00821        bool   endOfScene = currentTime >= showp.timeStamp;
00822 
00823        if (firstTime)
00824        switch (showp.ShowType)
00825        {
00826            case Monotype:
00827               /* first time choose the type, color and attributes will
00828                  be chosen randomly for every firework which explodes*/
00829               showp.forceType = true;
00830               chooseType = true;
00831               break;
00832 
00833            case Monochrome:
00834               /* first time choose the color, type and attributes will
00835                  be chosen randomly for every firework which explodes*/
00836               showp.forceColour = true;
00837               chooseColor =
00838               chooseOthers = true;
00839               break;
00840 
00841            default: break;
00842        }
00843 
00844        if (endOfScene || firstTime)
00845        switch (showp.ShowType)
00846        {
00847            case Show:
00848               /* if a scene ended, randomize global parameters for the
00849                  whole next scene */
00850               showp.forceType = true;
00851               showp.forceColour = true;
00852               showp.forcePower = true;
00853               chooseOthers =
00854               chooseColor =
00855               chooseType = true;
00856               updateTimings = true;
00857               break;
00858 
00859            default: break;
00860        }
00861 
00862        if ( chooseType )
00863        {
00864               showp.type = pickType();
00865               if (parameters.typesCount < 2)
00866                      showp.typeSec = NoRender;
00867               else
00868                      while ((showp.typeSec = pickType()) == showp.type);
00869        }
00870        if ( chooseColor ) {
00871               showp.colour = pickColour();
00872               showp.colourSec = pickColour();
00873        }
00874        if ( chooseOthers )
00875        {
00876               showp.powerEnvelop = DRAND * 8.0 + 3.0;
00877               if (DRAND > 0.9)
00878               {
00879                      showp.forceBicolour = true;
00880                      showp.colourSec = pickColour();
00881               } else
00882                      showp.forceBicolour = false;
00883        }
00884        if ( firstTime || updateTimings )
00885        {
00886               if (DRAND < 0.2)
00887                      showp.timeGap = 1.0 + DRAND * 2.0;
00888               else
00889                      showp.timeGap = 3.0 + DRAND * 10.0;
00890               showp.timeStamp = currentTime + showp.timeGap;
00891               showp.timeGap /= 1.5; //hack to introduce delay in sine func
00892        }
00893 
00894        updateGL();
00895 }
00896 
00897 void KFireSaver :: burnLogo(QImage * image)
00898 {
00899        if (!image || image->isNull())
00900               return;
00901        int step = parameters.enableReduceLogo ? 2 : 1,
00902            imageW = image->width(),
00903            imageH = image->height(),
00904            offsetX = imageW / 2,
00905            offsetY = imageH / 2;
00906        float speed = FIELDW_4 / (imageW > imageH ? imageW : imageH),
00907              speedXOffs = 5 * (DRAND - DRAND),
00908              speedYOffs = DRAND + DRAND + 1;
00909        //if image is too big, lower sample points
00910        while ((imageW/step)>96 || (imageH/step)>96)
00911               step *= 2;
00912        for (int y=0 ; y<imageH ; y+=step)
00913        {
00914               for (int x=0 ; x<imageW ; x+=step)
00915               {
00916                      QRgb pixel = image->pixel(x,y);
00917                      if ( qAlpha(pixel) < 250 )
00918                             continue;
00919                      //if ( DRAND > 0.9 )
00920                      //     continue;
00921 
00922                      Particle* particle = new Particle( Particle::LogoParticle );
00923                      particle->initializeValues();
00924                      particle->texture = particleTexture;
00925 
00926                      float xI = (x - offsetX) ,
00927                            yI = (offsetY - y) ;
00928                      particle->xpos = xI * speed * 0.5;
00929                      particle->zpos = yI * speed * 0.5 + 5;
00930                      particle->xspeed = xI * speed + speedXOffs;
00931                      particle->zspeed = yI * speed + speedYOffs;
00932 
00933                      particle->colour[0] = qRed(pixel) / 255.0f;
00934                      particle->colour[1] = qGreen(pixel) / 255.0f;
00935                      particle->colour[2] = qBlue(pixel) / 255.0f;
00936 
00937                      particleList.append(particle);
00938               }
00939        }
00940        if (parameters.enableSound)
00941               playSound(sound_debris);
00942 }
00943 
00944 void KFireSaver :: playSound(QString file)
00945 {
00946        //flush inactive players
00947        KPlayObject * playObject = playObjectList.first();
00948        while ( playObject )
00949        {
00950               if ( playObject->state() != Arts::posPlaying )
00951               {
00952                      playObjectList.remove();
00953                      playObject = playObjectList.current();
00954               } else
00955                      playObject = playObjectList.next();
00956        }
00957 
00958        //discart this sound if the player queue is already full (4 channels playing)
00959        if ( playObjectList.count() >= 6 )
00960               return;
00961 
00962        // not needed when all of the files are in the distribution
00963        //if (!QFile::exists(file))
00964               //return;
00965 
00966        KPlayObjectFactory factory(artsServer.server());
00967        playObject = factory.createPlayObject(KUrl(file), true);
00968 
00969        if (playObject && !playObject->isNull())
00970        {
00971               playObject->play();
00972               playObjectList.append(playObject);
00973        }
00974 }
00975 
00976 bool KFireSaver :: loadTexture( QString fileName, unsigned int & textureID )
00977 {
00978        //reset texture ID to the default EMPTY value
00979        textureID = 0;
00980 
00981        //load image
00982        QImage tmp;
00983        if ( !tmp.load( fileName ) )
00984               return false;
00985 
00986        //convert it to suitable format (flipped RGBA)
00987        QImage texture = QGLWidget::convertToGLFormat( tmp );
00988        if ( texture.isNull() )
00989               return false;
00990 
00991        //get texture number and bind loaded image to that texture
00992        glGenTextures( 1, &textureID );
00993        glBindTexture( GL_TEXTURE_2D, textureID );
00994        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
00995        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
00996        glTexImage2D( GL_TEXTURE_2D, 0, 4 /* 3 ??? */, texture.width(), texture.height(),
00997               0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
00998        return true;
00999 }
01000 
01001 void KFireSaver :: freeTexture( unsigned int & textureID )
01002 {
01003        if ( textureID > 0 )
01004            glDeleteTextures( 1, &textureID );
01005        textureID = 0;
01006 }
01007 
01008 void KFireSaver :: readConfig ()
01009 {
01010        KConfig config("kfiresaverrc",true,false);
01011 
01012        // show
01013        config.setGroup( "Show" );
01014        showp.ShowType = (enum enumShowType)config.readEntry( "ShowType", 1 );
01015        parameters.fireworksFrequency = 11 - config.readEntry( "FireworksFrequency", 7 );
01016        if ( parameters.fireworksFrequency < 1 )
01017               parameters.fireworksFrequency = 1;
01018        if ( parameters.fireworksFrequency > 11 )
01019               parameters.fireworksFrequency = 11;
01020        parameters.fireworksFrequency *= (parameters.fireworksFrequency + 1); //*karl gauss's sum*
01021        parameters.particleSize = config.readEntry( "ParticlesSize", 0 );
01022        if ( parameters.particleSize < -5 )
01023               parameters.particleSize = -5;
01024        if ( parameters.particleSize > 5 )
01025               parameters.particleSize = 5;
01026        if ( parameters.enableBottomFire = config.readEntry( "enable-BottomFire", true ) )
01027        {
01028               QColor blue = Qt::darkBlue;
01029               parameters.bottomFireColor = config.readEntry( "BottomFireColor", blue );
01030        }
01031        parameters.enableSound = config.readEntry( "enable-Sounds", false );
01032        parameters.enableNoOverhead = config.readEntry( "enable-NoOverhead", true );
01033        parameters.enableRealtime = config.readEntry( "enable-FrameSkip", true );
01034 
01035        // fireworks
01036        config.setGroup( "Fireworks" );
01037        parameters.typesCount = 0;
01038        if ( config.readEntry( "use-Classic", true ) )
01039               parameters.typesT[parameters.typesCount++] = Sphere;
01040        if ( config.readEntry( "use-Explosion", false ) )
01041               parameters.typesT[parameters.typesCount++] = NoFW;
01042        if ( config.readEntry( "use-FlameRing", false ) )
01043               parameters.typesT[parameters.typesCount++] = Circle;
01044        if ( config.readEntry( "use-FlameWorld", false ) )
01045               parameters.typesT[parameters.typesCount++] = BiCircle;
01046        if ( config.readEntry( "use-Fall", false ) )
01047               parameters.typesT[parameters.typesCount++] = AngelHairs;
01048        if ( config.readEntry( "use-Splitter", false ) )
01049               parameters.typesT[parameters.typesCount++] = Splitter;
01050        if ( config.readEntry( "use-Spirals", false ) )
01051               parameters.typesT[parameters.typesCount++] = Spirals;
01052        if ( config.readEntry( "use-SuperNova", false ) )
01053               parameters.typesT[parameters.typesCount++] = SuperNova;
01054        if ( !parameters.typesCount ) {
01055               kWarning() << "KFireSaver3D: Warning, no fireworks enabled in config file" ;
01056               kWarning() << "    enabling 'Classic Spherical'" ;
01057               parameters.typesCount = 1;
01058               parameters.typesT[0] = Sphere;
01059        }
01060        parameters.typesT[ parameters.typesCount ] =
01061               parameters.typesT[ parameters.typesCount-1 ];
01062        parameters.colorsCount = 0;
01063        if ( config.readEntry( "use-Red", false ) )
01064               parameters.colorsT[parameters.colorsCount++] = 0;
01065        if ( config.readEntry( "use-Orange", true ) )
01066               parameters.colorsT[parameters.colorsCount++] = 1;
01067        if ( config.readEntry( "use-Green", false ) )
01068               parameters.colorsT[parameters.colorsCount++] = 2;
01069        if ( config.readEntry( "use-Blue", false ) )
01070               parameters.colorsT[parameters.colorsCount++] = 3;
01071        if ( config.readEntry( "use-White", true ) )
01072               parameters.colorsT[parameters.colorsCount++] = 4;
01073        if ( config.readEntry( "use-Purple", false ) )
01074               parameters.colorsT[parameters.colorsCount++] = 5;
01075        if ( config.readEntry( "use-DeepGreen", true ) )
01076               parameters.colorsT[parameters.colorsCount++] = 6;
01077        if ( !parameters.colorsCount )
01078        {
01079               kWarning() << "KFireSaver3D: Warning enable at least one color" ;
01080               kWarning() << "    enabling 'Blinding White'" ;
01081               parameters.colorsCount = 1;
01082               parameters.colorsT[0] = 4;
01083        }
01084        parameters.colorsT[ parameters.colorsCount ] =
01085               parameters.colorsT[ parameters.colorsCount-1 ];
01086        parameters.enableCombos = config.readEntry( "use-Multicolor", true );
01087 
01088        // specials
01089        config.setGroup( "Specials" );
01090        if ( parameters.enableLogos = config.readEntry( "enable-Logos", true ) )
01091        {
01092               QImage tempImage;
01093               tempImage.setAlphaBuffer( true );
01094               if ( config.readEntry( "LogosTux", true ) )
01095                      if ( tempImage.load(locate("data","kfiresaver/kfs_tux.png")) )
01096                             imageList.append( new QImage(tempImage) );
01097               if ( config.readEntry( "LogosKonqui", true ) )
01098                      if ( tempImage.load(locate("data","kfiresaver/kfs_kde.png")) )
01099                             imageList.append( new QImage(tempImage) );
01100               if ( config.readEntry( "LogosKDEIcons", true ) ) {
01101                      const QString icons[] = {
01102                             "media-floppy", "media-optical", "drive-harddisk", "kmix",
01103                             "network-wired", "computer", "user-home", "konqueror",
01104                             "internet-mail", "graphics-viewer-document", "user-identity" };
01105                      for ( int i = 0; i < 11; i++ )
01106                             imageList.append( new QImage(DesktopIcon(icons[i],64).convertToImage()) );
01107               }
01108               parameters.enableReduceLogo = config.readEntry( "LogosReduceDetail", true );
01109               parameters.logoFrequency = 11 - config.readEntry( "LogosFrequency", 4 );
01110               if ( parameters.logoFrequency < 1 )
01111                      parameters.logoFrequency = 1;
01112               if ( parameters.logoFrequency > 11 )
01113                      parameters.logoFrequency = 11;
01114        }
01115        if ( parameters.enableStars = config.readEntry( "enable-Stars", true ) )
01116        {
01117               parameters.enableStarFlickering = config.readEntry( "StarsFlicker", false );
01118               parameters.enableStarGradient = config.readEntry( "StarsGradient", true );
01119               parameters.starsNumber = config.readEntry( "StarsNumber", 4 );
01120               if ( parameters.starsNumber < 0 )
01121                      parameters.starsNumber = 0;
01122               if ( parameters.starsNumber > 10 )
01123                      parameters.starsNumber = 10;
01124        }
01125        parameters.enableWritings = config.readEntry( "enable-Writings", true );
01126 
01127        // effects
01128        config.setGroup( "Effects" );
01129        parameters.enableSphereLight = config.readEntry( "enable-SphericalLight", true );
01130        if ( parameters.enableFlash = config.readEntry( "enable-Flash", false ) )
01131        {
01132               parameters.flashOpacity = config.readEntry( "FlashOpacity", 5 );
01133               if ( parameters.flashOpacity < 0 )
01134                      parameters.flashOpacity = 0;
01135               if ( parameters.flashOpacity > 10 )
01136                      parameters.flashOpacity = 10;
01137        }
01138        if ( parameters.enableFade = config.readEntry( "enable-Fade", false ) )
01139        {
01140               parameters.fadeAmount = config.readEntry( "FadeIntensity", 3 );
01141               if ( parameters.fadeAmount < 0 )
01142                      parameters.fadeAmount = 0;
01143               if ( parameters.fadeAmount > 9 )
01144                      parameters.fadeAmount = 9;
01145        }
01146        if ( parameters.enableMegaFlares = config.readEntry( "enable-Flares", true ) )
01147        {
01148               parameters.megaFlares = config.readEntry( "FlaresDimension", 5 );
01149               if ( parameters.megaFlares < 0 )
01150                      parameters.megaFlares = 0;
01151               if ( parameters.megaFlares > 10 )
01152                      parameters.megaFlares = 10;
01153               parameters.megaFlares += 4;
01154               parameters.megaFlares *= 2;
01155        }
01156        parameters.enableTrails = config.readEntry( "enable-Trail", false );
01157 }