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