diff options
Diffstat (limited to 'tdescreensaver/kdesavers/firesaver.cpp')
-rw-r--r-- | tdescreensaver/kdesavers/firesaver.cpp | 1151 |
1 files changed, 1151 insertions, 0 deletions
diff --git a/tdescreensaver/kdesavers/firesaver.cpp b/tdescreensaver/kdesavers/firesaver.cpp new file mode 100644 index 00000000..71954242 --- /dev/null +++ b/tdescreensaver/kdesavers/firesaver.cpp @@ -0,0 +1,1151 @@ +// This file is part of KFireSaver3D. + +// KFireSaver3D is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. + +// KFireSaver3D is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with KFireSaver3D; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +// Author: Enrico Ros, based on the great work of David Sansome (kfiresaver) +// Email: [email protected] + +#include <math.h> +#include <sys/time.h> +#include <stdlib.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kurl.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <arts/kmedia2.h> +#include <arts/kplayobject.h> +#include <arts/kplayobjectfactory.h> + +#include "firesaversetup.h" +#include "firesaverparticle.h" +#include "firesaverwriter.h" +#include "firesaver.h" + + +/* Factory code for KScreensaver begins *here* *\ +\* */ + +#include <tdescreensaver.h> +#include <kdialogbase.h> + +class KFireSaverKSS : public KScreenSaver +{ + public: + KFireSaverKSS( WId id ) + : KScreenSaver( id ) + { + setBackgroundColor( black ); + erase(); + saver = new KFireSaver; + embed(saver); + saver->show(); + } + + ~KFireSaverKSS() + { + delete saver; + } + + private: + KFireSaver* saver; +}; + +class KFireSaverSetupKDB : public KDialogBase +{ + public: + KFireSaverSetupKDB( TQWidget* parent = 0, const char* name = 0 ) + : KDialogBase( parent, name, true, i18n("Setup Screen Saver"), + Ok | Cancel | Help, Ok, true ) + { + setup = new KFireSaverSetup( this ); + setMainWidget( setup ); + setButtonText( KDialogBase::Help, i18n( "A&bout" ) ); + } + + private slots: + void slotHelp() + { + KMessageBox::about(this, i18n("<h3>KFireSaver 3D 1.0</h3>\n<p>TEST Koral - Enrico Ros::2004</p>") ); + } + void slotOk() + { + setup->writeConfig(); + accept(); + } + + private: + KFireSaverSetup * setup; +}; + +extern "C" +{ + KDE_EXPORT const char *kss_applicationName = "kfiresaver.kss"; + KDE_EXPORT const char *kss_description = I18N_NOOP( "Fireworks 3D (GL)" ); + KDE_EXPORT const char *kss_version = "0.7"; + + KDE_EXPORT KScreenSaver *kss_create( WId id ) + { + return new KFireSaverKSS( id ); + } + + KDE_EXPORT TQDialog *kss_setup() + { + return new KFireSaverSetupKDB; + } +} + +/* *\ +\* Factory code for KScreensaver ends *here* */ + + +KFireSaver :: KFireSaver( TQWidget *parent, const char *name ) + : TQGLWidget( parent, name ) +{ + // set random seed to initialize drand48() calls + timeval tv; + gettimeofday(&tv,NULL); + srand48( (long)tv.tv_usec ); + + readConfig(); + + particleList.setAutoDelete(true); + starList.setAutoDelete(true); + imageList.setAutoDelete(true); + playObjectList.setAutoDelete(true); + + //initialize openGL context before managing GL calls + makeCurrent(); + loadTexture( locate("data","kfiresaver/kfs_particle.png"), particleTexture ); + loadTexture( locate("data","kfiresaver/kfs_particle_flare.png"), flareTexture ); + loadTexture( locate("data","kfiresaver/kfs_particle_diastar.png"), diastarTexture ); + starTexture = particleTexture; + + //generate stars + if (parameters.enableStars) { + int number = parameters.starsNumber + 1; + number *= 10 * number; + for (int i=0 ; i<number ; i++) + { + Particle * star = new Particle( Particle::StarParticle ); + star->initializeValues(); + star->texture = starTexture; + if (parameters.enableStarGradient) + { + GLfloat red = star->colour[0], + green = star->colour[1], + blue = star->colour[2], + tint = 0.5 + star->ypos / FIELDWIDTH, + merge = 0.3 + DRAND*0.7, + mergen = 1 - merge; + star->colour[0] = red * merge + (1.0-tint) * mergen; + star->colour[1] = green * merge + tint * mergen; + star->colour[2] = blue * merge + tint * mergen; + } + starList.append( star ); + } + } + + //generate bottom fire + if (parameters.enableBottomFire) { + float cRed = (float)parameters.bottomFireColor.red() / 255.0, + cGreen = (float)parameters.bottomFireColor.green() / 255.0, + cBlue = (float)parameters.bottomFireColor.blue() / 255.0; + for (int i=0 ; i<NUMBER_OF_FIREPARTICLES ; i++) + { + Particle* particle = new Particle( Particle::FireParticle ); + particle->initializeValues(); + particle->texture = particleTexture; + particle->zspeed *= 4.0; + particle->colour[0] = cRed * (0.6 + 0.4*DRAND); + particle->colour[1] = cGreen * (0.6 + 0.4*DRAND); + particle->colour[2] = cBlue * (0.6 + 0.4*DRAND); + particleList.append(particle); + } + } + + //get sound files + if (parameters.enableSound) { + sound_explosion = locate("data","kfiresaver/kfs_explode.ogg"); + sound_debris = locate("data","kfiresaver/kfs_debris.ogg"); + } + + //create the writer class that manages flying writings. + if ( parameters.enableWritings ) + writer = new Writer("kfs_letters.desc"); + + showp.forceBicolour = + showp.forceColour = + showp.forcePower = + showp.forceType = false; + showp.timeStamp = 0.0; + startTimer(MSECPERIOD); + + //force initialization of "show" variables for the first time + timerEvent(NULL); +} + + +KFireSaver :: ~KFireSaver() +{ + freeTexture( particleTexture ); + freeTexture( starTexture ); + particleList.clear(); + starList.clear(); + imageList.clear(); + playObjectList.clear(); + if ( parameters.enableWritings ) + delete writer; +} + + +void KFireSaver :: initializeGL() +{ + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + glShadeModel( GL_SMOOTH ); + + resizeGL( 640, 480 ); +} + + +void KFireSaver :: resizeGL( int width, int height ) +{ + glViewport( 0, 0, width, height ); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho( -FIELDW_2, FIELDW_2, -FIELDW_2, FIELDW_2, 5.0, 60.0 ); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float ratio = (float)width / (float)height, + numH = 750 - 90 * parameters.particleSize, + numW = 1000 - 120 * parameters.particleSize; + if ( ratio >= (4.0/3.0) ) { + unitX = FIELDWIDTH / (numH * ratio); + unitY = FIELDWIDTH / numH; + } else { + unitX = FIELDWIDTH / numW; + unitY = FIELDWIDTH / (numW / ratio); + } + + timeval tv; + gettimeofday(&tv,NULL); + timeStampFrame = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; + + firstGLFrame = true; +} + + +void KFireSaver :: paintGL () +/* Main procedure. It does the following: + - calculate time diff between current and previous frame + - clear the color buffer + - simple render of stars + - advanced render of particles + - render + - update physics based on time difference + - check die/change conditions + - call to explode_firework if a leader dies + - if random -> start a new firework + - if random -> explode a penquin or kde logo +*/ +{ + /* calculate TIME ELAPSED between current and previous frame */ + + timeval tv; + gettimeofday(&tv,NULL); + double timeCurrent = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; + double timeDiff = (MSECPERIOD / 1000.0); + if (parameters.enableRealtime) + { + timeDiff = timeCurrent - timeStampFrame; + timeStampFrame = timeCurrent; + timeDiff = (timeDiff > 0.5) ? 0.5 : (timeDiff < 0.005) ? 0.005 : timeDiff; + } + + /* CLEAR SCREEN: to do it there are 2 ways */ + + glLoadIdentity(); + glTranslatef( 0, 0, -50.0 ); + glDisable( GL_TEXTURE_2D ); + + if ( !parameters.enableFade || firstGLFrame ) + { // quick - clear the OpenGL color buffer + glClearColor( 0.0, 0.0, 0.0, 1.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + firstGLFrame = false; + } + else + { // good looking + /* superpose a semi-transparent black rectangle so we + can see a sort of 'tail' for each particle drawn. */ + const GLfloat conv_tab[10] = { + 0.50, 0.33, 0.22, 0.15, 0.10, + 0.07, 0.05, 0.03, 0.02, 0.01 }; + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.0,0.0,0.0, conv_tab[parameters.fadeAmount]); + glBegin( GL_TRIANGLE_STRIP ); + glVertex2f( FIELDW_2, FIELDW_2 ); + glVertex2f( -FIELDW_2, FIELDW_2 ); + glVertex2f( FIELDW_2, -FIELDW_2 ); + glVertex2f( -FIELDW_2, -FIELDW_2 ); + glEnd(); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + } + + /* render STARS */ + + if (parameters.enableStars) { + if ( starTexture ) { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, currentTexture = starTexture ); + } else + glDisable( GL_TEXTURE_2D ); + + glBegin( GL_QUADS ); + bool flickers = parameters.enableStarFlickering; + float alpha = flickers ? 0.5 : 1.0; + Particle * star = starList.first(); + for (; star; star = starList.next()) + { + if (flickers && DRAND<0.6) + continue; + + GLfloat sizeX = star->pixelSize * unitX, + sizeY = star->pixelSize * unitY, + pLeft = star->xpos - sizeX, + pRight = star->xpos + sizeX, + pTop = star->ypos + sizeY, + pBottom = star->ypos - sizeY; + glColor4f(star->colour[0], star->colour[1], star->colour[2], alpha); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex2f( pLeft, pBottom ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex2f( pLeft, pTop ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex2f( pRight, pTop ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex2f( pRight, pBottom ); + } + glEnd(); + } + + /* render FIREWORKS */ + + glBegin( GL_QUADS ); + bool playedExplodeSound = false; + bool flashedScreen = false; + Particle * particle = particleList.first(); + for (; particle; particle = particleList.next()) + { + //bind the texture for current particle (if not already bound) + if ( !particle->texture ) { + glEnd(); + glDisable( GL_TEXTURE_2D ); + glBegin( GL_QUADS ); + currentTexture = 0; + } else if ( particle->texture != currentTexture ) { + glEnd(); + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, currentTexture = particle->texture ); + glBegin( GL_QUADS ); + } + + //perspective projection (done by hand to make it funnier than opengl's :-) + float mfactor = PERSP_MAG_FACTOR * particle->ypos; + if ( mfactor < -246.0 ) { + particleList.remove(); + particleList.prev(); + continue; + } + float sfactor = 256.0 / (256.0 + mfactor), + posx = sfactor * particle->xpos, + posy = sfactor * particle->zpos - 4.0; + //size computation (magnify if enableMegaFlares is set) + if ( parameters.enableMegaFlares ) { + mfactor = parameters.megaFlares*particle->ypos; + if ( mfactor < -255.0 || mfactor > 512.0 ) { + particleList.remove(); + particleList.prev(); + continue; + } + sfactor = 256.0 / (256.0 + mfactor); + if ( sfactor > 64 ) + sfactor = 76.8 - sfactor / 5.0; + } + float size = sfactor * particle->pixelSize, + sizeX = size * unitX, + sizeY = size * unitY; + + //determine brightness (alpha component) for the particle + if ( particle->useLife ) { + float life = particle->life, + startLife = particle->startLife; + //bright changes with the following curve: "2*k - k^2" (or "k(2-k)") + if ( life > startLife ) + particle->colour[3] = startLife + 1 - life; + else + particle->colour[3] = life / startLife; + //apply flickering if enabled + if (particle->flicker < 0) { + particle->colour[3] = 0.2; + if (++particle->flicker >= 0) + particle->flicker = FLICKER_FRAMES_DELAY; + } else if (particle->flicker > 0) { + if ( life <= startLife ) + particle->colour[3] = 1.0; + if (--particle->flicker <= 0) + particle->flicker = -FLICKER_FRAMES_DELAY; + } + glColor4fv( particle->colour ); + } else + glColor3fv( particle->colour ); + + //draw particle + float pLeft = posx - sizeX, + pTop = posy + sizeY, + pRight = posx + sizeX, + pBottom = posy - sizeY; + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex2f( pLeft, pBottom ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex2f( pLeft, pTop ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex2f( pRight, pTop ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex2f( pRight, pBottom ); + + //phisically update parameters of the particle + particle->updateParameters( timeDiff ); + + //check for particle death / explosion + switch (particle->particleType) + { + //a Fireparticle is restarted when in right conditions + case Particle::FireParticle: + if ( posx < -FIELDW_2 || posx > FIELDW_2 || + (particle->zpos < -10.0 && posy < -FIELDW_2) ) + { + particle->initializeValues(); + if ( DRAND > 0.9995 ) + particle->zspeed *= 4; + } + break; + + //a leader explodes when his z speed drops to zero + //or, if it uses life, at death + case Particle::FireWorkLeaderParticle: + if ((particle->zspeed <= 0.0f && !particle->useLife) || + (particle->useLife && particle->life <= 0.0) ) + { + // play sound if enabled (and once per frame) + if (parameters.enableSound && !playedExplodeSound) + { + playSound(sound_explosion); + playedExplodeSound = true; + } + // flash screen if enabled + if (parameters.enableFlash && !flashedScreen) { + glEnd(); + glDisable( GL_TEXTURE_2D ); + glColor4f( 1,1,1, parameters.flashOpacity / 10.0 ); + glBegin( GL_TRIANGLE_STRIP ); + glVertex2f( FIELDW_2, FIELDW_2 ); + glVertex2f( -FIELDW_2, FIELDW_2 ); + glVertex2f( FIELDW_2, -FIELDW_2 ); + glVertex2f( -FIELDW_2, -FIELDW_2 ); + glEnd(); + if ( particleTexture ) + glEnable( GL_TEXTURE_2D ); + glBegin( GL_QUADS ); + flashedScreen = true; + } + // generating children and removing parent + int elementIndex = particleList.at(); + explodeFirework(particle); + particleList.remove(elementIndex); + particleList.prev(); + } else if ( parameters.enableTrails && DRAND < 0.4 ) { + // leave trail behind the particle (it'a small and slow red debris) + Particle * p = new Particle( Particle::FireWorkDebrisParticle ); + p->initializeValues( 0, particle, 1, 1 ); + p->texture = particleTexture; + p->xspeed /= 4; + p->yspeed /= 4; + p->zspeed /= 8; + p->zacc /= 4; + p->pixelSize = 2; + p->colour[0] /= 2; + int elementIndex = particleList.at(); + particleList.append( p ); + particleList.at( elementIndex ); + } + break; + + //remove if dead or outside field + default: + if (particle->life <= 0.0 || posx<-FIELDW_2 || posx>FIELDW_2 || posy<-FIELDW_2) { + particleList.remove(); + particleList.prev(); + } + break; + } + } + glEnd(); + + /* render WRITINGS */ + + if ( parameters.enableWritings ) + { + int chance = (int) (1000.0 * DRAND); + if ( !chance ) { + static const TQString someStrings[] = { + i18n("www.kde.org"), + i18n("My TDE, please!"), + i18n("KoNqUeR the World"), + i18n("KFIRESAVER 3D"), + i18n("Gimme your eyes..."), + i18n("Thank you for using TDE"), + i18n("Going insane tonight"), + }; + int n = (int)(6.0 * DRAND); + writer->spawnWords( someStrings[n], Writer::Fun1 ); + } + writer->render( timeDiff ); + } + + /* generate a new FIREWORK_LEADER */ + + int random = (int) ((float)parameters.fireworksFrequency * DRAND); + if (showp.ShowType == Show) + { + //double the chances ('frequency') to raise a new leaderParticle + //but envelop it under a sine function + float step = (showp.timeStamp - timeCurrent) / showp.timeGap; + if (DRAND > sin(M_PI*step)) + random = -1; + if (showp.type == AngelHairs && DRAND < 0.5) + random = -1; + } + if ( !random ) + { + Particle * particle = new Particle( Particle::FireWorkLeaderParticle ); + particle->initializeValues(); + particle->texture = flareTexture; + particleList.append( particle ); + } + + /* explode a logo */ + + int logoImages = imageList.count(); + if ( logoImages > 0 ) { + random = (int) (parameters.logoFrequency * logoImages * 200.0 * DRAND); + if ( random < logoImages ) + { + if (parameters.enableFlash && !flashedScreen) { + glDisable( GL_TEXTURE_2D ); + glColor4f( 1,1,1, parameters.flashOpacity / 10.0 ); + glBegin( GL_TRIANGLE_STRIP ); + glVertex2f( FIELDW_2, FIELDW_2 ); + glVertex2f( -FIELDW_2, FIELDW_2 ); + glVertex2f( FIELDW_2, -FIELDW_2 ); + glVertex2f( -FIELDW_2, -FIELDW_2 ); + glEnd(); + } + burnLogo( imageList.at(random) ); + } + } +} + + +int KFireSaver :: pickColour() +{ + int color = (int) (DRAND * parameters.colorsCount); + return parameters.colorsT[ color ]; +} + + +KFireSaver :: enumFireworkType KFireSaver :: pickType() +{ + int type = (int) (DRAND * parameters.typesCount); + return parameters.typesT[ type ]; +} + + +void KFireSaver :: explodeFirework(Particle* leaderParticle) +{ + GLfloat displace[3] = {0.0,0.0,0.0}; + float tmp1 = 0.0, tmp2 = 0.0, tmp3 = 0.0, tmp4 = 0.0, tmp5 = 0.0; + + // color of exploded particles + bool bicolor = parameters.enableCombos && (showp.forceBicolour || DRAND > 0.95), + flickers = false; + int cscheme = showp.forceColour ? showp.colour : pickColour(), + cscheme2 = showp.forceColour ? showp.colourSec : pickColour(); + + // randomize type of exploding firework + enumFireworkType fwType = + showp.forceType ? (enumFireworkType) showp.type : pickType(); + + // other options for generated particles + int number = (int) ((DRAND + DRAND) * 150.0); + float power = showp.forcePower ? + showp.powerEnvelop * (0.8 + 0.3*DRAND) : + DRAND * 11.0 + 2.0, + powermin = DRAND * power; + + // now some rules ... + //a splitter can't split up more than 2 times + if (fwType == Splitter && leaderParticle->explosionsDepth > 1) { + if (parameters.typesCount == 1) + return; + if (showp.forceType) + fwType = showp.typeSec; + if (fwType == Splitter) + while ( (fwType = pickType()) == Splitter ); + } + + // PRE-ADJUST parameters for the firework we're creating + switch ( fwType ) + { + //no need to handle this. it's the default configuration. + case Sphere: + break; + + //explosion whithout emitting particles, only a flash + case NoFW: + number = 1; + power = powermin = 0; + break; + + //splits up into 'number' orange pieces. tmp1 holds base_life + case Splitter: + cscheme = showp.forceColour ? showp.colour : 1; + bicolor = false; + number = 3 + (int) (DRAND * 4); + power /= 2.0; + powermin = power / 2.0; + tmp1 = 0.4 + DRAND * 1.5; + break; + + //randomize a couple of angles (phi - theta) for exploding circle + case BiCircle: + number *= 2; + case Circle: + power = DRAND * 5.0 + 4.0; + tmp1 = DRAND * M_PI; + tmp2 = DRAND * M_PI; + tmp4 = cos(tmp2); //c2 + tmp3 = sin(tmp2); //s2 + tmp2 = cos(tmp1); //c1 + tmp1 = sin(tmp1); //s1 + break; + + //cascade of flickering orange particles + case AngelHairs: + cscheme = showp.forceColour ? showp.colour : 1; + bicolor = false; + flickers = true; + power = 0.8 + DRAND * 1.9; + powermin = DRAND*0.5; + number = 100 + (int)(DRAND * 150); + displace[0] = -leaderParticle->xspeed/2; + displace[1] = -leaderParticle->yspeed/2; + displace[2] = power; + break; + + //behave as a standard spherical firework + case Spirals: + break; + + //not yet implemented, suppressing particles + case SuperNova: + case NoRender: + number = 0; + break; + } + + //limit number of particles as we are getting to the capacity saturation + float currentParticles = (float) particleList.count(); + const float particleCapacity = 15000; + const float particleGap = 8000; + if ( number > 10 && currentParticles > (particleCapacity - particleGap) ) + { + //NoFW, Splitter and NoRender aren't limited. + number = (int)( (float)number * (particleCapacity - currentParticles) / particleGap ); + if ( number < 10 ) + number = 0; + } + + int newExplosionsDepth = leaderParticle->explosionsDepth + 1; + for (int i=0 ; i<number ; i++) + { + Particle * particle; + if ( fwType == Spirals ) + particle = new TurningParticle( Particle::FireWorkDebrisParticle ); + else + particle = new Particle( Particle::FireWorkDebrisParticle ); + + particle->initializeValues ( + bicolor && (i>number/2) ? cscheme2 : cscheme, + leaderParticle, powermin, power, + flickers, displace ); + particle->texture = particleTexture; + particle->explosionsDepth = newExplosionsDepth; + + // POST-ADJUST particle coefficients adapting to current FireworkType + switch ( fwType ) + { + //create a big, white particle, simulating explosion + case NoFW: + if (parameters.enableFade) + particle->startLife = particle->life = 0.030; + else + particle->startLife = particle->life = 0.075; + particle->texture = flareTexture; + particle->colour[0]= + particle->colour[1]= + particle->colour[2]=1.0; + particle->pixelSize = 50.0 + 75.0 * DRAND; + particle->zacc = 0; + break; + + //default. no need to change parameters, only create the + //'sphere light' as the first big particle if set. + case Sphere: + if (i==0 && parameters.enableSphereLight && number > 40) { + particle->texture = flareTexture; + particle->xspeed = leaderParticle->xspeed; + particle->yspeed = leaderParticle->yspeed; + particle->zspeed = 0.0; + particle->colour[0] /= 2.0; + particle->colour[1] /= 2.0; + particle->colour[2] /= 2.0; + float impression = power * (float)number/5.0; + particle->pixelSize = 25.0 * DRAND + impression; + if (parameters.enableFade) { + particle->startLife = particle->life = 0.040; + } else { + particle->startLife = 1.3; + particle->life = 1.8; + } + } + break; + + // + case Splitter: + particle->particleType = Particle::FireWorkLeaderParticle; + particle->pixelSize *= 3.0; + particle->startLife = particle->life = tmp1 * (0.75 + DRAND/3.0); + if (particle->zspeed < 0) + particle->zspeed = -particle->zspeed; + break; + + case Circle: + case BiCircle: + tmp5 = 2 * M_PI * DRAND; + //MMX can be useful.. if you know how to use it :-) + if ( fwType == BiCircle && i > number/2 ) { + GLfloat ey = cos(tmp5), + ez = sin(tmp5); + particle->xspeed = power * ( tmp3*ez ); + particle->yspeed = power * ( tmp2*ey - tmp1*tmp4*ez ); + particle->zspeed = power * ( tmp1*ey + tmp2*tmp4*ez ); + } else { + GLfloat ex = sin(tmp5), + ey = cos(tmp5); + particle->xspeed = power * ( tmp4*ex ); + particle->yspeed = power * ( tmp1*tmp3*ex + tmp2*ey ); + particle->zspeed = power * ( -tmp2*tmp3*ex + tmp1*ey ); + } + break; + + case AngelHairs: + particle->zacc = -9.807 * (0.05 + DRAND*0.20); + particle->life = 2.0 + DRAND*2.5; + particle->startLife = particle->life - 1; + if (particle->zspeed < 0) + particle->zspeed *= -1; + //particle->pixelSize = 5.0; + break; + + case Spirals: + particle->texture = diastarTexture; + break; + + //discard cases + case SuperNova: + case NoRender: + break; + } + particleList.append(particle); + } +} + +void KFireSaver :: timerEvent(TQTimerEvent*) +{ + timeval tv; + gettimeofday(&tv,NULL); + double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; + + bool chooseType = false, + chooseColor = false, + chooseOthers = false, + updateTimings = false; + bool firstTime = showp.timeStamp == 0.0; + bool endOfScene = currentTime >= showp.timeStamp; + + if (firstTime) + switch (showp.ShowType) + { + case Monotype: + /* first time choose the type, color and attributes will + be choosed randomly for every firework which explodes*/ + showp.forceType = true; + chooseType = true; + break; + + case Monochrome: + /* first time choose the color, type and attributes will + be choosed randomly for every firework which explodes*/ + showp.forceColour = true; + chooseColor = + chooseOthers = true; + break; + + default: break; + } + + if (endOfScene || firstTime) + switch (showp.ShowType) + { + case Show: + /* if a scene ended, randomize global parameters for the + whole next scene */ + showp.forceType = true; + showp.forceColour = true; + showp.forcePower = true; + chooseOthers = + chooseColor = + chooseType = true; + updateTimings = true; + break; + + default: break; + } + + if ( chooseType ) + { + showp.type = pickType(); + if (parameters.typesCount < 2) + showp.typeSec = NoRender; + else + while ((showp.typeSec = pickType()) == showp.type); + } + if ( chooseColor ) { + showp.colour = pickColour(); + showp.colourSec = pickColour(); + } + if ( chooseOthers ) + { + showp.powerEnvelop = DRAND * 8.0 + 3.0; + if (DRAND > 0.9) + { + showp.forceBicolour = true; + showp.colourSec = pickColour(); + } else + showp.forceBicolour = false; + } + if ( firstTime || updateTimings ) + { + if (DRAND < 0.2) + showp.timeGap = 1.0 + DRAND * 2.0; + else + showp.timeGap = 3.0 + DRAND * 10.0; + showp.timeStamp = currentTime + showp.timeGap; + showp.timeGap /= 1.5; //hack to introduce delay in sine func + } + + updateGL(); +} + +void KFireSaver :: burnLogo(TQImage * image) +{ + if (!image || image->isNull()) + return; + int step = parameters.enableReduceLogo ? 2 : 1, + imageW = image->width(), + imageH = image->height(), + offsetX = imageW / 2, + offsetY = imageH / 2; + float speed = FIELDW_4 / (imageW > imageH ? imageW : imageH), + speedXOffs = 5 * (DRAND - DRAND), + speedYOffs = DRAND + DRAND + 1; + //if image is too big, lower sample points + while ((imageW/step)>96 || (imageH/step)>96) + step *= 2; + for (int y=0 ; y<imageH ; y+=step) + { + for (int x=0 ; x<imageW ; x+=step) + { + TQRgb pixel = image->pixel(x,y); + if ( tqAlpha(pixel) < 250 ) + continue; + //if ( DRAND > 0.9 ) + // continue; + + Particle* particle = new Particle( Particle::LogoParticle ); + particle->initializeValues(); + particle->texture = particleTexture; + + float xI = (x - offsetX) , + yI = (offsetY - y) ; + particle->xpos = xI * speed * 0.5; + particle->zpos = yI * speed * 0.5 + 5; + particle->xspeed = xI * speed + speedXOffs; + particle->zspeed = yI * speed + speedYOffs; + + particle->colour[0] = tqRed(pixel) / 255.0f; + particle->colour[1] = tqGreen(pixel) / 255.0f; + particle->colour[2] = tqBlue(pixel) / 255.0f; + + particleList.append(particle); + } + } + if (parameters.enableSound) + playSound(sound_debris); +} + +void KFireSaver :: playSound(TQString file) +{ + //flush inactive players + KPlayObject * playObject = playObjectList.first(); + while ( playObject ) + { + if ( playObject->state() != Arts::posPlaying ) + { + playObjectList.remove(); + playObject = playObjectList.current(); + } else + playObject = playObjectList.next(); + } + + //discart this sound if the player queue is already full (4 channels playing) + if ( playObjectList.count() >= 6 ) + return; + + // not needed when all of the files are in the distribution + //if (!TQFile::exists(file)) + //return; + + KPlayObjectFactory factory(artsServer.server()); + playObject = factory.createPlayObject(KURL(file), true); + + if (playObject && !playObject->isNull()) + { + playObject->play(); + playObjectList.append(playObject); + } +} + +bool KFireSaver :: loadTexture( TQString fileName, unsigned int & textureID ) +{ + //reset texture ID to the default EMPTY value + textureID = 0; + + //load image + TQImage tmp; + if ( !tmp.load( fileName ) ) + return false; + + //convert it to suitable format (flipped RGBA) + TQImage texture = TQGLWidget::convertToGLFormat( tmp ); + if ( texture.isNull() ) + return false; + + //get texture number and bind loaded image to that texture + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, 4 /* 3 ??? */, texture.width(), texture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() ); + return true; +} + +void KFireSaver :: freeTexture( unsigned int & textureID ) +{ + if ( textureID > 0 ) + glDeleteTextures( 1, &textureID ); + textureID = 0; +} + +void KFireSaver :: readConfig () +{ + TDEConfig config("kfiresaverrc",true,false); + + // show + config.setGroup( "Show" ); + showp.ShowType = (enum enumShowType)config.readNumEntry( "ShowType", 1 ); + parameters.fireworksFrequency = 11 - config.readNumEntry( "FireworksFrequency", 7 ); + if ( parameters.fireworksFrequency < 1 ) + parameters.fireworksFrequency = 1; + if ( parameters.fireworksFrequency > 11 ) + parameters.fireworksFrequency = 11; + parameters.fireworksFrequency *= (parameters.fireworksFrequency + 1); //*karl gauss's sum* + parameters.particleSize = config.readNumEntry( "ParticlesSize", 0 ); + if ( parameters.particleSize < -5 ) + parameters.particleSize = -5; + if ( parameters.particleSize > 5 ) + parameters.particleSize = 5; + if ( parameters.enableBottomFire = config.readBoolEntry( "enable-BottomFire", true ) ) + { + TQColor blue = TQt::darkBlue; + parameters.bottomFireColor = config.readColorEntry( "BottomFireColor", &blue ); + } + parameters.enableSound = config.readBoolEntry( "enable-Sounds", false ); + parameters.enableNoOverhead = config.readBoolEntry( "enable-NoOverhead", true ); + parameters.enableRealtime = config.readBoolEntry( "enable-FrameSkip", true ); + + // fireworks + config.setGroup( "Fireworks" ); + parameters.typesCount = 0; + if ( config.readBoolEntry( "use-Classic", true ) ) + parameters.typesT[parameters.typesCount++] = Sphere; + if ( config.readBoolEntry( "use-Explosion", false ) ) + parameters.typesT[parameters.typesCount++] = NoFW; + if ( config.readBoolEntry( "use-FlameRing", false ) ) + parameters.typesT[parameters.typesCount++] = Circle; + if ( config.readBoolEntry( "use-FlameWorld", false ) ) + parameters.typesT[parameters.typesCount++] = BiCircle; + if ( config.readBoolEntry( "use-Fall", false ) ) + parameters.typesT[parameters.typesCount++] = AngelHairs; + if ( config.readBoolEntry( "use-Splitter", false ) ) + parameters.typesT[parameters.typesCount++] = Splitter; + if ( config.readBoolEntry( "use-Spirals", false ) ) + parameters.typesT[parameters.typesCount++] = Spirals; + if ( config.readBoolEntry( "use-SuperNova", false ) ) + parameters.typesT[parameters.typesCount++] = SuperNova; + if ( !parameters.typesCount ) { + kdWarning() << "KFireSaver3D: Warning, no fireworks enabled in config file" << endl; + kdWarning() << " enabling 'Classic Spherical'" << endl; + parameters.typesCount = 1; + parameters.typesT[0] = Sphere; + } + parameters.typesT[ parameters.typesCount ] = + parameters.typesT[ parameters.typesCount-1 ]; + parameters.colorsCount = 0; + if ( config.readBoolEntry( "use-Red", false ) ) + parameters.colorsT[parameters.colorsCount++] = 0; + if ( config.readBoolEntry( "use-Orange", true ) ) + parameters.colorsT[parameters.colorsCount++] = 1; + if ( config.readBoolEntry( "use-Green", false ) ) + parameters.colorsT[parameters.colorsCount++] = 2; + if ( config.readBoolEntry( "use-Blue", false ) ) + parameters.colorsT[parameters.colorsCount++] = 3; + if ( config.readBoolEntry( "use-White", true ) ) + parameters.colorsT[parameters.colorsCount++] = 4; + if ( config.readBoolEntry( "use-Purple", false ) ) + parameters.colorsT[parameters.colorsCount++] = 5; + if ( config.readBoolEntry( "use-DeepGreen", true ) ) + parameters.colorsT[parameters.colorsCount++] = 6; + if ( !parameters.colorsCount ) + { + kdWarning() << "KFireSaver3D: Warning enable at least one color" << endl; + kdWarning() << " enabling 'Blinding White'" << endl; + parameters.colorsCount = 1; + parameters.colorsT[0] = 4; + } + parameters.colorsT[ parameters.colorsCount ] = + parameters.colorsT[ parameters.colorsCount-1 ]; + parameters.enableCombos = config.readBoolEntry( "use-Multicolor", true ); + + // specials + config.setGroup( "Specials" ); + if ( parameters.enableLogos = config.readBoolEntry( "enable-Logos", true ) ) + { + TQImage tempImage; + tempImage.setAlphaBuffer( true ); + if ( config.readBoolEntry( "LogosTux", true ) ) + if ( tempImage.load(locate("data","kfiresaver/kfs_tux.png")) ) + imageList.append( new TQImage(tempImage) ); + if ( config.readBoolEntry( "LogosKonqui", true ) ) + if ( tempImage.load(locate("data","kfiresaver/kfs_kde.png")) ) + imageList.append( new TQImage(tempImage) ); + if ( config.readBoolEntry( "LogosKDEIcons", true ) ) { + const TQString icons[] = { + "3floppy_unmount", "cdrom_unmount", "hdd_mount", "kmix", + "network", "my-computer", "folder_home", "konqueror", + "kmail", "penguin", "personal" }; + for ( int i = 0; i < 11; i++ ) + imageList.append( new TQImage(DesktopIcon(icons[i],64).convertToImage()) ); + } + parameters.enableReduceLogo = config.readBoolEntry( "LogosReduceDetail", true ); + parameters.logoFrequency = 11 - config.readNumEntry( "LogosFrequency", 4 ); + if ( parameters.logoFrequency < 1 ) + parameters.logoFrequency = 1; + if ( parameters.logoFrequency > 11 ) + parameters.logoFrequency = 11; + } + if ( parameters.enableStars = config.readBoolEntry( "enable-Stars", true ) ) + { + parameters.enableStarFlickering = config.readBoolEntry( "StarsFlicker", false ); + parameters.enableStarGradient = config.readBoolEntry( "StarsGradient", true ); + parameters.starsNumber = config.readNumEntry( "StarsNumber", 4 ); + if ( parameters.starsNumber < 0 ) + parameters.starsNumber = 0; + if ( parameters.starsNumber > 10 ) + parameters.starsNumber = 10; + } + parameters.enableWritings = config.readBoolEntry( "enable-Writings", true ); + + // effects + config.setGroup( "Effects" ); + parameters.enableSphereLight = config.readBoolEntry( "enable-SphericalLight", true ); + if ( parameters.enableFlash = config.readBoolEntry( "enable-Flash", false ) ) + { + parameters.flashOpacity = config.readNumEntry( "FlashOpacity", 5 ); + if ( parameters.flashOpacity < 0 ) + parameters.flashOpacity = 0; + if ( parameters.flashOpacity > 10 ) + parameters.flashOpacity = 10; + } + if ( parameters.enableFade = config.readBoolEntry( "enable-Fade", false ) ) + { + parameters.fadeAmount = config.readNumEntry( "FadeIntensity", 3 ); + if ( parameters.fadeAmount < 0 ) + parameters.fadeAmount = 0; + if ( parameters.fadeAmount > 9 ) + parameters.fadeAmount = 9; + } + if ( parameters.enableMegaFlares = config.readBoolEntry( "enable-Flares", true ) ) + { + parameters.megaFlares = config.readNumEntry( "FlaresDimension", 5 ); + if ( parameters.megaFlares < 0 ) + parameters.megaFlares = 0; + if ( parameters.megaFlares > 10 ) + parameters.megaFlares = 10; + parameters.megaFlares += 4; + parameters.megaFlares *= 2; + } + parameters.enableTrails = config.readBoolEntry( "enable-Trail", false ); +} |