summaryrefslogtreecommitdiffstats
path: root/tdescreensaver/kdesavers/firesaver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdescreensaver/kdesavers/firesaver.cpp')
-rw-r--r--tdescreensaver/kdesavers/firesaver.cpp1151
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: asy@libero.it
+
+#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 );
+}