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