1 /*
2  * Tux Racer
3  * Copyright (C) 1999-2001 Jasmin F. Patry
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 
21 #include "tuxracer.h"
22 #include "part_sys.h"
23 #include "phys_sim.h"
24 #include "gl_util.h"
25 #include "tcl_util.h"
26 #include "course_render.h"
27 #include "render_util.h"
28 #include "textures.h"
29 
30 /* This constant is here as part of a debugging check to prevent an infinite
31    number of particles from being created */
32 #define MAX_PARTICLES 500000
33 
34 #define START_RADIUS 0.04
35 #define OLD_PART_SIZE 0.07
36 #define NEW_PART_SIZE 0.02
37 #define MIN_AGE     -0.2
38 #define MAX_AGE      1.0
39 
40 #define VARIANCE_FACTOR 0.8
41 
42 #define PARTICLE_SHADOW_HEIGHT 0.05
43 #define PARTICLE_SHADOW_ALPHA 0.1
44 
45 typedef struct _Particle {
46     point_t pt;
47     short type;
48     scalar_t base_size;
49     scalar_t cur_size;
50     scalar_t terrain_height;
51     scalar_t age;
52     scalar_t death;
53     scalar_t alpha;
54     vector_t vel;
55     struct _Particle *next;
56 } Particle;
57 
58 static GLfloat particle_colour[4];
59 
60 static Particle* head = NULL;
61 static int num_particles = 0;
62 
frand()63 scalar_t frand()
64 {
65     return (scalar_t)rand()/RAND_MAX;
66 }
67 
create_new_particles(point_t loc,vector_t vel,int num)68 void create_new_particles( point_t loc, vector_t vel, int num )
69 {
70     Particle *newp;
71     int i;
72     scalar_t speed;
73 
74     speed = normalize_vector( &vel );
75 
76     /* Debug check to track down infinite particle bug */
77     if ( num_particles + num > MAX_PARTICLES ) {
78 	check_assertion( 0, "maximum number of particles exceeded" );
79     }
80 
81     for (i=0; i<num; i++) {
82 
83         newp = (Particle*)malloc( sizeof( Particle) );
84 
85         if ( newp == NULL ) {
86             handle_system_error( 1, "out of memory" );
87         }
88 
89         num_particles += 1;
90 
91         newp->next = head;
92         head = newp;
93 
94         newp->pt.x = loc.x + 2.*(frand() - 0.5) * START_RADIUS;
95         newp->pt.y = loc.y;
96         newp->pt.z = loc.z + 2.*(frand() - 0.5) * START_RADIUS;
97 
98 	newp->type = (int) (frand() * (4.0 - EPS));
99 
100 	newp->base_size = ( frand() + 0.5 ) * OLD_PART_SIZE;
101 	newp->cur_size = NEW_PART_SIZE;
102 
103         newp->age = frand() * MIN_AGE;
104         newp->death = frand() * MAX_AGE;
105 
106 
107         newp->vel = add_vectors(
108 	    scale_vector( speed, vel ),
109 	    make_vector( VARIANCE_FACTOR * (frand() - 0.5) * speed,
110 			 VARIANCE_FACTOR * (frand() - 0.5) * speed,
111 			 VARIANCE_FACTOR * (frand() - 0.5) * speed  ) );
112     }
113 }
114 
update_particles(scalar_t time_step)115 void update_particles( scalar_t time_step )
116 {
117     Particle **p, *q;
118     scalar_t ycoord;
119 
120     for (p = &head; *p != NULL; ) {
121         (**p).age += time_step;
122 
123         if ( (**p).age < 0 ) continue;
124 
125 	(**p).pt = move_point( (**p).pt,
126 			       scale_vector( time_step, (**p).vel ) );
127 
128 	ycoord = find_y_coord( (**p).pt.x, (**p).pt.z );
129 
130 	if ( (**p).pt.y < ycoord - 3 ) {
131 	    (**p).age = (**p).death + 1;
132 	}
133 
134         if ( (**p).age >= (**p).death ) {
135             q = *p;
136             *p = q->next;
137             free(q);
138             num_particles -= 1;
139             continue;
140         }
141 
142         (**p).alpha = ( (**p).death - (**p).age ) / (**p).death;
143 
144 	(**p).cur_size = NEW_PART_SIZE +
145 	    ( OLD_PART_SIZE - NEW_PART_SIZE ) * ( (**p).age / (**p).death );
146 
147         (**p).vel.y += -EARTH_GRAV * time_step;
148 
149         p = &( (**p).next );
150     }
151 }
152 
draw_particles(player_data_t * plyr)153 void draw_particles( player_data_t *plyr )
154 {
155     Particle *p;
156     GLuint   texture_id;
157     char *binding;
158     point2d_t min_tex_coord, max_tex_coord;
159 
160     set_gl_options( PARTICLES );
161 
162     binding = "snow_particle";
163     if (!get_texture_binding( "snow_particle", &texture_id ) ) {
164 	print_warning( IMPORTANT_WARNING,
165 		       "Couldn't get texture for binding %s",
166 		       binding );
167 	texture_id = 0;
168     }
169 
170     glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
171 
172     glBindTexture( GL_TEXTURE_2D, texture_id );
173 
174 
175     for (p=head; p!=NULL; p = p->next) {
176         if ( p->age < 0 ) continue;
177 
178 	if ( p->type == 0 || p->type == 1 ) {
179 	    min_tex_coord.y = 0;
180 	    max_tex_coord.y = 0.5;
181 	} else {
182 	    min_tex_coord.y = 0.5;
183 	    max_tex_coord.y = 1.0;
184 	}
185 
186 	if ( p->type == 0 || p->type == 3 ) {
187 	    min_tex_coord.x = 0;
188 	    max_tex_coord.x = 0.5;
189 	} else {
190 	    min_tex_coord.x = 0.5;
191 	    max_tex_coord.x = 1.0;
192 	}
193 
194 	glColor4f( particle_colour[0],
195 		   particle_colour[1],
196 		   particle_colour[2],
197 		   particle_colour[3] * p->alpha );
198 
199 	draw_billboard( plyr, p->pt, p->cur_size, p->cur_size,
200 			False, min_tex_coord, max_tex_coord );
201     }
202 
203 }
204 
clear_particles()205 void clear_particles()
206 {
207     Particle *p, *q;
208 
209     p = head;
210     for (;;) {
211         if (p == NULL ) break;
212         q=p;
213         p=p->next;
214         free(q);
215     }
216     head = NULL;
217     num_particles = 0;
218 }
219 
reset_particles()220 void reset_particles() {
221     particle_colour[0] = 1.0;
222     particle_colour[1] = 1.0;
223     particle_colour[2] = 1.0;
224     particle_colour[3] = 1.0;
225 }
226 
particle_colour_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])227 static int particle_colour_cb(ClientData cd, Tcl_Interp *ip,
228 			      int argc, char *argv[])
229 {
230     scalar_t tmp_arr[4];
231     bool_t error = False;
232 
233     if ( argc == 2 ) {
234 	/* New format */
235 	if ( get_tcl_tuple ( ip, argv[1], tmp_arr, 4 ) == TCL_ERROR ) {
236 	    error = True;
237 	} else {
238 	    copy_to_glfloat_array( particle_colour, tmp_arr, 4 );
239 	}
240     } else {
241 	/* Old format */
242 	if (argc < 3) {
243 	    error = True;
244 	} else {
245 
246 	    NEXT_ARG;
247 
248 	    print_warning( DEPRECATION_WARNING,
249 			   "This format for tux_particle_colour is deprecated."
250 			   "  The new format is:\n"
251 			   "\ttux_particle_colour {r g b a}" );
252 
253 	    while ( !error && argc > 0 ) {
254 
255 		if ( strcmp( "-ambient_and_diffuse", *argv ) == 0 ||
256 		     strcmp( "-diffuse",  *argv ) == 0 )
257 		{
258 		    NEXT_ARG;
259 		    if ( argc == 0 ) {
260 			error = True;
261 			break;
262 		    }
263 		    if ( get_tcl_tuple ( ip, *argv, tmp_arr, 4 ) ==
264 			 TCL_ERROR )
265 		    {
266 			error = True;
267 			break;
268 		    }
269 		    copy_to_glfloat_array( particle_colour, tmp_arr, 4 );
270 		} else if ( strcmp( "-specular", *argv ) == 0 ) {
271 		    /* Ignore */
272 		    NEXT_ARG;
273 		} else if ( strcmp( "-shininess", *argv ) == 0 ) {
274 		    /* Ignore */
275 		    NEXT_ARG;
276 		} else {
277 		    print_warning( TCL_WARNING,
278 				   "tux_particle_colour: unrecognized "
279 				   "parameter `%s'", *argv );
280 		}
281 
282 		NEXT_ARG;
283 	    }
284 	}
285     }
286 
287     if ( error ) {
288 	print_warning( TCL_WARNING, "error in call to tux_particle_colour" );
289 	Tcl_AppendResult(
290 	    ip,
291 	    "\nUsage: tux_particle_colour {r g b a}", (char *) 0 );
292 	return TCL_ERROR;
293     }
294 
295     return TCL_OK;
296 }
297 
register_particle_callbacks(Tcl_Interp * ip)298 void register_particle_callbacks( Tcl_Interp *ip )
299 {
300     Tcl_CreateCommand (ip, "tux_particle_colour", particle_colour_cb,  0,0);
301     Tcl_CreateCommand (ip, "tux_particle_color", particle_colour_cb,  0,0);
302 }
303