Back to index

extremetuxracer  0.5beta
ui_snow.cpp
Go to the documentation of this file.
00001 /* 
00002  * PPRacer 
00003  * Copyright (C) 2004-2005 Volker Stroebel <volker@planetpenguin.de>
00004  *
00005  * Copyright (C) 1999-2001 Jasmin F. Patry
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020  */
00021 
00022 #include "ui_snow.h"
00023 #include "gl_util.h"
00024 #include "course_render.h"
00025 #include "render_util.h"
00026 #include "game_config.h"
00027 #include "textures.h"
00028 #include "ui_mgr.h"
00029 #include "loop.h"
00030 
00031 #include "stuff.h"
00032 
00033 #include "ppgltk/alg/defs.h"
00034 
00035 #define MAX_NUM_PARTICLES 10000
00036 #define BASE_NUM_PARTICLES 400
00037 #define GRAVITY_FACTOR 0.015
00038 #define BASE_VELOCITY 0.05
00039 #define VELOCITY_RANGE 0.02
00040 #define PUSH_DECAY_TIME_CONSTANT 0.2
00041 #define PUSH_DIST_DECAY 100 
00042 #define PUSH_FACTOR 0.5
00043 #define MAX_PUSH_FORCE 5
00044 #define WIND_FORCE 0.03
00045 #define AIR_DRAG 0.4
00046 
00047 #define PARTICLE_MIN_SIZE 1
00048 #define PARTICLE_SIZE_RANGE 10
00049 
00050 #define min(x,y) ((x)<(y)?(x):(y))
00051 #define max(x,y) ((x)>(y)?(x):(y))
00052 
00053 
00054 typedef struct _particle_t {
00055     pp::Vec2d pt;
00056     double size;
00057     pp::Vec2d vel;
00058     pp::Vec2d tex_min;
00059     pp::Vec2d tex_max;
00060 } particle_t;
00061 
00062 static particle_t particles[MAX_NUM_PARTICLES];
00063 static int num_particles = BASE_NUM_PARTICLES;
00064 static GLfloat particleColor[4] = { 1, 1, 1, 0.4 };
00065 static pp::Vec2d push_position(0,0);
00066 static pp::Vec2d last_push_position;
00067 static double last_update_time = -1;
00068 static bool push_position_initialized = false;
00069 
00070 static double frand()
00071 {
00072     return (double)rand()/RAND_MAX;
00073 } 
00074 
00075 static void make_particle( int i, double x, double y )
00076 {
00077     double p_dist;
00078     int type;
00079 
00080     particles[i].pt.x = x;
00081     particles[i].pt.y = y;
00082     p_dist = frand();
00083     particles[i].size = PARTICLE_MIN_SIZE + (1.0 - p_dist)*PARTICLE_SIZE_RANGE;
00084     particles[i].vel.x = 0;
00085     particles[i].vel.y = -BASE_VELOCITY-p_dist*VELOCITY_RANGE;
00086     type = (int) (frand() * (4.0 - EPS));
00087     if (type == 0) {
00088        particles[i].tex_min = pp::Vec2d( 0.0, 0.0 );
00089        particles[i].tex_max = pp::Vec2d( 0.5, 0.5 );
00090     } else if (type == 1) {
00091        particles[i].tex_min = pp::Vec2d( 0.5, 0.0 );
00092        particles[i].tex_max = pp::Vec2d( 1.0, 0.5 );
00093     } else if (type == 2) {
00094        particles[i].tex_min = pp::Vec2d( 0.5, 0.5 );
00095        particles[i].tex_max = pp::Vec2d( 1.0, 1.0 );
00096     } else {
00097        particles[i].tex_min = pp::Vec2d( 0.0, 0.5 );
00098        particles[i].tex_max = pp::Vec2d( 0.5, 1.0 );
00099     }
00100 }
00101 
00102 void init_ui_snow( void )
00103 {
00104     int i;
00105 
00106     for( i=0; i<num_particles; i++) {
00107        make_particle( i, frand(), frand() );
00108     }
00109     push_position = pp::Vec2d( 0.0, 0.0 );
00110 }
00111 
00112 void update_ui_snow( double time_step, bool windy )
00113 {
00114     pp::Vec2d *v, f;
00115     pp::Vec2d *pt;
00116     double size;
00117     double dist_from_push, p_dist;
00118     pp::Vec2d push_vector;
00119     int i;
00120     double push_timestep, time;
00121 
00122     time = getClockTime();
00123 
00124     push_vector.x = 0;
00125     push_vector.y = 0;
00126     push_timestep = 0;
00127        
00128     if ( push_position_initialized ) {
00129        push_vector.x = push_position.x - last_push_position.x;
00130        push_vector.y = push_position.y - last_push_position.y;
00131        push_timestep = time - last_update_time;
00132     }
00133     last_push_position = push_position;
00134     last_update_time = time;
00135 
00136     for ( i=0; i<num_particles; i++) {
00137        pt = &particles[i].pt;
00138        v = &particles[i].vel;
00139        size = particles[i].size;
00140 
00141        f.x = 0;
00142        f.y = 0;
00143 
00144        /* Mouse push and gravity */
00145        dist_from_push = (pow((pt->x - push_position.x), 2) +
00146                        pow((pt->y - push_position.y), 2));
00147        if ( push_timestep > 0 ) {
00148            f.x = PUSH_FACTOR * push_vector.x / push_timestep; 
00149            
00150            f.y = PUSH_FACTOR * push_vector.y / push_timestep; 
00151 
00152            f.x = min(MAX_PUSH_FORCE,f.x);
00153            f.x = max(-MAX_PUSH_FORCE,f.x);
00154            f.y = min(MAX_PUSH_FORCE,f.y);
00155            f.y = max(-MAX_PUSH_FORCE,f.y);
00156 
00157            f.x *= 1.0/(PUSH_DIST_DECAY*dist_from_push + 1) * 
00158               size/PARTICLE_SIZE_RANGE;
00159            f.y *= 1.0/(PUSH_DIST_DECAY*dist_from_push + 1) *
00160               size/PARTICLE_SIZE_RANGE;
00161        }
00162 
00163        /* Update velocity */
00164        v->x += ( f.x + ( windy ? WIND_FORCE : 0.0 ) - v->x * AIR_DRAG ) * 
00165            time_step;
00166        v->y += ( f.y - GRAVITY_FACTOR - v->y * AIR_DRAG ) * 
00167            time_step;
00168 
00169        /* Update position */
00170         pt->x += v->x * time_step * ( size / PARTICLE_SIZE_RANGE ); 
00171         pt->y += v->y * time_step * ( size / PARTICLE_SIZE_RANGE );
00172 
00173        if ( pt->x < 0 ) {
00174            pt->x = 1;
00175        } else if ( pt->x > 1 ) {
00176            pt->x = 0.0;
00177        }
00178     }
00179 
00180     /* Kill off & regenerate particles */
00181     for (i=0; i<num_particles; i++) {
00182        particle_t *p = &particles[i];
00183 
00184        if (p->pt.y < -0.05) {
00185            /* If we have an excess of particles, kill off with
00186               50% probability */
00187            if ( num_particles > BASE_NUM_PARTICLES && frand() > 0.5 ) {
00188               /* Delete the particle */
00189               *p = particles[num_particles-1];
00190               num_particles -= 1;
00191            } else {
00192               p->pt.x = frand();
00193               p->pt.y = 1+frand()*BASE_VELOCITY;
00194               p_dist = frand();
00195               p->size = PARTICLE_MIN_SIZE + 
00196                   ( 1.0 - p_dist ) * PARTICLE_SIZE_RANGE;
00197               p->vel.x = 0;
00198               p->vel.y = -BASE_VELOCITY-p_dist*VELOCITY_RANGE;
00199            }
00200        }
00201     }
00202 
00203     if ( time_step < PUSH_DECAY_TIME_CONSTANT ) {
00204        push_vector.x *= 1.0 - time_step/PUSH_DECAY_TIME_CONSTANT;
00205        push_vector.y *= 1.0 - time_step/PUSH_DECAY_TIME_CONSTANT;
00206     } else {
00207        push_vector.x = 0.0;
00208        push_vector.y = 0.0;
00209     }
00210 } 
00211 
00212 void draw_ui_snow( void )
00213 {
00214     GLuint   texture_id;
00215     char *binding;
00216     pp::Vec2d *pt, *tex_min, *tex_max;
00217     double size;
00218     double xres, yres;
00219     int i;
00220 
00221     xres = getparam_x_resolution();
00222     yres = getparam_y_resolution();
00223     
00224     UIMgr.setupDisplay();
00225 
00226     binding = "ui_snow_particle";
00227     if (!get_texture_binding( "ui_snow_particle", &texture_id ) ) {
00228        print_warning( IMPORTANT_WARNING,
00229                      "Couldn't get texture for binding %s", 
00230                      binding );
00231        texture_id = 0;
00232     } 
00233 
00234     glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
00235 
00236     glBindTexture( GL_TEXTURE_2D, texture_id );
00237 
00238     glColor4f( particleColor[0], 
00239               particleColor[1], 
00240               particleColor[2],
00241               particleColor[3] );
00242 
00243     glPushMatrix();
00244     {
00245        for ( i=0; i<num_particles; i++) {
00246            pt = &particles[i].pt;
00247            size = particles[i].size;
00248            tex_min = &particles[i].tex_min;
00249            tex_max = &particles[i].tex_max;
00250            glPushMatrix();
00251            {
00252               glTranslatef( pt->x*xres, pt->y*yres, 0 );
00253               glBegin( GL_QUADS );
00254               {
00255                   glTexCoord2f( tex_min->x, tex_min->y );
00256                   glVertex2f( 0, 0 );
00257                   glTexCoord2f( tex_max->x, tex_min->y );
00258                   glVertex2f( size, 0 );
00259                   glTexCoord2f( tex_max->x, tex_max->y );
00260                   glVertex2f( size, size );
00261                   glTexCoord2f( tex_min->x, tex_max->y );
00262                   glVertex2f( 0, size );
00263               }
00264               glEnd();
00265            }
00266            glPopMatrix();
00267        } 
00268     }
00269     glPopMatrix();
00270 
00271 } 
00272 
00273 void
00274 reset_ui_snow_cursor_pos( pp::Vec2d pos ) 
00275 {
00276     double xres, yres;
00277 
00278     xres = getparam_x_resolution();
00279     yres = getparam_y_resolution();
00280     push_position = pp::Vec2d( pos.x/(double)xres,
00281                               pos.y/(double)yres );
00282     last_push_position = push_position;
00283     push_position_initialized = true;
00284 }
00285 
00286 void 
00287 push_ui_snow( pp::Vec2d pos )
00288 {
00289     double xres, yres;
00290 
00291     xres = getparam_x_resolution();
00292     yres = getparam_y_resolution();
00293     push_position = pp::Vec2d( pos.x/(double)xres,
00294                               pos.y/(double)yres );
00295     if ( !push_position_initialized ) {
00296        last_push_position = push_position;
00297     }
00298     push_position_initialized = true;
00299 }
00300 
00301 void
00302 make_ui_snow( pp::Vec2d pos ) {
00303     double xres, yres;
00304 
00305     xres = getparam_x_resolution();
00306     yres = getparam_y_resolution();
00307 
00308     if ( num_particles < MAX_NUM_PARTICLES ) {
00309        make_particle( num_particles, pos.x/xres, pos.y/yres );
00310        num_particles++;
00311     }
00312 }