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 "course_load.h"
23 #include "course_render.h"
24 #include "course_quad.h"
25 #include "image.h"
26 #include "textures.h"
27 #include "phys_sim.h"
28 #include "tcl_util.h"
29 #include "keyframe.h"
30 #include "gl_util.h"
31 #include "lights.h"
32 #include "fog.h"
33 #include "part_sys.h"
34 #include "multiplayer.h"
35 #include "audio_data.h"
36 #include "track_marks.h"
37 
38 #define MAX_TREES 8192
39 #define MAX_TREE_TYPES 32
40 #define MAX_ITEMS 8192
41 #define MAX_ITEM_TYPES 128
42 
43 #define MIN_ANGLE 5
44 #define MAX_ANGLE 80
45 
46 #define ICE_IMG_VAL  0
47 #define ROCK_IMG_VAL 127
48 #define SNOW_IMG_VAL 255
49 #define TREE_IMG_THRESHOLD 128
50 #define NUM_TERRAIN_TYPES 3
51 
52 #define DEP_TREE_RED 255
53 #define DEP_TREE_GREEN 0
54 #define DEP_TREE_BLUE 0
55 
56 static bool_t        course_loaded = False;
57 
58 /* This array stores the heightmap.  It is stored as follows: suppose
59    your at the top of a course, looking down.  The elevation data
60    starts at the very top-left of the course, and proceeds from left
61    to right.  So the very top strip of the course is stored first,
62    followed by the next strip in left-to-right order, and so on until
63    the botton strip. */
64 static scalar_t     *elevation;
65 static scalar_t      elev_scale;
66 static scalar_t      course_width, course_length;
67 static scalar_t      play_width, play_length;
68 static scalar_t      course_angle;
69 static int           nx, ny;
70 static tree_t        tree_locs[MAX_TREES];
71 static int           num_trees;
72 static terrain_t    *terrain;
73 static point2d_t     start_pt;
74 static char         *course_author = NULL;
75 static char         *course_name = NULL;
76 static int           base_height_value;
77 static tree_type_t   tree_types[MAX_TREE_TYPES];
78 static int           num_tree_types = 0;
79 static int           tree_dep_call = -1;
80 
81 static item_t        item_locs[MAX_ITEMS];
82 static item_type_t   item_types[MAX_ITEM_TYPES];
83 static int           num_item_types = 0;
84 static int           num_items;
85 
86 /* Interleaved vertex, normal, and color data */
87 static GLubyte      *vnc_array = NULL;
88 
get_course_elev_data()89 scalar_t     *get_course_elev_data()    { return elevation; }
get_course_terrain_data()90 terrain_t    *get_course_terrain_data() { return terrain; }
get_course_angle()91 scalar_t      get_course_angle()        { return course_angle; }
get_tree_locs()92 tree_t       *get_tree_locs()           { return tree_locs; }
get_num_trees()93 int           get_num_trees()           { return num_trees; }
get_tree_polyhedron(int type)94 polyhedron_t  get_tree_polyhedron(int type) { return tree_types[type].poly; }
get_tree_name(int type)95 char         *get_tree_name(int type)       { return tree_types[type].name; }
get_start_pt()96 point2d_t     get_start_pt()            { return start_pt; }
set_start_pt(point2d_t p)97 void          set_start_pt( point2d_t p ) { start_pt = p; }
get_course_author()98 char         *get_course_author()       { return course_author; }
get_course_name()99 char         *get_course_name()         { return course_name; }
100 
get_item_locs()101 item_t       *get_item_locs()           { return item_locs; }
get_num_items()102 int           get_num_items()           { return num_items; }
get_item_name(int type)103 char         *get_item_name(int type)   { return item_types[type].name; }
get_num_item_types()104 int           get_num_item_types()      { return num_item_types; }
get_item_types()105 item_type_t  *get_item_types()          { return item_types; }
106 
get_gl_arrays(GLubyte ** vnc_arr)107 void get_gl_arrays( GLubyte **vnc_arr )
108 {
109     *vnc_arr = vnc_array;
110 }
111 
get_course_dimensions(scalar_t * width,scalar_t * length)112 void get_course_dimensions( scalar_t *width, scalar_t *length )
113 {
114     *width = course_width;
115     *length = course_length;
116 }
117 
get_play_dimensions(scalar_t * width,scalar_t * length)118 void get_play_dimensions( scalar_t *width, scalar_t *length )
119 {
120     *width = play_width;
121     *length = play_length;
122 }
123 
124 /*!
125   Returns the base (minimum) height of the terrain at \c distance
126   \pre     A course has been loaded
127   \arg \c  distance the (non-negative) distance down the course
128 
129   \return  Minimum height (y-coord) of terrain
130   \author  jfpatry
131   \date    Created:  2000-08-30
132   \date    Modified: 2000-08-30
133 */
get_terrain_base_height(scalar_t distance)134 scalar_t get_terrain_base_height( scalar_t distance )
135 {
136     scalar_t slope = tan( ANGLES_TO_RADIANS( course_angle ) );
137     scalar_t base_height;
138 
139     check_assertion( distance > -EPS,
140 		     "distance should be positive" );
141 
142     /* This will need to be fixed once we add variably-sloped terrain */
143     base_height = -slope * distance -
144 	base_height_value / 255.0 * elev_scale;
145     return base_height;
146 }
147 
148 /*!
149   Returns the maximum height of the terrain at \c distance
150   \pre     A course has been loaded
151   \arg \c  distance the (non-negative) distance down the course
152 
153   \return  Maximum height (y-coord) of terrain
154   \author  jfpatry
155   \date    Created:  2000-08-30
156   \date    Modified: 2000-08-30
157 */
get_terrain_max_height(scalar_t distance)158 scalar_t get_terrain_max_height( scalar_t distance )
159 {
160     return get_terrain_base_height( distance ) + elev_scale;
161 }
162 
get_course_divisions(int * x,int * y)163 void get_course_divisions( int *x, int *y )
164 {
165     *x = nx;
166     *y = ny;
167 }
168 
169 
reset_course()170 static void reset_course()
171 {
172     int i;
173 
174     /*
175      * Defaults
176      */
177     num_trees     = 0;
178     num_items     = 0;
179     course_angle  = 20.;
180     course_width  = 50.;
181     course_length = 130.;
182     play_width  = 50.;
183     play_length = 130.;
184     nx = ny = -1;
185     start_pt.x = 0;
186     start_pt.y = 0;
187     base_height_value = 127; /* 50% grey */
188 
189     set_course_mirroring( False );
190 
191     reset_lights();
192     reset_fog();
193     reset_particles();
194 
195     if ( course_author != NULL ) {
196 	free( course_author );
197     }
198     course_author = NULL;
199 
200     if ( course_name != NULL ) {
201 	free( course_name );
202     }
203     course_name = NULL;
204 
205     if ( course_loaded == False ) return;
206 
207     reset_course_quadtree();
208 
209     free( elevation ); elevation = NULL;
210     free( terrain ); terrain = NULL;
211 
212     free( vnc_array ); vnc_array = NULL;
213 
214     for ( i = 0; i < num_tree_types; i++) {
215 	unbind_texture( tree_types[i].name );
216 	free( tree_types[i].name );
217 	tree_types[i].name = NULL;
218 
219 	free( tree_types[i].poly.polygons );
220 	tree_types[i].poly.polygons = NULL;
221 
222 	free( tree_types[i].poly.vertices );
223 	tree_types[i].poly.vertices = NULL;
224 
225 	free( tree_types[i].texture );
226 	tree_types[i].texture = NULL;
227 
228 	tree_types[i].poly.num_vertices  = 0;
229 	tree_types[i].poly.num_polygons  = 0;
230 	tree_types[i].texture = NULL;
231 	tree_types[i].num_trees = 0;
232 
233 	tree_types[i].pos = NULL;
234 	tree_types[i].insert_pos = NULL;
235     }
236     num_tree_types = 0;
237     tree_dep_call = -1;
238 
239     for ( i = 0; i < num_item_types; i++) {
240 	if (item_types[i].reset_point == False) {
241 	    unbind_texture( item_types[i].name );
242 	}
243 
244 	free( item_types[i].name );
245 	item_types[i].name = NULL;
246 
247 	free( item_types[i].texture );
248 	item_types[i].texture = NULL;
249 
250 	item_types[i].pos = NULL;
251 	item_types[i].insert_pos = NULL;
252 
253 	item_types[i].num_items = 0;
254     }
255     num_item_types = 0;
256 
257     course_loaded = False;
258 
259     reset_key_frame();
260 }
261 
course_exists(int num)262 bool_t course_exists( int num )
263 {
264     char buff[BUFF_LEN];
265     struct stat s;
266 
267     sprintf( buff, "%s/courses/%d", getparam_data_dir(), num );
268     if ( stat( buff, &s ) != 0 ) {
269 	return False;
270     }
271     if ( ! S_ISDIR( s.st_mode ) ) {
272 	return False;
273     }
274     return True;
275 }
276 
fill_gl_arrays()277 void fill_gl_arrays()
278 {
279     int x,y;
280     vector_t *normals = get_course_normals();
281     vector_t nml;
282     int idx;
283 
284     glDisableClientState(GL_VERTEX_ARRAY);
285     glDisableClientState(GL_NORMAL_ARRAY);
286     glDisableClientState(GL_COLOR_ARRAY);
287 
288     /* Align vertices and normals on 16-byte intervals (Q3A does this) */
289     vnc_array = (GLubyte*) malloc( STRIDE_GL_ARRAY * nx * ny );
290 
291     for (x=0; x<nx; x++) {
292 	for (y=0; y<ny; y++) {
293 	    idx = STRIDE_GL_ARRAY*(y*nx+x);
294 
295 #define floatval(i) (*(GLfloat*)(vnc_array+idx+(i)*sizeof(GLfloat)))
296 
297 	    floatval(0) = (GLfloat)x / (nx-1.) * course_width;
298 	    floatval(1) = ELEV(x,y);
299 	    floatval(2) = -(GLfloat)y / (ny-1.) * course_length;
300 
301 	    nml = normals[ x + y * nx ];
302 	    floatval(4) = nml.x;
303 	    floatval(5) = nml.y;
304 	    floatval(6) = nml.z;
305 	    floatval(7) = 1.0f;
306 
307 #undef floatval
308 #define byteval(i) (*(GLubyte*)(vnc_array+idx+8*sizeof(GLfloat) +\
309     i*sizeof(GLubyte)))
310 
311 	    byteval(0) = 255;
312 	    byteval(1) = 255;
313 	    byteval(2) = 255;
314 	    byteval(3) = 255;
315 
316 #undef byteval
317 
318 	}
319     }
320 
321     glEnableClientState(GL_VERTEX_ARRAY);
322     glVertexPointer( 3, GL_FLOAT, STRIDE_GL_ARRAY, vnc_array );
323 
324     glEnableClientState(GL_NORMAL_ARRAY);
325     glNormalPointer( GL_FLOAT, STRIDE_GL_ARRAY,
326 		     vnc_array + 4*sizeof(GLfloat) );
327 
328     glEnableClientState(GL_COLOR_ARRAY);
329     glColorPointer( 4, GL_UNSIGNED_BYTE, STRIDE_GL_ARRAY,
330 		    vnc_array + 8*sizeof(GLfloat) );
331 }
332 
load_course(char * course)333 void load_course( char *course )
334 {
335     char buff[BUFF_LEN];
336     char cwd[BUFF_LEN];
337 
338     reset_course();
339 
340     if ( getcwd( cwd, BUFF_LEN ) == NULL ) {
341 	handle_system_error( 1, "getcwd failed" );
342     }
343 
344     sprintf( buff, "%s/courses/%s", getparam_data_dir(), course );
345     if ( chdir( buff ) != 0 ) {
346 	handle_system_error( 1, "Couldn't chdir to %s", buff );
347     }
348 
349     if ( Tcl_EvalFile( g_game.tcl_interp, "./course.tcl") == TCL_ERROR ) {
350 	handle_error( 1, "Error evaluating %s/course.tcl: %s",
351 		      buff, Tcl_GetStringResult( g_game.tcl_interp ) );
352     }
353 
354     if ( chdir( cwd ) != 0 ) {
355 	handle_system_error( 1, "Couldn't chdir to %s", cwd );
356     }
357 
358     check_assertion( !Tcl_InterpDeleted( g_game.tcl_interp ),
359 		     "Tcl interpreter deleted" );
360 
361     calc_normals();
362 
363     fill_gl_arrays();
364 
365     init_course_quadtree( elevation, nx, ny, course_width/(nx-1.),
366 			  -course_length/(ny-1),
367 			  g_game.player[local_player()].view.pos,
368 			  getparam_course_detail_level() );
369 
370     init_track_marks();
371 
372     course_loaded = True;
373 
374     /* flush unused audio files */
375     delete_unused_audio_data();
376 }
377 
378 
intensity_to_terrain(int intensity)379 static terrain_t intensity_to_terrain( int intensity )
380 {
381     int dist[NUM_TERRAIN_TYPES];
382     int min;
383     terrain_t min_idx;
384     int i;
385 
386     dist[Ice]  = abs( intensity - ICE_IMG_VAL );
387     dist[Snow] = abs( intensity - SNOW_IMG_VAL );
388     dist[Rock] = abs( intensity - ROCK_IMG_VAL );
389 
390     min = dist[0];
391     min_idx = (terrain_t)0;
392     for (i=1; i<NUM_TERRAIN_TYPES; i++) {
393 	if ( dist[i] < min ) {
394 	    min = dist[i];
395 	    min_idx = (terrain_t)i;
396 	}
397     }
398     return min_idx;
399 }
400 
course_dim_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])401 static int course_dim_cb ( ClientData cd, Tcl_Interp *ip,
402 			   int argc, char *argv[])
403 {
404     double width, length;
405 
406     if ( ( argc != 3 ) && ( argc != 5 ) ) {
407         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
408 			 "Usage: ", argv[0], " <course width> <course length>",
409 			 " [<play width> <play length>]", (char *)0 );
410         return TCL_ERROR;
411     }
412 
413     if ( Tcl_GetDouble( ip, argv[1], &width ) != TCL_OK ) {
414         Tcl_AppendResult(ip, argv[0], ": invalid course width",
415 			 (char *)0 );
416         return TCL_ERROR;
417     }
418     if ( Tcl_GetDouble( ip, argv[2], &length ) != TCL_OK ) {
419         Tcl_AppendResult(ip, argv[0], ": invalid course length",
420 			 (char *)0 );
421         return TCL_ERROR;
422     }
423 
424     course_width = width;
425     course_length = length;
426 
427     if ( argc == 5 ) {
428 	if ( Tcl_GetDouble( ip, argv[3], &width ) != TCL_OK ) {
429 	    Tcl_AppendResult(ip, argv[0], ": invalid play width",
430 			     (char *)0 );
431 	    return TCL_ERROR;
432 	}
433 	if ( Tcl_GetDouble( ip, argv[4], &length ) != TCL_OK ) {
434 	    Tcl_AppendResult(ip, argv[0], ": invalid play length",
435 			     (char *)0 );
436 	    return TCL_ERROR;
437 	}
438 	play_width = width;
439 	play_length = length;
440     } else {
441 	play_width = course_width;
442 	play_length = course_length;
443     }
444 
445     return TCL_OK;
446 }
447 
angle_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])448 static int angle_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
449 {
450     double angle;
451 
452     if ( argc != 2 ) {
453         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
454 			 "Usage: ", argv[0], " <angle>",
455 			 (char *)0 );
456         return TCL_ERROR;
457     }
458 
459     if ( Tcl_GetDouble( ip, argv[1], &angle ) != TCL_OK ) {
460         return TCL_ERROR;
461     }
462 
463     if ( angle < MIN_ANGLE ) {
464 	print_warning( TCL_WARNING, "course angle is too small. Setting to %f",
465 		       MIN_ANGLE );
466 	angle = MIN_ANGLE;
467     }
468 
469     if ( MAX_ANGLE < angle ) {
470 	print_warning( TCL_WARNING, "course angle is too large. Setting to %f",
471 		       MAX_ANGLE );
472 	angle = MAX_ANGLE;
473     }
474 
475     course_angle = angle;
476 
477     return TCL_OK;
478 }
479 
480 
elev_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])481 static int elev_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
482 {
483     IMAGE *elev_img;
484     scalar_t slope;
485     int   x,y;
486     int   pad;
487 
488     if ( argc != 2 ) {
489         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
490 			 "Usage: ", argv[0], " <elevation bitmap>",
491 			 (char *)0 );
492         return TCL_ERROR;
493     }
494 
495     if (course_loaded) {
496 	print_warning( TCL_WARNING, "ignoring %s: course already loaded",
497 		       argv[0] );
498 	return TCL_OK;
499     }
500 
501     elev_img = ImageLoad( argv[1] );
502     if ( elev_img == NULL ) {
503 	print_warning( TCL_WARNING, "%s: couldn't load %s", argv[0], argv[1] );
504 	return TCL_ERROR;
505     }
506 
507     nx = elev_img->sizeX;
508     ny = elev_img->sizeY;
509 
510     elevation = (scalar_t *)malloc( sizeof(scalar_t)*nx*ny );
511 
512     if ( elevation == NULL ) {
513 	handle_system_error( 1, "malloc failed" );
514     }
515 
516     slope = tan( ANGLES_TO_RADIANS( course_angle ) );
517 
518     pad = 0;    /* RGBA images rows are aligned on 4-byte boundaries */
519     for (y=0; y<ny; y++) {
520         for (x=0; x<nx; x++) {
521 	    ELEV(nx-1-x, ny-1-y) =
522 		( ( elev_img->data[ (x + nx * y) * elev_img->sizeZ + pad ]
523 		    - base_height_value ) / 255.0 ) * elev_scale
524 		- (scalar_t) (ny-1.-y)/ny * course_length * slope;
525         }
526         pad += (nx*elev_img->sizeZ) % 4;
527     }
528 
529     free( elev_img->data );
530     free( elev_img );
531 
532     return TCL_OK;
533 }
534 
terrain_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])535 static int terrain_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
536 {
537     IMAGE *terrain_img;
538     int   x,y;
539     int   pad;
540     int   idx;
541 
542     if ( argc != 2 ) {
543         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
544 			 "Usage: ", argv[0], " <terrain bitmap>",
545 			 (char *)0 );
546         return TCL_ERROR;
547     }
548 
549     terrain_img = ImageLoad( argv[1] );
550 
551     if ( terrain_img == NULL ) {
552 	print_warning( TCL_WARNING, "%s: couldn't load %s", argv[0], argv[1] );
553         Tcl_AppendResult(ip, argv[0], ": couldn't load ", argv[1],
554 			 (char *)0 );
555 	return TCL_ERROR;
556     }
557 
558     if ( nx != terrain_img->sizeX || ny != terrain_img->sizeY ) {
559         Tcl_AppendResult(ip, argv[0], ": terrain bitmap must have same "
560 			 "dimensions as elevation bitmap",
561 			 (char *)0 );
562 
563 	return TCL_ERROR;
564     }
565 
566     terrain = (terrain_t *)malloc( sizeof(terrain_t)*nx*ny );
567 
568     if ( terrain == NULL ) {
569 	handle_system_error( 1, "malloc failed" );
570     }
571 
572     pad = 0;
573     for (y=0; y<ny; y++) {
574         for (x=0; x<nx; x++) {
575             idx = (nx-1-x) + nx*(ny-1-y);
576 	    terrain[idx] = intensity_to_terrain(
577 		terrain_img->data[(x+nx*y)*terrain_img->sizeZ+pad] );
578         }
579         pad += (nx*terrain_img->sizeZ) % 4;
580     }
581 
582     free( terrain_img->data );
583     free( terrain_img );
584 
585     return TCL_OK;
586 }
587 
bgnd_img_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])588 static int bgnd_img_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
589 {
590 
591     if ( argc != 2 ) {
592         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
593 			 "Usage: ", argv[0], " <background image>",
594 			 (char *)0 );
595         return TCL_ERROR;
596     }
597 
598     if (!load_and_bind_texture( "background", argv[1] )) {
599       Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
600       return TCL_ERROR;
601 	}
602 
603     return TCL_OK;
604 }
605 
606 /*
607 // set the defaults that the deprecated tree calls assume
608 */
tree_defaults(tree_type_t * type)609 static void tree_defaults (tree_type_t * type) {
610 
611     check_assertion( type != NULL, "need a non-NULL tree type");
612     type->name = string_copy("tree");
613     type->diam = 2.0;
614     type->height = 4.5;
615     type->vary = 0.5;
616     type->poly.num_vertices = 0;
617     type->poly.vertices = NULL;
618     type->poly.num_polygons = 0;
619     type->poly.polygons = NULL;
620     type->texture = NULL;
621     type->num_trees = 0;
622     type->red = DEP_TREE_RED;
623     type->green = DEP_TREE_GREEN;
624     type->blue = DEP_TREE_BLUE;
625     type->pos = NULL;
626     type->insert_pos = NULL;
627 }
628 
629 /*
630 // deprecated
631 */
tree_tex_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])632 static int tree_tex_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
633 {
634 
635     if ( argc != 2 ) {
636         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
637 			 "Usage: ", argv[0], " <tree texture>",
638 			 (char *)0 );
639         return TCL_ERROR;
640     }
641 
642     if (tree_dep_call == -1) {
643 	if ( num_tree_types + 1 >= MAX_TREE_TYPES ) {
644 	    Tcl_AppendResult(ip, argv[0], ": max number of tree types reached",
645 			     (char *)0 );
646 	    return TCL_ERROR;
647 	}
648 	tree_dep_call = num_tree_types++;
649 	tree_defaults( &tree_types[tree_dep_call] );
650     }
651 
652     if (!load_and_bind_texture( tree_types[tree_dep_call].name, argv[1] )) {
653 	Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
654 	return TCL_ERROR;
655     }
656 
657     tree_types[tree_dep_call].texture =
658 		    string_copy( tree_types[tree_dep_call].name );
659 
660     return TCL_OK;
661 }
662 
ice_tex_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])663 static int ice_tex_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
664 {
665 
666     if ( argc != 2 ) {
667         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
668 			 "Usage: ", argv[0], " <ice texture>",
669 			 (char *)0 );
670         return TCL_ERROR;
671     }
672 
673     if (!load_and_bind_texture( "ice", argv[1] )) {
674       Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
675       return TCL_ERROR;
676 	}
677 
678     return TCL_OK;
679 }
680 
rock_tex_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])681 static int rock_tex_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
682 {
683 
684     if ( argc != 2 ) {
685         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
686 			 "Usage: ", argv[0], " <rock texture>",
687 			 (char *)0 );
688         return TCL_ERROR;
689     }
690 
691     if (!load_and_bind_texture( "rock", argv[1] )) {
692       Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
693       return TCL_ERROR;
694 	}
695 
696     return TCL_OK;
697 }
698 
snow_tex_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])699 static int snow_tex_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
700 {
701 
702     if ( argc != 2 ) {
703         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
704 			 "Usage: ", argv[0], " <snow texture>",
705 			 (char *)0 );
706         return TCL_ERROR;
707     }
708 
709     if ( !load_and_bind_texture( "snow", argv[1] ) ) {
710       Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
711 	  return TCL_ERROR;
712     }
713 
714     return TCL_OK;
715 }
716 
start_pt_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])717 static int start_pt_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
718 {
719     double xcd, ycd;
720 
721     if ( argc != 3 ) {
722         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
723 			 "Usage: ", argv[0], " <x coord> <y coord>",
724 			 (char *)0 );
725         return TCL_ERROR;
726     }
727 
728     if ( Tcl_GetDouble( ip, argv[1], &xcd ) != TCL_OK ) {
729         Tcl_AppendResult(ip, argv[0], ": invalid x coordinate", (char *)0 );
730         return TCL_ERROR;
731     }
732     if ( Tcl_GetDouble( ip, argv[2], &ycd ) != TCL_OK ) {
733         Tcl_AppendResult(ip, argv[0], ": invalid y coordinate", (char *)0 );
734         return TCL_ERROR;
735     }
736 
737     if ( !( xcd > 0 && xcd < course_width ) ) {
738 	print_warning( TCL_WARNING, "%s: x coordinate out of bounds, "
739 		       "using 0\n", argv[0] );
740 	xcd = 0;
741     }
742 
743     if ( !( ycd > 0 && ycd < course_length ) ) {
744 	print_warning( TCL_WARNING, "%s: y coordinate out of bounds, "
745 		       "using 0\n", argv[0] );
746 	ycd = 0;
747     }
748 
749     start_pt.x = xcd;
750     start_pt.y = -ycd;
751 
752     return TCL_OK;
753 }
754 
elev_scale_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])755 static int elev_scale_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
756 {
757 
758     double scale;
759 
760     if ( argc != 2 ) {
761         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
762 			 "Usage: ", argv[0], " <scale>",
763 			 (char *)0 );
764         return TCL_ERROR;
765     }
766 
767     if ( Tcl_GetDouble( ip, argv[1], &scale ) != TCL_OK ) {
768         Tcl_AppendResult(ip, argv[0], ": invalid scale", (char *)0 );
769         return TCL_ERROR;
770     }
771 
772     if ( scale <= 0 ) {
773 	print_warning( TCL_WARNING, "%s: scale must be positive", argv[0] );
774 	return TCL_ERROR;
775     }
776 
777     elev_scale = scale;
778 
779     return TCL_OK;
780 }
781 
is_tree(unsigned char pixel[],tree_type_t ** which_type)782 static int is_tree( unsigned char pixel[], tree_type_t ** which_type )
783 {
784     int           min_distance = pixel[0] + pixel[1] + pixel[2];
785     int i;
786     int distance;
787 
788     *which_type = NULL;
789     for (i = 0; i < num_tree_types; i++) {
790 	/* assume red green blue pixel ordering */
791 	distance = abs ( tree_types[i].red - pixel[0] ) +
792 		    abs ( tree_types[i].green - pixel[1] ) +
793 		    abs ( tree_types[i].blue - pixel[2] );
794 	if (distance < min_distance) {
795 	    min_distance = distance;
796 	    *which_type = &tree_types[i];
797 	}
798     }
799 
800     return min_distance;
801 }
802 
is_item(unsigned char pixel[],item_type_t ** which_type)803 static int is_item( unsigned char pixel[], item_type_t ** which_type )
804 {
805     int      min_distance = pixel[0] + pixel[1] + pixel[2];
806     int i;
807     int distance;
808 
809     *which_type = NULL;
810     for (i = 0; i < num_item_types; i++) {
811 	/* assume red green blue pixel ordering */
812 	distance = abs ( item_types[i].red - pixel[0] ) +
813 		    abs ( item_types[i].green - pixel[1] ) +
814 		    abs ( item_types[i].blue - pixel[2] );
815 	if (distance < min_distance) {
816 	    min_distance = distance;
817 	    *which_type = &item_types[i];
818 	}
819     }
820 
821     return min_distance;
822 }
823 
824 /*
825 // modified
826 // now more accurate to call this object_cb - no matter
827 */
trees_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])828 static int trees_cb ( ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
829 {
830     IMAGE *treeImg;
831     int sx, sy, sz;
832     int x,y;
833     int pad;
834     tree_type_t *which_tree;
835     item_type_t *which_item;
836     point2d_t * pos;
837     int i;
838     list_elem_t elem;
839     int best_tree_dist, best_item_dist;
840 
841     if ( argc != 2 ) {
842         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
843 			 "Usage: ", argv[0], " <tree location bitmap>",
844 			 (char *)0 );
845         return TCL_ERROR;
846     }
847 
848     treeImg = ImageLoad( argv[1] );
849     if ( treeImg == NULL ) {
850 	print_warning( TCL_WARNING, "%s: couldn't load %s",
851 		       argv[0], argv[1] );
852         Tcl_AppendResult(ip, argv[0], ": couldn't load ", argv[1],
853 			 (char *)0 );
854 	return TCL_ERROR;
855     }
856 
857     if ( num_tree_types == 0 && num_item_types == 0 ) {
858 	print_warning( IMPORTANT_WARNING,
859 		       "tux_trees callback called with no tree or item "
860 		       "types set" );
861     }
862 
863     sx = treeImg->sizeX;
864     sy = treeImg->sizeY;
865     sz = treeImg->sizeZ;
866 
867     for (i = 0; i < num_tree_types; i++) {
868 	tree_types[i].pos = create_list();
869 	tree_types[i].insert_pos = 0;
870 	tree_types[i].num_trees = 0;
871     }
872 
873     for (i = 0; i < num_item_types; i++) {
874 	item_types[i].pos = create_list();
875 	item_types[i].insert_pos = 0;
876 	item_types[i].num_items = 0;
877     }
878 
879     num_trees = 0;
880     num_items = 0;
881     pad = 0;
882     for (y=0; y<sy; y++) {
883         for (x=0; x<sx; x++) {
884             best_tree_dist = is_tree ( &treeImg->data[ (x + y*sx)*sz + pad ],
885 					 &which_tree );
886             best_item_dist = is_item ( &treeImg->data[ (x + y*sx)*sz + pad ],
887 					 &which_item );
888 
889 	    if ( best_tree_dist < best_item_dist && which_tree != NULL ) {
890                 if (num_trees+1 == MAX_TREES ) {
891                     fprintf( stderr, "%s: maximum number of trees reached.\n",
892 			     argv[0] );
893                     break;
894                 }
895 		num_trees += 1;
896 		which_tree->num_trees += 1;
897 		pos = (point2d_t *) malloc(sizeof(point2d_t));
898 		pos->x = (sx-x)/(scalar_t)(sx-1.)*course_width;
899 		pos->y = -(sy-y)/(scalar_t)(sy-1.)*course_length;
900 		which_tree->insert_pos = insert_list_elem(which_tree->pos,
901 			which_tree->insert_pos, (list_elem_data_t) pos);
902 
903             } else if ( which_item != NULL ) {
904                 if (num_items+1 == MAX_ITEMS ) {
905                     fprintf( stderr, "%s: maximum number of items reached.\n",
906 			     argv[0] );
907                     break;
908                 }
909 		num_items += 1;
910 		which_item->num_items += 1;
911 		pos = (point2d_t *) malloc(sizeof(point2d_t));
912 		pos->x = (sx-x)/(scalar_t)(sx-1.)*course_width;
913 		pos->y = -(sy-y)/(scalar_t)(sy-1.)*course_length;
914 		which_item->insert_pos = insert_list_elem(which_item->pos,
915 			which_item->insert_pos, (list_elem_data_t) pos);
916 	    }
917 
918         }
919         pad += ( sx * sz ) % 4; /* to compensate for word-aligned rows */
920     }
921 
922     /*
923     // double pass so that tree and object types are clumped together - reduce
924     // texture switching
925     */
926     num_trees = 0;
927     for (i = 0; i < num_tree_types; i++) {
928 	elem = get_list_head(tree_types[i].pos);
929 	while (elem != NULL) {
930 	    pos = (point2d_t *) get_list_elem_data(elem);
931 
932 	    tree_locs[num_trees].ray.pt.x = pos->x;
933 	    tree_locs[num_trees].ray.pt.z = pos->y;
934 	    tree_locs[num_trees].ray.pt.y = find_y_coord( pos->x, pos->y );
935 	    free( pos );
936 	    elem = get_next_list_elem(tree_types[i].pos, elem);
937 
938 	    tree_locs[num_trees].ray.vec = make_vector( 0, 1, 0);
939 
940 	    tree_locs[num_trees].height =
941 		    (scalar_t)rand()/RAND_MAX*tree_types[i].vary*2;
942 	    tree_locs[num_trees].height -= tree_types[i].vary;
943 	    tree_locs[num_trees].height = tree_types[i].height +
944 			tree_locs[num_trees].height * tree_types[i].height;
945 	    tree_locs[num_trees].diam = (tree_locs[num_trees].height /
946 			tree_types[i].height) * tree_types[i].diam;
947 	    tree_locs[num_trees].tree_type = i;
948 	    num_trees++;
949 	}
950 	del_list( tree_types[i].pos );
951     }
952 
953     num_items = 0;
954     for (i = 0; i < num_item_types; i++) {
955 	elem = get_list_head(item_types[i].pos);
956 	while (elem != NULL) {
957 	    pos = (point2d_t *) get_list_elem_data(elem);
958 
959 	    item_locs[num_items].ray.pt.x = pos->x;
960 	    item_locs[num_items].ray.pt.z = pos->y;
961 	    item_locs[num_items].ray.pt.y = find_y_coord( pos->x, pos->y )
962 					    + item_types[i].above_ground;
963 	    free( pos );
964 	    elem = get_next_list_elem(item_types[i].pos, elem);
965 
966 	    item_locs[num_items].ray.vec = make_vector( 0, 1, 0);
967 
968 	    item_locs[num_items].height = item_types[i].height ;
969 	    item_locs[num_items].diam = item_types[i].diam;
970 	    item_locs[num_items].item_type = i;
971 	    if ( item_types[i].nocollision )  {
972 		item_locs[num_items].collectable = -1;
973 	    } else {
974 		item_locs[num_items].collectable = 1;
975 	    }
976 	    if ( item_types[i].reset_point )  {
977 		item_locs[num_items].drawable = False;
978 	    } else {
979 		item_locs[num_items].drawable = True;
980 	    }
981 	    num_items++;
982 	}
983 	del_list( item_types[i].pos );
984     }
985 
986 
987     free( treeImg->data );
988     free( treeImg );
989 
990     return TCL_OK;
991 }
992 
993 /*
994 // deprecated
995 */
tree_size_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])996 static int tree_size_cb( ClientData cd, Tcl_Interp *ip,
997 			 int argc, char *argv[])
998 {
999     double diam, height;
1000 
1001     if ( argc != 3 ) {
1002         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1003 			 "Usage: ", argv[0], " <diameter> <height>",
1004 			 (char *)0 );
1005         return TCL_ERROR;
1006     }
1007 
1008     if ( Tcl_GetDouble( ip, argv[1], &diam ) != TCL_OK ) {
1009         Tcl_AppendResult(ip, argv[0], ": invalid diameter",
1010 			 (char *)0 );
1011         return TCL_ERROR;
1012     }
1013     if ( Tcl_GetDouble( ip, argv[2], &height ) != TCL_OK ) {
1014         Tcl_AppendResult(ip, argv[0], ": invalid height",
1015 			 (char *)0 );
1016         return TCL_ERROR;
1017     }
1018 
1019     if (tree_dep_call == -1) {
1020 	if ( num_tree_types + 1 >= MAX_TREE_TYPES ) {
1021 	    Tcl_AppendResult(ip, argv[0], ": max number of tree types reached",
1022 			     (char *)0 );
1023 	    return TCL_ERROR;
1024 	}
1025 	tree_dep_call = num_tree_types++;
1026 	tree_defaults( &tree_types[tree_dep_call] );
1027     }
1028 
1029     tree_types[tree_dep_call].diam   = diam;
1030     tree_types[tree_dep_call].height = height;
1031 
1032     return TCL_OK;
1033 }
1034 
1035 /*
1036  * The following functions are used to get the parameters for
1037  * tux_tree_poly
1038  */
1039 
1040 /*
1041  * Gets the vertex list
1042  */
1043 static int
get_polyhedron_vertices(Tcl_Interp * ip,char * arg,int * num_vertices,point_t ** vertex_list)1044 get_polyhedron_vertices(Tcl_Interp *ip, char *arg,
1045 			int *num_vertices, point_t **vertex_list)
1046 {
1047   int i;
1048   char **indices=0;
1049   int rtn;
1050   scalar_t pt[3];
1051 
1052   rtn = Tcl_SplitList(ip, arg, num_vertices, &indices);
1053   if( rtn != TCL_OK )
1054   {
1055     Tcl_AppendResult(ip,
1056 		     "A vertex list must be provided\n",
1057 		     (char *) 0);
1058 
1059     Tcl_Free((char *) indices);
1060     return (TCL_ERROR);
1061   }
1062 
1063   *vertex_list = (point_t*)malloc(sizeof(point_t)*(*num_vertices));
1064   for(i = 0; i < *num_vertices; i++) {
1065     get_tcl_tuple(ip, indices[i], pt, 3);
1066     (*vertex_list)[i] = make_point_from_array(pt);
1067   }
1068 
1069   Tcl_Free((char *) indices);
1070 
1071   return TCL_OK;
1072 }
1073 
1074 /*
1075  * gets the vertex-index-list of a single polygon
1076  */
1077 static int
get_single_polygon(Tcl_Interp * ip,char * arg,polygon_t * polygon)1078 get_single_polygon(Tcl_Interp *ip, char *arg,
1079 		   polygon_t *polygon)
1080 {
1081   int i;
1082   char **indices=0;
1083   int rtn;
1084   int num_vertices;
1085 
1086   rtn = Tcl_SplitList(ip, arg, &num_vertices, &indices);
1087   if( rtn != TCL_OK )
1088   {
1089     Tcl_AppendResult(ip,
1090 		     "a list of vertices must be provided for each polygon\n",
1091 		     (char *) 0);
1092     Tcl_Free((char *) indices);
1093     return (TCL_ERROR);
1094   }
1095 
1096   polygon->num_vertices = num_vertices;
1097   polygon->vertices = (int*)malloc(sizeof(int)*num_vertices);
1098 
1099   for(i = 0; i < num_vertices; i++)
1100     Tcl_GetInt(ip, indices[i], &(polygon->vertices[i]));
1101 
1102   Tcl_Free((char *) indices);
1103 
1104   return TCL_OK;
1105 }
1106 
1107 /*
1108  * gets the polygon list.
1109  */
1110 static int
get_polyhedron_polygon(Tcl_Interp * ip,char * arg,int * num_polygons,polygon_t ** polygon_list)1111 get_polyhedron_polygon(Tcl_Interp *ip, char *arg,
1112 		       int *num_polygons, polygon_t **polygon_list)
1113 {
1114   int i;
1115   char **indices=0;
1116   int rtn;
1117 
1118   rtn = Tcl_SplitList(ip, arg, num_polygons, &indices);
1119   if( rtn != TCL_OK )
1120   {
1121     Tcl_AppendResult(ip,
1122 		     "A polygon list must be provided\n",
1123 		     (char *) 0);
1124     Tcl_Free((char *) indices);
1125     return TCL_ERROR;
1126   }
1127 
1128   *polygon_list = (polygon_t*)malloc(sizeof(polygon_t)*(*num_polygons));
1129   for(i = 0; i < *num_polygons; i++)
1130     get_single_polygon(ip, indices[i], &((*polygon_list)[i]));
1131 
1132   Tcl_Free((char *) indices);
1133 
1134   return TCL_OK;
1135 }
1136 
1137 /*
1138  * finally the polyhedron object.
1139  */
1140 /*
1141 // deprecated
1142 */
1143 static int
tree_poly_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1144 tree_poly_cb(ClientData cd, Tcl_Interp *ip, int argc, char *argv[])
1145 {
1146   int           num_vertices;
1147   point_t     *vertices;
1148   int           num_polygons;
1149   polygon_t     *polygons;
1150 
1151   if (argc != 3) {
1152     Tcl_AppendResult(ip,
1153 		     argv[0],
1154 		     "invalid number of arguments\n"
1155 		     "Usage: tux_tree_poly <vertex list> <face list>"
1156 		     " <polygon list>",
1157 		     (char *) 0 );
1158     return TCL_ERROR;
1159   }
1160 
1161     if (tree_dep_call == -1) {
1162 	if ( num_tree_types + 1 >= MAX_TREE_TYPES ) {
1163 	    Tcl_AppendResult(ip, argv[0], ": max number of tree types reached",
1164 			     (char *)0 );
1165 	    return TCL_ERROR;
1166 	}
1167 	tree_dep_call = num_tree_types++;
1168 	tree_defaults( &tree_types[tree_dep_call] );
1169     }
1170 
1171   if( get_polyhedron_vertices(ip, argv[1], &num_vertices, &vertices) == TCL_OK )
1172     if( get_polyhedron_polygon(ip, argv[2], &num_polygons, &polygons) == TCL_OK ) {
1173         tree_types[tree_dep_call].poly.num_polygons = num_polygons;
1174         tree_types[tree_dep_call].poly.polygons     = polygons;
1175         tree_types[tree_dep_call].poly.num_vertices = num_vertices;
1176         tree_types[tree_dep_call].poly.vertices     = vertices;
1177         return TCL_OK;
1178     }
1179 
1180   Tcl_AppendResult(ip, argv[0], ": error obtaining polyhedron data",
1181 		   (char *)0 );
1182   return TCL_ERROR;
1183 }
1184 
friction_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1185 static int friction_cb ( ClientData cd, Tcl_Interp *ip,
1186 			 int argc, char *argv[])
1187 {
1188     double fric[3];
1189     scalar_t fric_s[3];
1190     int i;
1191 
1192     if ( argc != 4 ) {
1193         fprintf( stderr, "Usage: %s <ice> <rock> <snow>", argv[0] );
1194         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1195 			 "Usage: ", argv[0], " <ice coeff.> <rock coeff.> "
1196 			 "<snow coeff.>",
1197 			 (char *)0 );
1198         return TCL_ERROR;
1199     }
1200 
1201     if ( Tcl_GetDouble( ip, argv[1], &fric[0] ) != TCL_OK ) {
1202         Tcl_AppendResult(ip, argv[0], ": invalid ice coefficient",
1203 			 (char *)0 );
1204         return TCL_ERROR;
1205     }
1206 
1207     if ( Tcl_GetDouble( ip, argv[2], &fric[1] ) != TCL_OK ) {
1208         Tcl_AppendResult(ip, argv[0], ": invalid rock coefficient",
1209 			 (char *)0 );
1210         return TCL_ERROR;
1211     }
1212 
1213     if ( Tcl_GetDouble( ip, argv[3], &fric[2] ) != TCL_OK ) {
1214         Tcl_AppendResult(ip, argv[0], ": invalid snow coefficient",
1215 			 (char *)0 );
1216         return TCL_ERROR;
1217     }
1218 
1219     for ( i=0; i<sizeof(fric)/sizeof(fric[0]); i++) {
1220 	fric_s[i] = fric[i];
1221     }
1222 
1223     set_friction_coeff( fric_s );
1224 
1225     return TCL_OK;
1226 }
1227 
course_author_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1228 static int course_author_cb( ClientData cd, Tcl_Interp *ip,
1229 			     int argc, char *argv[])
1230 {
1231     if ( argc != 2 ) {
1232         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1233 			 "Usage: ", argv[0], " <author's name>",
1234 			 (char *)0 );
1235         return TCL_ERROR;
1236     }
1237 
1238     if ( course_author != NULL ) {
1239         free(course_author);
1240     }
1241     course_author = string_copy( argv[1] );
1242 
1243     return TCL_OK;
1244 }
1245 
course_name_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1246 static int course_name_cb( ClientData cd, Tcl_Interp *ip,
1247 			   int argc, char *argv[])
1248 {
1249     if ( argc != 2 ) {
1250         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1251 			 "Usage: ", argv[0], " <course name>",
1252 			 (char *)0 );
1253         return TCL_ERROR;
1254     }
1255 
1256     if ( course_name != NULL ) {
1257         free(course_name);
1258     }
1259     course_name = string_copy( argv[1] );
1260 
1261     return TCL_OK;
1262 }
1263 
base_height_value_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1264 static int base_height_value_cb( ClientData cd, Tcl_Interp *ip,
1265 				 int argc, char *argv[])
1266 {
1267     int value;
1268 
1269     if ( argc != 2 ) {
1270         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n",
1271 			 "Usage: ", argv[0], " <base height>",
1272 			 (char *)0 );
1273         return TCL_ERROR;
1274     }
1275 
1276     if ( Tcl_GetInt( ip, argv[1], &value ) != TCL_OK ) {
1277         Tcl_AppendResult(ip, argv[0], ": invalid base height",
1278 			 (char *)0 );
1279         return TCL_ERROR;
1280     }
1281 
1282     base_height_value = value;
1283 
1284     return TCL_OK;
1285 }
1286 
tree_props_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1287 static int tree_props_cb( ClientData cd, Tcl_Interp *ip,
1288 				 int argc, char *argv[])
1289 {
1290     int         i;
1291     int         num_vertices;
1292     point_t     *vertices;
1293     int         num_polys;
1294     polygon_t   *polys;
1295     int         rtn, num_col;
1296     char **     indices = 0;
1297     int         error = 0;
1298     int         convert_temp;
1299 
1300     if ( num_tree_types + 1 >= MAX_TREE_TYPES ) {
1301 	Tcl_AppendResult(ip, argv[0], ": max number of tree types reached",
1302 			 (char *)0 );
1303 	return TCL_ERROR;
1304     }
1305 
1306     /* fill in values not specified with defaults */
1307     tree_types[num_tree_types].name = NULL;
1308     tree_types[num_tree_types].diam = 2.0;
1309     tree_types[num_tree_types].height = 4.5;
1310     tree_types[num_tree_types].vary = 0.5;
1311     tree_types[num_tree_types].poly.num_vertices = 0;
1312     tree_types[num_tree_types].poly.vertices = NULL;
1313     tree_types[num_tree_types].poly.num_polygons = 0;
1314     tree_types[num_tree_types].poly.polygons = NULL;
1315     tree_types[num_tree_types].texture = NULL;
1316     tree_types[num_tree_types].num_trees = 0;
1317     tree_types[num_tree_types].red = 255;
1318     tree_types[num_tree_types].green = 255;
1319     tree_types[num_tree_types].blue = 255;
1320     tree_types[num_tree_types].pos = NULL;
1321     tree_types[num_tree_types].insert_pos = NULL;
1322 
1323     for ( i = 1; (i < argc - 1) && !error; i += 2 ) {
1324 	if ( strcmp( "-name", argv[i] ) == 0 ) {
1325 	    tree_types[num_tree_types].name = string_copy(argv[i+1]);
1326 
1327 	} else if ( strcmp( "-diameter", argv[i] ) == 0 ) {
1328 	    if ( Tcl_GetDouble( ip, argv[i+1],
1329 		    &tree_types[num_tree_types].diam) != TCL_OK ) {
1330 		Tcl_AppendResult(ip, argv[0], ": invalid diameter",
1331 				 (char *)0 );
1332 		error = 1;
1333 	    }
1334 
1335 	} else if ( strcmp( "-height", argv[i] ) == 0 ) {
1336 	    if ( Tcl_GetDouble( ip, argv[i+1],
1337 		    &tree_types[num_tree_types].height) != TCL_OK ) {
1338 		Tcl_AppendResult(ip, argv[0], ": invalid height",
1339 				 (char *)0 );
1340 		error = 1;
1341 	    }
1342 
1343 	} else if ( strcmp( "-texture", argv[i] ) == 0 ) {
1344 	    if ( !tree_types[num_tree_types].texture ) {
1345 		tree_types[num_tree_types].texture = string_copy(argv[i+1]);
1346 	    } else {
1347 		Tcl_AppendResult(ip, argv[0], ": specify only one texture",
1348 				(char *)0 );
1349 	    }
1350 
1351 	} else if ( strcmp( "-colour", argv[i] ) == 0 ) {
1352 	    rtn = Tcl_SplitList(ip, argv[i+1], &num_col, &indices);
1353 	    if( rtn != TCL_OK ) {
1354 		Tcl_AppendResult(ip, "a list of colours must be provided\n",
1355 			     (char *) 0);
1356 		Tcl_Free((char *) indices);
1357 		error = 1;
1358 	    }
1359 
1360 	    if (num_col == 3 || num_col == 4) {
1361 		Tcl_GetInt(ip, indices[0], &convert_temp);
1362 		tree_types[num_tree_types].red = (unsigned char) convert_temp;
1363 		Tcl_GetInt(ip, indices[1], &convert_temp);
1364 		tree_types[num_tree_types].green = (unsigned char) convert_temp;
1365 		Tcl_GetInt(ip, indices[2], &convert_temp);
1366 		tree_types[num_tree_types].blue = (unsigned char) convert_temp;
1367 	    } else {
1368 		Tcl_AppendResult(ip, argv[0], ": must specify three colours"
1369 			" to link with tree type", (char *) 0);
1370 		error = 1;
1371 	    }
1372 	    Tcl_Free((char *) indices);
1373 
1374 	} else if ( strcmp( "-polyhedron", argv[i] ) == 0 ) {
1375 	    rtn = Tcl_SplitList(ip, argv[i+1], &num_col, &indices);
1376 	    if( rtn != TCL_OK || num_col != 2 ) {
1377 		Tcl_AppendResult(ip, "two sublists of vertices and polygons"
1378 				     " must be specified\n",
1379 			     (char *) 0);
1380 		Tcl_Free((char *) indices);
1381 		error = 1;
1382 	    }
1383 	    if( get_polyhedron_vertices(ip, indices[0], &num_vertices,
1384 			&vertices) == TCL_OK ) {
1385 		if( get_polyhedron_polygon(ip, indices[1], &num_polys, &polys)
1386 			== TCL_OK ) {
1387 		    tree_types[num_tree_types].poly.num_polygons = num_polys;
1388 		    tree_types[num_tree_types].poly.polygons     = polys;
1389 		    tree_types[num_tree_types].poly.num_vertices = num_vertices;
1390 		    tree_types[num_tree_types].poly.vertices     = vertices;
1391 		} else {
1392 		    free( vertices );
1393 		    error = 1;
1394 		}
1395 	    } else {
1396 		error = 1;
1397 	    }
1398 
1399 	} else if ( strcmp( "-size_varies", argv[i] ) == 0 ) {
1400 	    if ( Tcl_GetDouble( ip, argv[i+1],
1401 		    &tree_types[num_tree_types].vary) != TCL_OK ) {
1402 		Tcl_AppendResult(ip, argv[0], ": invalid size variance",
1403 				 (char *)0 );
1404 		error = 1;
1405 	    }
1406 	} else {
1407 	    print_warning( TCL_WARNING, "tux_props_cb: unrecognized "
1408 			    "parameter '%s'", argv[i]);
1409 	    /* not sure if next arg is valid command - try it */
1410 	    i -= 1;
1411 	}
1412     }
1413 
1414     if ( tree_types[num_tree_types].name == 0 ||
1415 		tree_types[num_tree_types].poly.vertices == 0 ||
1416 		tree_types[num_tree_types].texture == 0 ) {
1417 	Tcl_AppendResult(ip, argv[0], ": some mandatory elements not filled",
1418 		" tree name, texture name and tree polygon must be supplied.",
1419 		(char *)0 );
1420 	free( tree_types[num_tree_types].name );
1421 	free( tree_types[num_tree_types].texture );
1422 	free( tree_types[num_tree_types].poly.vertices );
1423 	free( tree_types[num_tree_types].poly.polygons );
1424 	return TCL_ERROR;
1425 
1426     } else if ( error ) {
1427 	free( tree_types[num_tree_types].name );
1428 	free( tree_types[num_tree_types].texture );
1429 	free( tree_types[num_tree_types].poly.vertices );
1430 	free( tree_types[num_tree_types].poly.polygons );
1431 	return TCL_ERROR;
1432 
1433     } else if (!bind_texture( tree_types[num_tree_types].name,
1434 		tree_types[num_tree_types].texture )) {
1435 	Tcl_AppendResult(ip, argv[0], ": could not bind texture ",
1436 		    tree_types[num_tree_types].texture, (char *) 0);
1437 	free( tree_types[num_tree_types].name );
1438 	free( tree_types[num_tree_types].texture );
1439 	free( tree_types[num_tree_types].poly.vertices );
1440 	free( tree_types[num_tree_types].poly.polygons );
1441 	return TCL_ERROR;
1442     }
1443 
1444     num_tree_types += 1;
1445     return TCL_OK;
1446 }
1447 
item_spec_cb(ClientData cd,Tcl_Interp * ip,int argc,char * argv[])1448 static int item_spec_cb( ClientData cd, Tcl_Interp *ip,
1449 				 int argc, char *argv[])
1450 {
1451     int          rtn, num_col;
1452     char **      indices = NULL;
1453     int          convert_temp;
1454     char *       err_msg = "";
1455     char         buff[BUFF_LEN];
1456 
1457     if ( num_item_types + 1 >= MAX_ITEM_TYPES ) {
1458 	Tcl_AppendResult(ip, argv[0], ": max number of item types reached",
1459 			 (char *)0 );
1460 	return TCL_ERROR;
1461     }
1462 
1463     item_types[num_item_types].name = NULL;
1464     item_types[num_item_types].texture = NULL;
1465     item_types[num_item_types].diam = .8;
1466     item_types[num_item_types].height = 0.5;
1467     item_types[num_item_types].above_ground = 0.0;
1468     item_types[num_item_types].red = 255;
1469     item_types[num_item_types].green = 255;
1470     item_types[num_item_types].blue = 255;
1471     item_types[num_item_types].nocollision = False;
1472     item_types[num_item_types].reset_point = False;
1473     item_types[num_item_types].pos = NULL;
1474     item_types[num_item_types].insert_pos = NULL;
1475     item_types[num_item_types].num_items = 0;
1476     item_types[num_item_types].use_normal = False;
1477 
1478     NEXT_ARG;
1479 
1480     while ( *argv != NULL ) {
1481 	if ( strcmp( "-name", *argv ) == 0 ) {
1482 	    NEXT_ARG;
1483 	    CHECK_ARG( "-name", err_msg, item_spec_bail );
1484 
1485 	    item_types[num_item_types].name = string_copy(*argv);
1486 
1487 	} else if ( strcmp( "-height", *argv ) == 0 ) {
1488 	    NEXT_ARG;
1489 	    CHECK_ARG( "-height", err_msg, item_spec_bail );
1490 
1491 	    if ( Tcl_GetDouble( ip, *argv,
1492 		    &item_types[num_item_types].height) != TCL_OK ) {
1493 		Tcl_AppendResult(ip, argv[0], ": invalid height\n",
1494 					(char *) 0);
1495 	    }
1496 
1497 	} else if ( strcmp( "-diameter", *argv ) == 0 ) {
1498 	    NEXT_ARG;
1499 	    CHECK_ARG( "-diameter", err_msg, item_spec_bail );
1500 
1501 	    if ( Tcl_GetDouble( ip, *argv,
1502 		    &item_types[num_item_types].diam) != TCL_OK ) {
1503 		Tcl_AppendResult(ip, argv[0], ": invalid diameter\n",
1504 					(char *) 0);
1505 	    }
1506 
1507 	} else if ( strcmp( "-texture", *argv ) == 0 ) {
1508 	    NEXT_ARG;
1509 	    CHECK_ARG( "-texture", err_msg, item_spec_bail );
1510 
1511 	    if ( !item_types[num_item_types].texture ) {
1512 		item_types[num_item_types].texture = string_copy(*argv);
1513 	    } else {
1514 		Tcl_AppendResult(ip, argv[0], ": specify only one texture\n",
1515 				(char *)0 );
1516 	    }
1517 
1518 	} else if ( strcmp( "-above_ground", *argv ) == 0 ) {
1519 	    NEXT_ARG;
1520 	    CHECK_ARG( "-above_ground", err_msg, item_spec_bail );
1521 
1522 	    if ( Tcl_GetDouble( ip, *argv,
1523 		    &item_types[num_item_types].above_ground) != TCL_OK ) {
1524 		Tcl_AppendResult(ip, argv[0], ": invalid height above ground\n",
1525 					(char *) 0);
1526 	    }
1527 
1528 	} else if ( strcmp( "-colour", *argv ) == 0 ||
1529 		    strcmp( "-color", *argv ) == 0 )
1530 	{
1531 	    NEXT_ARG;
1532 	    CHECK_ARG( "-colour", err_msg, item_spec_bail );
1533 
1534 	    rtn = Tcl_SplitList(ip, *argv, &num_col, &indices);
1535 	    if( rtn != TCL_OK ) {
1536 		err_msg = "Must provide a list of colours for -colour";
1537 		goto item_spec_bail;
1538 	    }
1539 
1540 	    if (num_col == 3 || num_col == 4) {
1541 		Tcl_GetInt(ip, indices[0], &convert_temp);
1542 		item_types[num_item_types].red = (unsigned char) convert_temp;
1543 		Tcl_GetInt(ip, indices[1], &convert_temp);
1544 		item_types[num_item_types].green = (unsigned char) convert_temp;
1545 		Tcl_GetInt(ip, indices[2], &convert_temp);
1546 		item_types[num_item_types].blue = (unsigned char) convert_temp;
1547 	    } else {
1548 		err_msg = "Colour specification must have 3 or 4 elements";
1549 		goto item_spec_bail;
1550 	    }
1551 	    Tcl_Free((char *) indices);
1552 	    indices = NULL;
1553 
1554 	} else if ( strcmp( "-nocollision", *argv ) == 0 ||
1555 		    strcmp( "-nocollect", *argv ) == 0 ) {
1556 	    item_types[num_item_types].nocollision = True;
1557 
1558 	} else if ( strcmp( "-reset_point", *argv ) == 0 ) {
1559 	    item_types[num_item_types].reset_point = True;
1560 	    item_types[num_item_types].nocollision = True;
1561 	} else if ( strcmp( "-normal", *argv ) == 0 ) {
1562 	    NEXT_ARG;
1563 	    CHECK_ARG( "-normal", err_msg, item_spec_bail );
1564 
1565 	    if ( get_tcl_tuple(
1566 		ip, *argv, (scalar_t*)&(item_types[num_item_types].normal), 3 )
1567 		 != TCL_OK )
1568 	    {
1569 		err_msg = "Must specify a list of size three for -normal";
1570 		goto item_spec_bail;
1571 	    }
1572 
1573 	    normalize_vector( &(item_types[num_item_types].normal) );
1574 
1575 	    item_types[num_item_types].use_normal = True;
1576 
1577 	} else {
1578 	    sprintf( buff, "Unrecognized option `%s'", *argv );
1579 	    goto item_spec_bail;
1580 	}
1581 
1582 	NEXT_ARG;
1583     }
1584 
1585     if ( item_types[num_item_types].name == 0 ||
1586 	 ( item_types[num_item_types].texture == 0 &&
1587 	   item_types[num_item_types].reset_point == False ) )
1588     {
1589 	err_msg = "Some mandatory elements not filled.  "
1590 	    "Item name and texture name must be supplied.";
1591 	goto item_spec_bail;
1592     }
1593 
1594     if ( item_types[num_item_types].reset_point == False &&
1595 	 !bind_texture( item_types[num_item_types].name,
1596 			item_types[num_item_types].texture ))
1597     {
1598 	err_msg = "could not bind specified texture";
1599 	goto item_spec_bail;
1600     }
1601 
1602     num_item_types += 1;
1603     return TCL_OK;
1604 
1605 item_spec_bail:
1606     if ( indices ) {
1607 	Tcl_Free( (char*) indices );
1608 	indices = NULL;
1609     }
1610 
1611     if ( item_types[num_item_types].name ) {
1612 	free( item_types[num_item_types].name );
1613 	item_types[num_item_types].name = NULL;
1614     }
1615 
1616     if ( item_types[num_item_types].texture ) {
1617 	free( item_types[num_item_types].texture );
1618 	item_types[num_item_types].texture = NULL;
1619     }
1620 
1621     Tcl_AppendResult(
1622 	ip,
1623 	"Error in call to tux_item_spec: ",
1624 	err_msg,
1625 	"\n",
1626 	"Usage: tux_item_spec -name <name> -height <height> "
1627 	"-diameter <diameter> -colour {r g b [a]} "
1628 	"[-texture <texture>] [-above_ground <distance>] "
1629 	"[-nocollect] [-reset_point] [-normal {x y z}]",
1630 	(NULL) );
1631     return TCL_ERROR;
1632 }
1633 
register_course_load_tcl_callbacks(Tcl_Interp * ip)1634 void register_course_load_tcl_callbacks( Tcl_Interp *ip )
1635 {
1636     Tcl_CreateCommand (ip, "tux_course_dim", course_dim_cb,  0,0);
1637     Tcl_CreateCommand (ip, "tux_angle",      angle_cb,  0,0);
1638     Tcl_CreateCommand (ip, "tux_elev_scale", elev_scale_cb,   0,0);
1639     Tcl_CreateCommand (ip, "tux_elev",       elev_cb,        0,0);
1640     Tcl_CreateCommand (ip, "tux_terrain",    terrain_cb,   0,0);
1641     Tcl_CreateCommand (ip, "tux_trees",      trees_cb,   0,0);
1642     Tcl_CreateCommand (ip, "tux_tree_size",  tree_size_cb,   0,0);
1643     Tcl_CreateCommand (ip, "tux_tree_poly",  tree_poly_cb,   0,0);
1644     Tcl_CreateCommand (ip, "tux_bgnd_img",   bgnd_img_cb,   0,0);
1645     Tcl_CreateCommand (ip, "tux_tree_tex",   tree_tex_cb,   0,0);
1646     Tcl_CreateCommand (ip, "tux_ice_tex",    ice_tex_cb,   0,0);
1647     Tcl_CreateCommand (ip, "tux_rock_tex",   rock_tex_cb,   0,0);
1648     Tcl_CreateCommand (ip, "tux_snow_tex",   snow_tex_cb,   0,0);
1649     Tcl_CreateCommand (ip, "tux_start_pt",   start_pt_cb,   0,0);
1650     Tcl_CreateCommand (ip, "tux_friction",   friction_cb,   0,0);
1651     Tcl_CreateCommand (ip, "tux_course_author", course_author_cb, 0,0);
1652     Tcl_CreateCommand (ip, "tux_course_name", course_name_cb, 0,0);
1653     Tcl_CreateCommand (ip, "tux_base_height_value", base_height_value_cb, 0,0);
1654     Tcl_CreateCommand (ip, "tux_tree_props",  tree_props_cb,   0,0);
1655     Tcl_CreateCommand (ip, "tux_item_spec",  item_spec_cb,   0,0);
1656 }
1657 
1658