Back to index

extremetuxracer  0.5beta
course_load.cpp
Go to the documentation of this file.
00001 /* 
00002  * PPRacer 
00003  * Copyright (C) 2004-2005 Volker Stroebel <volker@planetpenguin.de>
00004  *
00005  * Copyright (C) 1999-2001 Jasmin F. Patry
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020  */
00021 
00022 #include "course_load.h"
00023 #include "course_render.h"
00024 #include "course_quad.h"
00025 
00026 #include "game_config.h"
00027 
00028 #include "ppgltk/images/image.h"
00029 
00030 #include "textures.h"
00031 #include "phys_sim.h"
00032 #include "tcl_util.h"
00033 #include "keyframe.h"
00034 #include "gl_util.h"
00035 #include "lights.h"
00036 #include "fog.h"
00037 #include "snow.h"
00038 #include "part_sys.h"
00039 
00040 #include "hud.h"
00041 
00042 #include "player.h"
00043 
00044 #include "ppgltk/audio/audio_data.h"
00045 
00046 #include "track_marks.h"
00047 
00048 #include "ppgltk/alg/defs.h"
00049 
00050 #include "translation.h"
00051 
00052 #define MAX_TREES 8192
00053 #define MAX_TREE_TYPES 32
00054 #define MAX_ITEMS 8192
00055 #define MAX_ITEM_TYPES 128
00056 
00057 #define MIN_ANGLE 5
00058 #define MAX_ANGLE 80
00059 
00060 #define ICE_IMG_VAL  0
00061 #define ROCK_IMG_VAL 127
00062 #define SNOW_IMG_VAL 255
00063 #define TREE_IMG_THRESHOLD 128
00064 
00065 #define DEP_TREE_RED 255
00066 #define DEP_TREE_GREEN 0
00067 #define DEP_TREE_BLUE 0
00068 
00069 terrain_tex_t terrain_texture[NUM_TERRAIN_TYPES];
00070 unsigned int num_terrains=0;
00071 
00072 static bool        course_loaded = false;
00073 
00074 static float     *elevation;
00075 static float      elev_scale;
00076 static float      course_width, course_length;
00077 static float      play_width, play_length;
00078 static float      course_angle;
00079 static int           nx, ny;
00080 static Tree        tree_locs[MAX_TREES];
00081 static int           num_trees;
00082 static int    *terrain;
00083 static pp::Vec2d     start_pt;
00084 
00085 
00086 static std::string courseAuthor;
00087 static std::string courseName;
00088 
00089 
00090 static int           base_height_value;
00091 static tree_type_t   tree_types[MAX_TREE_TYPES];
00092 static int           num_tree_types = 0;
00093 static int           tree_dep_call = -1;
00094 
00095 static Item                 item_locs[MAX_ITEMS];
00096 static item_type_t   item_types[MAX_ITEM_TYPES];
00097 static int           num_item_types = 0;
00098 static int           num_items;
00099 
00100 
00103 std::list<int> usedTerrains;
00104 
00105 
00106 /* Interleaved vertex, normal, and color data */
00107 static GLubyte      *vnc_array = NULL;
00108 
00109 float    *get_course_elev_data()    { return elevation; }
00110 int    *get_course_terrain_data() { return terrain; }
00111 float      get_course_angle()        { return course_angle; } 
00112 Tree       *get_tree_locs()           { return tree_locs; }
00113 int           get_num_trees()           { return num_trees; }
00114 pp::Polyhedron*  get_tree_polyhedron(int type) { return tree_types[type].ph; }
00115 const char* get_tree_name(int type)       { return tree_types[type].name.c_str(); }
00116 pp::Vec2d     get_start_pt()            { return start_pt; }
00117 void          set_start_pt( pp::Vec2d p ) { start_pt = p; }
00118 std::string& get_course_author() { return courseAuthor; }
00119 std::string& get_course_name() { return courseName; }
00120 
00121 Item       *get_item_locs()           { return item_locs; }
00122 int           get_num_items()           { return num_items; }
00123 const char         *get_item_name(int type)   { return item_types[type].name.c_str(); }
00124 int           get_num_item_types()      { return num_item_types; }
00125 item_type_t  *get_item_types()          { return item_types; }
00126 
00127 
00128 
00129 void get_gl_arrays( GLubyte **vnc_arr )
00130 {
00131     *vnc_arr = vnc_array;
00132 }
00133 
00134 void get_course_dimensions( float *width, float *length )
00135 {
00136     *width = course_width;
00137     *length = course_length;
00138 } 
00139 
00140 void get_play_dimensions( float *width, float *length )
00141 {
00142     *width = play_width;
00143     *length = play_length;
00144 } 
00145 
00156 float get_terrain_base_height( float distance )
00157 {
00158     float slope = tan( ANGLES_TO_RADIANS( course_angle ) );
00159     float base_height;
00160     
00161     check_assertion( distance > -EPS,
00162                    "distance should be positive" );
00163 
00164     /* This will need to be fixed once we add variably-sloped terrain */
00165     base_height = -slope * distance - 
00166        base_height_value / 255.0 * elev_scale;
00167     return base_height;
00168 }
00169 
00180 float get_terrain_max_height( float distance )
00181 {
00182     return get_terrain_base_height( distance ) + elev_scale;
00183 }
00184 
00185 void get_course_divisions( int *x, int *y )
00186 {
00187     *x = nx;
00188     *y = ny;
00189 } 
00190 
00191 
00192 static void reset_course()
00193 {
00194     int i;
00195 
00196     /*
00197      * Defaults
00198      */
00199        num_terrains = 0;
00200     num_trees     = 0;
00201     num_items     = 0;
00202     course_angle  = 20.;
00203     course_width  = 50.;
00204     course_length = 130.;
00205     play_width  = 50.;
00206     play_length = 130.;
00207     nx = ny = -1;
00208     start_pt.x = 0;
00209     start_pt.y = 0;
00210     base_height_value = 127; /* 50% grey */
00211 
00212     set_course_mirroring( false );
00213 
00214     reset_lights();
00215     fogPlane.reset();
00216     reset_particles();
00217 
00218        courseAuthor.erase();
00219        courseName.erase();
00220 
00221        if ( course_loaded == false ) return;
00222 
00223     reset_course_quadtree();
00224 
00225     free( elevation ); elevation = NULL;
00226     free( terrain ); terrain = NULL;
00227 
00228     free( vnc_array ); vnc_array = NULL;
00229 
00230     for ( i = 0; i < num_tree_types; i++) {
00231        unbind_texture( tree_types[i].name.c_str() );
00232        tree_types[i].name.erase();
00233 
00234        tree_types[i].num_trees = 0;
00235 
00236     }
00237     num_tree_types = 0;
00238     tree_dep_call = -1;
00239 
00240     for ( i = 0; i < num_item_types; i++) {
00241        if (item_types[i].reset_point == false) {
00242            unbind_texture( item_types[i].name.c_str() );
00243        }
00244 
00245        item_types[i].name.erase();
00246 
00247        item_types[i].texture.erase();
00248 
00249        item_types[i].num_items = 0;
00250     }
00251     num_item_types = 0;
00252 
00253     course_loaded = false;
00254 
00255     reset_key_frame();
00256 } 
00257 
00258 bool course_exists( int num )
00259 {
00260     char buff[BUFF_LEN];
00261     struct stat s;
00262 
00263     sprintf( buff, "%s/courses/%d", getparam_data_dir(), num );
00264     if ( stat( buff, &s ) != 0 ) {
00265        return false;
00266     }
00267     if ( ! S_ISDIR( s.st_mode ) ) {
00268        return false;
00269     }
00270     return true;
00271 }
00272 
00273 void fill_gl_arrays()
00274 {
00275     int x,y;
00276     pp::Vec3d *normals = get_course_normals();
00277     pp::Vec3d nml;
00278     int idx;
00279 
00280     glDisableClientState(GL_VERTEX_ARRAY);
00281     glDisableClientState(GL_NORMAL_ARRAY);
00282     glDisableClientState(GL_COLOR_ARRAY);
00283 
00284     /* Align vertices and normals on 16-byte intervals (Q3A does this) */
00285     vnc_array = (GLubyte*) malloc( STRIDE_GL_ARRAY * nx * ny );
00286 
00287     for (x=0; x<nx; x++) {
00288        for (y=0; y<ny; y++) {
00289            idx = STRIDE_GL_ARRAY*(y*nx+x);
00290           
00291 #define floatval(i) (*(GLfloat*)(vnc_array+idx+(i)*sizeof(GLfloat)))
00292 
00293            floatval(0) = (GLfloat)x / (nx-1.) * course_width;
00294            floatval(1) = ELEV(x,y);
00295            floatval(2) = -(GLfloat)y / (ny-1.) * course_length;
00296 
00297            nml = normals[ x + y * nx ];
00298            floatval(4) = nml.x;
00299            floatval(5) = nml.y;
00300            floatval(6) = nml.z;
00301            floatval(7) = 1.0f;
00302           
00303 #undef floatval
00304 #define byteval(i) (*(GLubyte*)(vnc_array+idx+8*sizeof(GLfloat) +\
00305     i*sizeof(GLubyte)))
00306 
00307            byteval(0) = 255;
00308            byteval(1) = 255;
00309            byteval(2) = 255;
00310            byteval(3) = 255;
00311 
00312 #undef byteval
00313 
00314        }
00315     }
00316 
00317     glEnableClientState(GL_VERTEX_ARRAY);
00318     glVertexPointer( 3, GL_FLOAT, STRIDE_GL_ARRAY, vnc_array );
00319 
00320     glEnableClientState(GL_NORMAL_ARRAY);
00321     glNormalPointer( GL_FLOAT, STRIDE_GL_ARRAY, 
00322                    vnc_array + 4*sizeof(GLfloat) );
00323 
00324     glEnableClientState(GL_COLOR_ARRAY);
00325     glColorPointer( 4, GL_UNSIGNED_BYTE, STRIDE_GL_ARRAY, 
00326                   vnc_array + 8*sizeof(GLfloat) );
00327 }
00328 
00329 void load_course( std::string& course )
00330 {
00331     char buff[BUFF_LEN];
00332     char cwd[BUFF_LEN];
00333 
00334     reset_course();
00335        HUD1.reset();
00336        
00337        
00338     if ( getcwd( cwd, BUFF_LEN ) == NULL ) {
00339        handle_system_error( 1, "getcwd failed" );
00340     }
00341     if (course[0]=='/'){
00342               if ( chdir(course.c_str() ) != 0 ) {      
00343                      handle_system_error( 1, "Couldn't chdir to %s", course.c_str() );
00344        } 
00345        }else{
00346               sprintf( buff, "%s/courses/%s", getparam_data_dir(), course.c_str() );
00347               if ( chdir( buff ) != 0 ) {
00348                      handle_system_error( 1, "Couldn't chdir to %s", buff );
00349        } 
00350        }
00351        
00352     if ( Tcl_EvalFile( tclInterp, "./course.tcl") == TCL_ERROR ) {
00353        handle_error( 1, "Error evaluating %s/course.tcl: %s",  
00354                     buff, Tcl_GetStringResult( tclInterp ) );
00355     } 
00356 
00357     if ( chdir( cwd ) != 0 ) {
00358        handle_system_error( 1, "Couldn't chdir to %s", cwd );
00359     } 
00360 
00361     check_assertion( !Tcl_InterpDeleted( tclInterp ),
00362                    "Tcl interpreter deleted" );
00363 
00364     calc_normals();
00365 
00366     fill_gl_arrays();
00367 
00368     init_course_quadtree( elevation, nx, ny, course_width/(nx-1.), 
00369                        -course_length/(ny-1),
00370                        players[0].view.pos, 
00371                        getparam_course_detail_level() );
00372 
00373     init_track_marks();
00374 
00375     course_loaded = true;
00376 
00377     /* flush unused audio files */
00378     delete_unused_audio_data();
00379 } 
00380 
00381 
00382 static inline int intensity_to_terrain( const int intensity )
00383 {
00384        for (unsigned int i=0; i<num_terrains; i++) {
00385               if (terrain_texture[i].value == intensity){
00386                      terrain_texture[i].count++;
00387                      return i;
00388               }
00389        }
00390        terrain_texture[0].count++;
00391        return 0;
00392 }
00393 
00394 
00395 static int course_dim_cb ( ClientData cd, Tcl_Interp *ip, 
00396                         int argc, CONST84 char *argv[]) 
00397 {
00398     double width, length;
00399 
00400     if ( ( argc != 3 ) && ( argc != 5 ) ) {
00401         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00402                       "Usage: ", argv[0], " <course width> <course length>",
00403                       " [<play width> <play length>]", (char *)0 );
00404         return TCL_ERROR;
00405     }
00406 
00407     if ( Tcl_GetDouble( ip, argv[1], &width ) != TCL_OK ) {
00408         Tcl_AppendResult(ip, argv[0], ": invalid course width", 
00409                       (char *)0 );
00410         return TCL_ERROR;
00411     } 
00412     if ( Tcl_GetDouble( ip, argv[2], &length ) != TCL_OK ) {
00413         Tcl_AppendResult(ip, argv[0], ": invalid course length", 
00414                       (char *)0 );
00415         return TCL_ERROR;
00416     } 
00417 
00418     course_width = width;
00419     course_length = length;
00420 
00421     if ( argc == 5 ) {
00422        if ( Tcl_GetDouble( ip, argv[3], &width ) != TCL_OK ) {
00423            Tcl_AppendResult(ip, argv[0], ": invalid play width", 
00424                           (char *)0 );
00425            return TCL_ERROR;
00426        } 
00427        if ( Tcl_GetDouble( ip, argv[4], &length ) != TCL_OK ) {
00428            Tcl_AppendResult(ip, argv[0], ": invalid play length", 
00429                           (char *)0 );
00430            return TCL_ERROR;
00431        } 
00432        play_width = width;
00433        play_length = length;
00434     } else {
00435        play_width = course_width;
00436        play_length = course_length;
00437     }
00438 
00439     return TCL_OK;
00440 } 
00441 
00442 static int angle_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00443 {
00444     double angle;
00445 
00446     if ( argc != 2 ) {
00447         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00448                       "Usage: ", argv[0], " <angle>",
00449                       (char *)0 );
00450         return TCL_ERROR;
00451     } 
00452 
00453     if ( Tcl_GetDouble( ip, argv[1], &angle ) != TCL_OK ) {
00454         return TCL_ERROR;
00455     } 
00456 
00457     if ( angle < MIN_ANGLE ) {
00458        print_warning( TCL_WARNING, "course angle is too small. Setting to %f",
00459                      MIN_ANGLE );
00460        angle = MIN_ANGLE;
00461     }
00462 
00463     if ( MAX_ANGLE < angle ) {
00464        print_warning( TCL_WARNING, "course angle is too large. Setting to %f",
00465                      MAX_ANGLE );
00466        angle = MAX_ANGLE;
00467     }
00468 
00469     course_angle = angle;
00470 
00471     return TCL_OK;
00472 } 
00473 
00474 
00475 static int elev_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00476 {
00477     pp::Image *elev_img;
00478     float slope;
00479     int   x,y;
00480     int   pad;
00481 
00482     if ( argc != 2 ) {
00483         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00484                       "Usage: ", argv[0], " <elevation bitmap>",
00485                       (char *)0 );
00486         return TCL_ERROR;
00487     } 
00488 
00489     if (course_loaded) {
00490        print_warning( TCL_WARNING, "ignoring %s: course already loaded",
00491                      argv[0] );
00492        return TCL_OK;
00493     }
00494 
00495     elev_img = pp::Image::readFile( argv[1] );
00496     if ( elev_img == NULL ) {
00497        print_warning( TCL_WARNING, "%s: couldn't load %s", argv[0], argv[1] );
00498        return TCL_ERROR;
00499     }
00500        
00501     nx = elev_img->width;
00502     ny = elev_img->height;
00503 
00504     elevation = (float *)malloc( sizeof(float)*nx*ny );
00505 
00506     if ( elevation == NULL ) {
00507               handle_system_error( 1, "malloc failed" );
00508     }
00509 
00510     slope = tan( ANGLES_TO_RADIANS( course_angle ) );
00511 
00512        //elev_img->sizeZ=4;
00513        
00514     pad = 0;    /* RGBA images rows are aligned on 4-byte boundaries */
00515     for (y=0; y<ny; y++) {
00516         for (x=0; x<nx; x++) {
00517            ELEV(nx-1-x, ny-1-y) = 
00518               ( ( elev_img->data[ (x + nx * y) * elev_img->depth + pad ] 
00519                   - base_height_value ) / 255.0 ) * elev_scale
00520               - (double) (ny-1.-y)/ny * course_length * slope;
00521         } 
00522         //pad += (nx*elev_img->depth) % 4;
00523     } 
00524 
00525        delete elev_img;
00526        
00527     return TCL_OK;
00528 } 
00529 
00530 
00531 static bool
00532 sort_terrain(const int x, const int y)
00533 {
00534        if(terrain_texture[x].wheight < terrain_texture[y].wheight){
00535               return true;
00536        }else{
00537               return false;
00538        }      
00539 }
00540 
00541 
00542 static int terrain_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00543 {
00544     pp::Image *terrain_img;
00545     int   x,y;
00546     int   pad;
00547     int   idx;
00548        int    terrain_value;
00549        int    image_pointer;
00550 
00551     if ( argc != 2 ) {
00552         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00553                       "Usage: ", argv[0], " <terrain bitmap>",
00554                       (char *)0 );
00555         return TCL_ERROR;
00556     } 
00557 
00558     terrain_img = pp::Image::readFile( argv[1] );
00559 
00560     if ( terrain_img == NULL ) {
00561        print_warning( TCL_WARNING, "%s: couldn't load %s", argv[0], argv[1] );
00562         Tcl_AppendResult(ip, argv[0], ": couldn't load ", argv[1],
00563                       (char *)0 );
00564        return TCL_ERROR;
00565     }
00566 
00567     if ( nx != terrain_img->width || ny != terrain_img->height ) {
00568         Tcl_AppendResult(ip, argv[0], ": terrain bitmap must have same " 
00569                       "dimensions as elevation bitmap",
00570                       (char *)0 );
00571 
00572        return TCL_ERROR;
00573     }
00574 
00575     terrain = (int *)malloc( sizeof(int)*nx*ny );
00576 
00577     if ( terrain == NULL ) {
00578        handle_system_error( 1, "malloc failed" );
00579     }
00580        
00581        pad = 0;
00582        
00583     for (y=0; y<ny; y++) {
00584         for (x=0; x<nx; x++) {
00585                      
00586             idx = (nx-1-x) + nx*(ny-1-y); 
00587                      image_pointer=(x+nx*y)*terrain_img->depth+pad;
00588                      
00589                      terrain_value=terrain_img->data[image_pointer] +
00590                                           (terrain_img->data[image_pointer+1] << 8)+ 
00591                                           (terrain_img->data[image_pointer+2] << 16);
00592                      
00593            terrain[idx] = intensity_to_terrain(terrain_value);
00594               
00595         } 
00596         //pad += (nx*terrain_img->depth) % 4;
00597     } 
00598        
00599     delete terrain_img;
00600 
00601        //build sorted list with used terrains for quadtree
00602        usedTerrains.clear();
00603        for (unsigned int i=0; i<num_terrains; i++){
00604               //check if the terraintype is used in the course
00605               if(terrain_texture[i].count>0){
00606                      usedTerrains.push_back(i);
00607               }
00608        }
00609        usedTerrains.sort(sort_terrain);
00610        
00611     return TCL_OK;
00612 } 
00613 
00614 static int bgnd_img_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00615 {
00616 
00617     if ( argc != 2 ) {
00618         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00619                       "Usage: ", argv[0], " <background image>",
00620                       (char *)0 );
00621         return TCL_ERROR;
00622     } 
00623 
00624     if (!load_and_bind_texture( "background", argv[1] )) {
00625       Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
00626       return TCL_ERROR;
00627        }
00628 
00629     return TCL_OK;
00630 }
00631 
00632 static int terrain_tex_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00633 {
00634        
00635     int i;
00636        int  rtn, num_col;
00637        CONST84 char* text_bind =NULL;
00638        int convert_temp;
00639        CONST84 char** indices = 0;
00640 
00641        if (num_terrains>=NUM_TERRAIN_TYPES){
00642               Tcl_AppendResult(ip, argv[0], ": max number og terrains reached", (char *)0 );
00643         return TCL_ERROR;
00644        }
00645        
00646 
00647        /* fill in values not specified with defaults */
00648        terrain_texture[num_terrains].type=1;
00649        terrain_texture[num_terrains].value=0;
00650        terrain_texture[num_terrains].friction=0.5;
00651        terrain_texture[num_terrains].compression=0.1;
00652        terrain_texture[num_terrains].texbind=0;
00653        terrain_texture[num_terrains].partbind=0;
00654        terrain_texture[num_terrains].soundactive=false;
00655        terrain_texture[num_terrains].trackmark.head=0;
00656        terrain_texture[num_terrains].trackmark.mark=0;
00657        terrain_texture[num_terrains].trackmark.tail=0;
00658        terrain_texture[num_terrains].wheight=150;
00659        terrain_texture[num_terrains].count=0;
00660        
00661 
00662     for ( i = 1; (i < argc - 1); i += 2 ) {
00663        
00664        if ( strcmp( "-name", argv[i] ) == 0 ) {
00665               text_bind=argv[i+1];
00666        
00667        } else if ( strcmp( "-texture", argv[i]) == 0 ) {
00668               if ( !load_and_bind_texture( text_bind,argv[i+1]) ) {
00669               Tcl_AppendResult(ip, argv[0], ": could not load texture", (char *) 0);
00670                      return TCL_ERROR;
00671        }
00672           if (!get_texture_binding(text_bind, &terrain_texture[num_terrains].texbind)) {
00673           terrain_texture[num_terrains].texbind = 0;
00674        }
00675               
00676        } else if ( strcmp( "-wheight", argv[i] ) == 0 ) {
00677            if ( Tcl_GetInt( ip, argv[i+1],
00678                   &terrain_texture[num_terrains].wheight) != TCL_OK ){
00679                             Tcl_AppendResult(ip, argv[0], ": invalid wheight",
00680                              (char *)0 );
00681                      return TCL_ERROR;    
00682            }
00683        } else if ( strcmp( "-color", argv[i] ) == 0 ) {
00684            rtn = Tcl_SplitList(ip, argv[i+1], &num_col, &indices);
00685            if( rtn != TCL_OK ) {
00686               Tcl_AppendResult(ip, "a list of colors must be provided\n",
00687                           (char *) 0);
00688               Tcl_Free((char *) indices);
00689               return TCL_ERROR;
00690            }
00691 
00692            if (num_col == 3 || num_col == 4) {
00693               Tcl_GetInt(ip, indices[0], &convert_temp);
00694               terrain_texture[num_terrains].value = (unsigned char) convert_temp;
00695               Tcl_GetInt(ip, indices[1], &convert_temp);
00696               terrain_texture[num_terrains].value += (unsigned char) convert_temp << 8;
00697               Tcl_GetInt(ip, indices[2], &convert_temp);
00698               terrain_texture[num_terrains].value += (unsigned char) convert_temp << 16;
00699                      
00700            } else {
00701               Tcl_AppendResult(ip, argv[0], ": must specify three colors"
00702                      " to link with terrain type", (char *) 0);
00703               return TCL_ERROR;
00704            }
00705            Tcl_Free((char *) indices);
00706        }  else if ( strcmp( "-friction", argv[i] ) == 0 ) {
00707            if ( Tcl_GetDouble( ip, argv[i+1],
00708                   &terrain_texture[num_terrains].friction) != TCL_OK ) {
00709                             Tcl_AppendResult(ip, argv[0], ": invalid friction",
00710                              (char *)0 );
00711                      return TCL_ERROR;    
00712            }
00713        }else if ( strcmp( "-compression", argv[i] ) == 0 ) {
00714            if ( Tcl_GetDouble( ip, argv[i+1],
00715                   &terrain_texture[num_terrains].compression) != TCL_OK ) {
00716                             Tcl_AppendResult(ip, argv[0], ": invalid compression",
00717                              (char *)0 );
00718                      return TCL_ERROR;
00719            }
00720        }else if ( strcmp( "-particles", argv[i]) == 0 ) {
00721               if (!get_texture_binding(argv[i+1], &terrain_texture[num_terrains].partbind)) {
00722                      terrain_texture[num_terrains].texbind = 0;
00723               }
00724        } else if ( strcmp( "-envmap_texture", argv[i]) == 0 ) {
00725               if (!get_texture_binding(argv[i+1], &terrain_texture[num_terrains].envmapbind)) {
00726                      terrain_texture[num_terrains].envmapbind = 0;
00727               }
00728        } else if ( strcmp( "-track_head", argv[i]) == 0 ) {
00729               if (!get_texture_binding(argv[i+1], &terrain_texture[num_terrains].trackmark.head)) {
00730                      terrain_texture[num_terrains].trackmark.head = 0;
00731               }
00732        } else if ( strcmp( "-track_mark", argv[i]) == 0 ) {
00733               if (!get_texture_binding(argv[i+1], &terrain_texture[num_terrains].trackmark.mark)) {
00734                      terrain_texture[num_terrains].trackmark.mark = 0;
00735               }
00736        } else if ( strcmp( "-track_tail", argv[i]) == 0 ) {
00737               if (!get_texture_binding(argv[i+1], &terrain_texture[num_terrains].trackmark.tail)) {
00738                       terrain_texture[num_terrains].trackmark.tail = 0;
00739               }
00740        } else if ( strcmp( "-sound", argv[i]) == 0 ) {
00741               terrain_texture[num_terrains].sound = argv[i+1];        
00742        }
00743 
00744        
00745        
00746    } 
00747 
00748     num_terrains++;
00749     return TCL_OK;
00750        
00751        
00752 } 
00753 
00754 static int snow_type_cb(ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]){
00755     
00756     if ( argc != SnowTypeArgCount + 2) { // the arguments for the type, plus the index, plus the arg for the function name itself (argv[0])
00757         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00758                       "Usage: ", argv[0], " <index of the type> <speed> <minSize> <maxSize> <number of particles> <number of near particles>",
00759                       (char *)0 );
00760         return TCL_ERROR;
00761     }
00762     SnowType type;
00763     int index;
00764     
00765     if ( Tcl_GetInt( ip, argv[1],
00766                   &index) != TCL_OK ){
00767                             Tcl_AppendResult(ip, argv[0], ": invalid index",
00768                              (char *)0 );
00769                      return TCL_ERROR;    
00770            }
00771     if ( Tcl_GetDouble( ip, argv[2],
00772                   &(type.speed)) != TCL_OK ){
00773                             Tcl_AppendResult(ip, argv[0], ": invalid speed",
00774                              (char *)0 );
00775                      return TCL_ERROR;    
00776            }
00777     if ( Tcl_GetDouble( ip, argv[3],
00778                   &(type.minSize)) != TCL_OK ){
00779                             Tcl_AppendResult(ip, argv[0], ": invalid minSize",
00780                              (char *)0 );
00781                      return TCL_ERROR;    
00782            }
00783     if ( Tcl_GetDouble( ip, argv[4],
00784                   &(type.maxSize)) != TCL_OK ){
00785                             Tcl_AppendResult(ip, argv[0], ": invalid maxSize",
00786                              (char *)0 );
00787                      return TCL_ERROR;    
00788            }
00789     if ( Tcl_GetInt( ip, argv[5],
00790                   &(type.MAXPART)) != TCL_OK ){
00791                             Tcl_AppendResult(ip, argv[0], ": invalid MAXPART",
00792                              (char *)0 );
00793                      return TCL_ERROR;    
00794            }
00795     if ( Tcl_GetInt( ip, argv[6],
00796                   &(type.MAXNEAR)) != TCL_OK ){
00797                             Tcl_AppendResult(ip, argv[0], ": invalid MAXNEAR",
00798                              (char *)0 );
00799                      return TCL_ERROR;    
00800            }
00801     
00802     RegisterSnowType(index,type);
00803     return TCL_OK;//Must return TCL_OK, otherwise the program just crashes because the Tcl interpreter assumes the absence of return value signals an error
00804 }
00805 
00806 static int wind_type_cb(ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]){
00807     
00808     if ( argc != WindTypeArgCount + 2) { // the arguments for the type, plus the index, plus the arg for the function name itself(argv[0])
00809         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00810                       "Usage: ", argv[0], " <index of the type> <wind_vel.x> <wind_vel.y> <wind_vel.z>",
00811                       (char *)0 );
00812         return TCL_ERROR;
00813     }
00814     WindType type;
00815     int index;
00816     
00817     if ( Tcl_GetInt( ip, argv[1],
00818                   &index) != TCL_OK ){
00819                             Tcl_AppendResult(ip, argv[0], ": invalid index",
00820                              (char *)0 );
00821                      return TCL_ERROR;    
00822            }
00823     if ( Tcl_GetDouble( ip, argv[2],
00824                   &(type.wind_vel.x)) != TCL_OK ){
00825                             Tcl_AppendResult(ip, argv[0], ": invalid wind_vel.x",
00826                              (char *)0 );
00827                      return TCL_ERROR;    
00828            }
00829     if ( Tcl_GetDouble( ip, argv[3],
00830                   &(type.wind_vel.y)) != TCL_OK ){
00831                             Tcl_AppendResult(ip, argv[0], ": invalid wind_vel.y",
00832                              (char *)0 );
00833                      return TCL_ERROR;    
00834            }
00835     if ( Tcl_GetDouble( ip, argv[4],
00836                   &(type.wind_vel.z)) != TCL_OK ){
00837                             Tcl_AppendResult(ip, argv[0], ": invalid wind_vel.z",
00838                              (char *)0 );
00839                      return TCL_ERROR;    
00840            }
00841     
00842     RegisterWindType(index,type);
00843     return TCL_OK;//Must return TCL_OK, otherwise the program just crashes because the Tcl interpreter assumes the absence of return value signals an error
00844 }
00845 
00846 static int start_pt_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00847 {
00848     double xcd, ycd;
00849 
00850     if ( argc != 3 ) {
00851         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00852                       "Usage: ", argv[0], " <x coord> <y coord>",
00853                       (char *)0 );
00854         return TCL_ERROR;
00855     } 
00856 
00857     if ( Tcl_GetDouble( ip, argv[1], &xcd ) != TCL_OK ) {
00858         Tcl_AppendResult(ip, argv[0], ": invalid x coordinate", (char *)0 );
00859         return TCL_ERROR;
00860     } 
00861     if ( Tcl_GetDouble( ip, argv[2], &ycd ) != TCL_OK ) {
00862         Tcl_AppendResult(ip, argv[0], ": invalid y coordinate", (char *)0 );
00863         return TCL_ERROR;
00864     } 
00865 
00866     if ( !( xcd > 0 && xcd < course_width ) ) {
00867        print_warning( TCL_WARNING, "%s: x coordinate out of bounds, "
00868                      "using 0\n", argv[0] );
00869        xcd = 0;
00870     }
00871 
00872     if ( !( ycd > 0 && ycd < course_length ) ) {
00873        print_warning( TCL_WARNING, "%s: y coordinate out of bounds, "
00874                      "using 0\n", argv[0] );
00875        ycd = 0;
00876     }
00877 
00878     start_pt.x = xcd;
00879     start_pt.y = -ycd;
00880 
00881     return TCL_OK;
00882 } 
00883 
00884 static int elev_scale_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00885 {
00886 
00887     double scale;
00888     
00889     if ( argc != 2 ) {
00890         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00891                       "Usage: ", argv[0], " <scale>",
00892                       (char *)0 );
00893         return TCL_ERROR;
00894     } 
00895 
00896     if ( Tcl_GetDouble( ip, argv[1], &scale ) != TCL_OK ) {
00897         Tcl_AppendResult(ip, argv[0], ": invalid scale", (char *)0 );
00898         return TCL_ERROR;
00899     } 
00900 
00901     if ( scale <= 0 ) {
00902        print_warning( TCL_WARNING, "%s: scale must be positive", argv[0] );
00903        return TCL_ERROR;
00904     }
00905 
00906     elev_scale = scale;
00907 
00908     return TCL_OK;
00909 } 
00910 
00911 static int is_tree( unsigned char pixel[], tree_type_t ** which_type )
00912 {
00913        int min_distance = pixel[0] + pixel[1] + pixel[2];
00914     
00915        int i;
00916     int distance;
00917  
00918     *which_type = NULL;
00919     for (i = 0; i < num_tree_types; i++) {
00920        /* assume red green blue pixel ordering */
00921        distance = abs ( tree_types[i].red - pixel[0] ) +
00922                   abs ( tree_types[i].green - pixel[1] ) +
00923                   abs ( tree_types[i].blue - pixel[2] );
00924        if (distance < min_distance) {
00925            min_distance = distance;
00926            *which_type = &tree_types[i];
00927        }
00928     }
00929     
00930     return min_distance;
00931 }
00932 
00933 static int is_item( unsigned char pixel[], item_type_t ** which_type )
00934 {
00935     int      min_distance = pixel[0] + pixel[1] + pixel[2];
00936     int i;
00937     int distance;
00938  
00939     *which_type = NULL;
00940     for (i = 0; i < num_item_types; i++) {
00941        /* assume red green blue pixel ordering */
00942        distance = abs ( item_types[i].red - pixel[0] ) +
00943                   abs ( item_types[i].green - pixel[1] ) +
00944                   abs ( item_types[i].blue - pixel[2] );
00945        if (distance < min_distance) {
00946            min_distance = distance;
00947            *which_type = &item_types[i];
00948        }
00949     }
00950     
00951     return min_distance;
00952 }
00953 
00954 static int trees_cb ( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
00955 {
00956     pp::Image *treeImg;
00957     int sx, sy, sz;
00958     int x,y;
00959     int pad;
00960     tree_type_t *which_tree;
00961     item_type_t *which_item;
00962     int i;
00963     int best_tree_dist, best_item_dist;
00964 
00965     if ( argc != 2 ) {
00966         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
00967                       "Usage: ", argv[0], " <tree location bitmap>",
00968                       (char *)0 );
00969         return TCL_ERROR;
00970     } 
00971 
00972     treeImg = pp::Image::readFile( argv[1] );
00973     if ( treeImg->data == NULL ) {
00974        print_warning( TCL_WARNING, "%s: couldn't load %s", 
00975                      argv[0], argv[1] );
00976         Tcl_AppendResult(ip, argv[0], ": couldn't load ", argv[1], 
00977                       (char *)0 );
00978        return TCL_ERROR;
00979     }
00980 
00981     if ( num_tree_types == 0 && num_item_types == 0 ) {
00982        print_warning( IMPORTANT_WARNING,
00983                      "tux_trees callback called with no tree or item "
00984                      "types set" );
00985     }
00986 
00987     sx = treeImg->width;
00988     sy = treeImg->height;
00989     sz = treeImg->depth;
00990 
00991     for (i = 0; i < num_tree_types; i++) {
00992               tree_types[i].num_trees = 0;
00993     }
00994 
00995     for (i = 0; i < num_item_types; i++) {
00996               item_types[i].num_items = 0;
00997     }
00998 
00999     num_trees = 0;
01000     num_items = 0;
01001     pad = 0;
01002     for (y=0; y<sy; y++) {
01003         for (x=0; x<sx; x++) {
01004                      
01005                      unsigned char* pixel=&treeImg->data[ (x + y*sx)*sz + pad ];
01006                      if(pixel[0]!=0 || pixel[1]!=0 || pixel[2]!=0){
01007                             
01008                             best_tree_dist = is_tree ( pixel,&which_tree );
01009               best_item_dist = is_item ( pixel,&which_item );
01010 
01011                      if ( best_tree_dist < best_item_dist && which_tree != NULL ) {
01012                      if (num_trees+1 == MAX_TREES ) {
01013                     fprintf( stderr, "%s: maximum number of trees reached.\n", 
01014                                    argv[0] );
01015                       break;
01016                   }
01017                                    num_trees += 1;
01018                                    which_tree->num_trees += 1;
01019                                                                       
01020                                    which_tree->pos.push_back(pp::Vec2d(
01021                                           (sx-x)/(double)(sx-1.)*course_width,
01022                                           -(sy-y)/(double)(sy-1.)*course_length));
01023                                    
01024                             } else if ( which_item != NULL ) {
01025                      if (num_items+1 == MAX_ITEMS ) {
01026                      fprintf( stderr, "%s: maximum number of items reached.\n", 
01027                                           argv[0] );
01028                             break;
01029                   }
01030                                    num_items += 1;
01031                                    which_item->num_items += 1;
01032                                    
01033                                    which_item->pos.push_back(pp::Vec2d(
01034                                           (sx-x)/(double)(sx-1.)*course_width,
01035                                           -(sy-y)/(double)(sy-1.)*course_length));
01036                                    
01037                                    }
01038                      }
01039                      }
01040                      //pad += ( sx * sz ) % 4; /* to compensate for word-aligned rows */
01041     }
01042 
01043     /*
01044     // double pass so that tree and object types are clumped together - reduce
01045     // texture switching
01046     */
01047        std::list<pp::Vec2d>::iterator it;
01048     num_trees = 0;
01049     for (i = 0; i < num_tree_types; i++) {
01050                      
01051               for(it=tree_types[i].pos.begin();it!=tree_types[i].pos.end();it++){
01052                   tree_locs[num_trees].ray.pt.x = (*it).x;
01053               tree_locs[num_trees].ray.pt.z = (*it).y;
01054               tree_locs[num_trees].ray.pt.y = find_y_coord( (*it).x, (*it).y ) + tree_types[i].height;
01055               tree_locs[num_trees].ray.vec = pp::Vec3d( 0, 1, 0);
01056 
01057                      //tree_locs[num_trees].height = (double)rand()/RAND_MAX*tree_types[i].vary*2;
01058               //tree_locs[num_trees].height -= tree_types[i].vary;
01059               //tree_locs[num_trees].height = tree_types[i].height + 
01060                      //tree_locs[num_trees].height * tree_types[i].height;
01061               //tree_locs[num_trees].diam = (tree_locs[num_trees].height /
01062                      //tree_types[i].height) * tree_types[i].diam;
01063                      
01064                      tree_locs[num_trees].diam = tree_types[i].diam;
01065               tree_locs[num_trees].type = i;
01066                      tree_locs[num_trees].setModel(tree_types[i].model);
01067                      tree_locs[num_trees].setPolyhedron(tree_types[i].ph);
01068               
01069               num_trees++;
01070               }
01071               tree_types[i].pos.clear();
01072     }
01073 
01074     num_items = 0;
01075     for (i = 0; i < num_item_types; i++) {
01076               
01077               for(it=item_types[i].pos.begin();it!=item_types[i].pos.end();it++){
01078 
01079               item_locs[num_items].ray.pt.x = (*it).x;
01080               item_locs[num_items].ray.pt.z = (*it).y;
01081               item_locs[num_items].ray.pt.y = find_y_coord( (*it).x, (*it).y )
01082                                        + item_types[i].above_ground;
01083                      item_locs[num_items].ray.vec = pp::Vec3d(0, 1, 0);
01084 
01085               item_locs[num_items].height = item_types[i].height ; 
01086               item_locs[num_items].diam = item_types[i].diam;
01087               item_locs[num_items].type = i;
01088                      item_locs[num_items].setType(item_types[i].type);
01089                      item_locs[num_items].setScore(item_types[i].score);
01090 
01091               if ( item_types[i].reset_point )  {
01092                             item_locs[num_items].setDrawable(false);
01093               } else {
01094                             item_locs[num_items].setDrawable(true);
01095               }
01096               num_items++;
01097               }
01098               item_types[i].pos.clear();
01099     }
01100 
01101     delete treeImg;
01102 
01103     return TCL_OK;
01104 }
01105 
01106 static int friction_cb ( ClientData cd, Tcl_Interp *ip, 
01107                       int argc, CONST84 char *argv[]) 
01108 {
01109     double fric[4];
01110     float fric_s[4];
01111     unsigned int i;
01112 
01113     if ( argc != 5 ) {
01114         fprintf( stderr, "Usage: %s <ice> <rock> <snow> <ramp>", argv[0] );
01115         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
01116                       "Usage: ", argv[0], " <ice coeff.> <rock coeff.> "
01117                       "<snow coeff.> <ramp coeff.>",
01118                       (char *)0 );
01119         return TCL_ERROR;
01120     } 
01121 
01122     if ( Tcl_GetDouble( ip, argv[1], &fric[0] ) != TCL_OK ) {
01123         Tcl_AppendResult(ip, argv[0], ": invalid ice coefficient",
01124                       (char *)0 );
01125         return TCL_ERROR;
01126     } 
01127 
01128     if ( Tcl_GetDouble( ip, argv[2], &fric[1] ) != TCL_OK ) {
01129         Tcl_AppendResult(ip, argv[0], ": invalid rock coefficient",
01130                       (char *)0 );
01131         return TCL_ERROR;
01132     } 
01133 
01134     if ( Tcl_GetDouble( ip, argv[3], &fric[2] ) != TCL_OK ) {
01135         Tcl_AppendResult(ip, argv[0], ": invalid snow coefficient",
01136                       (char *)0 );
01137         return TCL_ERROR;
01138     }
01139         
01140        if ( Tcl_GetDouble( ip, argv[4], &fric[3] ) != TCL_OK ) {
01141         Tcl_AppendResult(ip, argv[0], ": invalid ramp coefficient",
01142                       (char *)0 );
01143         return TCL_ERROR;
01144     }
01145        
01146 
01147     for ( i=0; i<sizeof(fric)/sizeof(fric[0]); i++) {
01148        fric_s[i] = fric[i];
01149     }
01150 
01151     set_friction_coeff( fric_s );
01152 
01153     return TCL_OK;
01154 } 
01155 
01156 static int course_author_cb( ClientData cd, Tcl_Interp *ip, 
01157                           int argc, CONST84 char *argv[]) 
01158 {
01159     if ( argc != 2 ) {
01160         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
01161                       "Usage: ", argv[0], " <author's name>",
01162                       (char *)0 );
01163         return TCL_ERROR;
01164     } 
01165 
01166     courseAuthor = argv[1];
01167 
01168     return TCL_OK;
01169 } 
01170 
01171 static int course_name_cb( ClientData cd, Tcl_Interp *ip, 
01172                         int argc, CONST84 char *argv[]) 
01173 {
01174     if ( argc != 2 ) {
01175         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
01176                       "Usage: ", argv[0], " <course name>",
01177                       (char *)0 );
01178         return TCL_ERROR;
01179     } 
01180 
01181     courseName = argv[1];
01182 
01183     return TCL_OK;
01184 } 
01185 
01186 static int base_height_value_cb( ClientData cd, Tcl_Interp *ip, 
01187                              int argc, CONST84 char *argv[]) 
01188 {
01189     int value;
01190 
01191     if ( argc != 2 ) {
01192         Tcl_AppendResult(ip, argv[0], ": invalid number of arguments\n", 
01193                       "Usage: ", argv[0], " <base height>",
01194                       (char *)0 );
01195         return TCL_ERROR;
01196     } 
01197 
01198     if ( Tcl_GetInt( ip, argv[1], &value ) != TCL_OK ) {
01199         Tcl_AppendResult(ip, argv[0], ": invalid base height",
01200                       (char *)0 );
01201         return TCL_ERROR;
01202     } 
01203 
01204     base_height_value = value;
01205 
01206     return TCL_OK;
01207 }
01208 
01209 static int tree_model_cb( ClientData cd, Tcl_Interp *ip, 
01210                              int argc, CONST84 char *argv[])
01211 {
01212        if ( num_tree_types + 1 >= MAX_TREE_TYPES ) {
01213               Tcl_AppendResult(ip, argv[0], ": max number of tree types reached",
01214                       (char *)0 );
01215               return TCL_ERROR;
01216     }
01217        
01218     tree_types[num_tree_types].height = 1.0;
01219        tree_types[num_tree_types].diam = 1.0;
01220     tree_types[num_tree_types].num_trees = 0;
01221     tree_types[num_tree_types].red = 255;
01222     tree_types[num_tree_types].green = 255;
01223     tree_types[num_tree_types].blue = 255;
01224        tree_types[num_tree_types].model = NULL;
01225        
01226 
01227        int error = 0;
01228        
01229        for ( int i = 1; (i < argc - 1) && !error; i += 2 ) {
01230               if ( strcmp( "-name", argv[i] ) == 0 ) {
01231                   tree_types[num_tree_types].name = argv[i+1];
01232 
01233               } else if ( strcmp( "-height", argv[i] ) == 0 ) {
01234               if ( Tcl_GetDouble( ip, argv[i+1],
01235                             &tree_types[num_tree_types].height) != TCL_OK ) {
01236                      Tcl_AppendResult(ip, argv[0], ": invalid height",
01237                                    (char *)0 );
01238                      error = 1;
01239               }
01240               } else if ( strcmp( "-diam", argv[i] ) == 0 ) {
01241               if ( Tcl_GetDouble( ip, argv[i+1],
01242                             &tree_types[num_tree_types].diam) != TCL_OK ) {
01243                      Tcl_AppendResult(ip, argv[0], ": invalid diameter",
01244                                    (char *)0 );
01245                      error = 1;
01246               
01247               }
01248                      
01249               } else if ( strcmp( "-model", argv[i] ) == 0 ) {
01250                      tree_types[num_tree_types].model =
01251                             new pp::Model(argv[i+1]);
01252                      
01253               } else if ( strcmp( "-color", argv[i] ) == 0 ) {
01254                      int         rtn, num_col;
01255               CONST84 char **     indices = 0;
01256                      
01257               rtn = Tcl_SplitList(ip, argv[i+1], &num_col, &indices);
01258                      if( rtn != TCL_OK ) {
01259                             Tcl_AppendResult(ip, "a list of colors must be provided\n",
01260                                    (char *) 0);
01261                             Tcl_Free((char *) indices);
01262                             error = 1;
01263               }
01264     
01265                      int convert_temp;
01266 
01267               if (num_col == 3 || num_col == 4) {
01268                             Tcl_GetInt(ip, indices[0], &convert_temp);
01269                             tree_types[num_tree_types].red = (unsigned char) convert_temp;
01270                             Tcl_GetInt(ip, indices[1], &convert_temp);
01271                             tree_types[num_tree_types].green = (unsigned char) convert_temp;
01272                             Tcl_GetInt(ip, indices[2], &convert_temp);
01273                             tree_types[num_tree_types].blue = (unsigned char) convert_temp;
01274               } else {
01275                             Tcl_AppendResult(ip, argv[0], ": must specify three colors"
01276                             " to link with tree type", (char *) 0);
01277                             error = 1;
01278               }
01279               Tcl_Free((char *) indices);
01280               } 
01281        }
01282        
01283        tree_types[num_tree_types].ph = 
01284                      tree_types[num_tree_types].model->getPolyhedron();      
01285        
01286        num_tree_types += 1;
01287        return TCL_OK;
01288 }
01289 
01290 
01291 static int item_spec_cb( ClientData cd, Tcl_Interp *ip, 
01292                              int argc, CONST84 char *argv[])
01293 {
01294     int          rtn, num_col;
01295     CONST84 char **      indices = NULL;
01296     int          convert_temp;
01297     char *       err_msg = "";
01298     char         buff[BUFF_LEN];
01299 
01300     if ( num_item_types + 1 >= MAX_ITEM_TYPES ) {
01301        Tcl_AppendResult(ip, argv[0], ": max number of item types reached",
01302                       (char *)0 );
01303        return TCL_ERROR;
01304     }
01305 
01306     item_types[num_item_types].diam = .8;
01307     item_types[num_item_types].height = 0.5;
01308     item_types[num_item_types].above_ground = 0.0;
01309     item_types[num_item_types].red = 255;
01310     item_types[num_item_types].green = 255;
01311     item_types[num_item_types].blue = 255;
01312        item_types[num_item_types].type = Item::UNCOLLECTABLE;
01313     item_types[num_item_types].reset_point = false;
01314     item_types[num_item_types].num_items = 0;
01315     item_types[num_item_types].use_normal = false;
01316        item_types[num_item_types].score=1;
01317 
01318     NEXT_ARG;
01319 
01320     while ( *argv != NULL ) {
01321        if ( strcmp( "-name", *argv ) == 0 ) {
01322            NEXT_ARG;
01323            CHECK_ARG( "-name", err_msg, item_spec_bail );
01324 
01325            item_types[num_item_types].name = *argv;
01326 
01327        } else if ( strcmp( "-height", *argv ) == 0 ) {
01328            NEXT_ARG;
01329            CHECK_ARG( "-height", err_msg, item_spec_bail );
01330 
01331            if ( Tcl_GetDouble( ip, *argv,
01332                   &item_types[num_item_types].height) != TCL_OK ) {
01333               Tcl_AppendResult(ip, argv[0], ": invalid height\n",
01334                                    (char *) 0);
01335            }
01336 
01337        } else if ( strcmp( "-diameter", *argv ) == 0 ) {
01338            NEXT_ARG;
01339            CHECK_ARG( "-diameter", err_msg, item_spec_bail );
01340 
01341            if ( Tcl_GetDouble( ip, *argv,
01342                   &item_types[num_item_types].diam) != TCL_OK ) {
01343               Tcl_AppendResult(ip, argv[0], ": invalid diameter\n",
01344                                    (char *) 0);
01345            }
01346        
01347        } else if ( strcmp( "-texture", *argv ) == 0 ) {
01348            NEXT_ARG;
01349            CHECK_ARG( "-texture", err_msg, item_spec_bail );
01350 
01351            if ( item_types[num_item_types].texture.empty() ) {
01352                      item_types[num_item_types].texture = *argv;
01353            } else {
01354                      Tcl_AppendResult(ip, argv[0], ": specify only one texture\n",
01355                             (char *)0 );
01356            }
01357 
01358        } else if ( strcmp( "-above_ground", *argv ) == 0 ) {
01359            NEXT_ARG;
01360            CHECK_ARG( "-above_ground", err_msg, item_spec_bail );
01361 
01362            if ( Tcl_GetDouble( ip, *argv,
01363                   &item_types[num_item_types].above_ground) != TCL_OK ) {
01364               Tcl_AppendResult(ip, argv[0], ": invalid height above ground\n",
01365                                    (char *) 0);
01366            }
01367 
01368        } else if ( strcmp( "-score", *argv ) == 0 ) {
01369            NEXT_ARG;
01370            CHECK_ARG( "-score", err_msg, item_spec_bail );
01371 
01372            if ( Tcl_GetInt( ip, *argv,
01373                   &item_types[num_item_types].score) != TCL_OK ) {
01374               Tcl_AppendResult(ip, argv[0], ": invalid score\n",
01375                                    (char *) 0);
01376            }
01377 
01378        } else if ( strcmp( "-color", *argv ) == 0 ) 
01379        {
01380            NEXT_ARG;
01381            CHECK_ARG( "-color", err_msg, item_spec_bail );
01382 
01383            rtn = Tcl_SplitList(ip, *argv, &num_col, &indices);
01384            if( rtn != TCL_OK ) {
01385               err_msg = "Must provide a list of colors for -color";
01386               goto item_spec_bail; 
01387            }
01388 
01389            if (num_col == 3 || num_col == 4) {
01390               Tcl_GetInt(ip, indices[0], &convert_temp);
01391               item_types[num_item_types].red = (unsigned char) convert_temp;
01392               Tcl_GetInt(ip, indices[1], &convert_temp);
01393               item_types[num_item_types].green = (unsigned char) convert_temp;
01394               Tcl_GetInt(ip, indices[2], &convert_temp);
01395               item_types[num_item_types].blue = (unsigned char) convert_temp;
01396            } else {
01397               err_msg = "Color specification must have 3 or 4 elements";
01398               goto item_spec_bail;
01399            }
01400            Tcl_Free((char *) indices);
01401            indices = NULL;
01402 
01403        } else if ( strcmp( "-type", *argv ) == 0) {
01404               NEXT_ARG;
01405            CHECK_ARG( "-type", err_msg, item_spec_bail );
01406               
01407               if(!strcmp(*argv,"herring")){
01408                      item_types[num_item_types].type=Item::HERRING;
01409               }else if (!strcmp(*argv,"life")){
01410                      item_types[num_item_types].type=Item::LIFE;
01411               }      
01412        } else if ( strcmp( "-reset_point", *argv ) == 0 ) {
01413            item_types[num_item_types].reset_point = true;
01414            item_types[num_item_types].type = Item::UNCOLLECTABLE;
01415        } else if ( strcmp( "-normal", *argv ) == 0 ) {
01416            NEXT_ARG;
01417            CHECK_ARG( "-normal", err_msg, item_spec_bail );
01418 
01419            if ( get_tcl_tuple( 
01420               ip, *argv, (double*)&(item_types[num_item_types].normal), 3 )
01421                != TCL_OK )
01422            {
01423               err_msg = "Must specify a list of size three for -normal";
01424               goto item_spec_bail;
01425            }
01426 
01427            item_types[num_item_types].normal.normalize();
01428 
01429            item_types[num_item_types].use_normal = true;
01430 
01431        } else {
01432            sprintf( buff, "Unrecognized option `%s'", *argv );
01433            goto item_spec_bail;
01434        }
01435 
01436        NEXT_ARG;
01437     }
01438 
01439     if ( item_types[num_item_types].name.empty() ||
01440         ( item_types[num_item_types].texture.empty() &&
01441           item_types[num_item_types].reset_point == false ) ) 
01442     {
01443        err_msg = "Some mandatory elements not filled.  "
01444            "Item name and texture name must be supplied.";
01445        goto item_spec_bail;
01446     }
01447 
01448     if ( item_types[num_item_types].reset_point == false &&
01449         !bind_texture( item_types[num_item_types].name.c_str(),
01450                      item_types[num_item_types].texture.c_str() )) 
01451     {
01452        err_msg = "could not bind specified texture";
01453        goto item_spec_bail;
01454     }
01455 
01456     num_item_types += 1;
01457     return TCL_OK;
01458 
01459 item_spec_bail:
01460     if ( indices ) {
01461        Tcl_Free( (char*) indices );
01462        indices = NULL;
01463     }
01464 
01465        item_types[num_item_types].name.erase();
01466        item_types[num_item_types].texture.erase();
01467 
01468         
01469 
01470     Tcl_AppendResult(
01471        ip,
01472        "Error in call to tux_item_spec: ", 
01473        err_msg,
01474        "\n",
01475        "Usage: tux_item_spec -name <name> -height <height> "
01476        "-diameter <diameter> -color {r g b [a]} "
01477        "[-texture <texture>] [-above_ground <distance>] [-score <score>] "
01478        "[-nocollect] [-reset_point] [-normal {x y z}]",
01479        (NULL) );
01480     return TCL_ERROR;
01481 }
01482 
01483 static int wind_velocity_cb( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
01484 {
01485        pp::Vec3d velocity;
01486        double scale;
01487        char *err_msg = "";
01488        int rtn, num_col;
01489        CONST84 char ** indices = NULL;
01490        int convert_temp;
01491 
01492     NEXT_ARG;
01493 
01494     while ( *argv != NULL ){
01495               if ( strcmp( "-scale", *argv ) == 0 ) {
01496                   NEXT_ARG;
01497                   CHECK_ARG( "-scale", err_msg, item_spec_bail );            
01498 
01499                   if ( Tcl_GetDouble( ip, *argv,
01500                          &scale) != TCL_OK ) {
01501                      Tcl_AppendResult(ip, argv[0], ": invalid scale\n",
01502                                           (char *) 0);
01503                   }
01504               }else if ( strcmp( "-velocity", *argv ) == 0 ||
01505                   strcmp( "-velocity", *argv ) == 0 ){
01506                   NEXT_ARG;
01507                   CHECK_ARG( "-velocity", err_msg, item_spec_bail );
01508        
01509                   rtn = Tcl_SplitList(ip, *argv, &num_col, &indices);
01510               if( rtn != TCL_OK ) {
01511                             err_msg = "Must provide a list of velocities for -velocity";
01512                             goto item_spec_bail; 
01513                      }
01514 
01515                      if (num_col == 3) {
01516                             Tcl_GetInt(ip, indices[0], &convert_temp);
01517                             velocity.x = (unsigned char) convert_temp;
01518                             Tcl_GetInt(ip, indices[1], &convert_temp);
01519                             velocity.y = (unsigned char) convert_temp;
01520                             Tcl_GetInt(ip, indices[2], &convert_temp);
01521                             velocity.z = (unsigned char) convert_temp;
01522                   } else {
01523                             err_msg = "Velocity specification must have 3 elements";
01524                             goto item_spec_bail;
01525               }
01526                      Tcl_Free((char *) indices);
01527               indices = NULL;
01528               }
01529 
01530               NEXT_ARG;
01531     }  
01532        
01533        set_wind_velocity(velocity,scale);
01534     return TCL_OK;
01535 
01536 item_spec_bail:
01537     if ( indices ){
01538        Tcl_Free( (char*) indices );
01539        indices = NULL;
01540     }
01541 
01542     Tcl_AppendResult(
01543               ip,
01544               "Error in call to tux_wind_velocity: ", 
01545               err_msg,
01546               "\n",
01547               "Usage: tux-wind_velocity -velocity {x y z} -scale <scale>",
01548               (NULL) );
01549     return TCL_ERROR;
01550 }
01551 
01552 
01553 static int hud_cb( ClientData cd, Tcl_Interp *ip, int argc, CONST84 char *argv[]) 
01554 {
01555        char *err_msg = "";
01556        int rtn, num_col;
01557        CONST84 char ** indices = NULL;
01558        int convert_temp;
01559        
01560        int hud=1, element_num=-1;
01561        
01562        HUD::Element element;
01563        
01564     NEXT_ARG;
01565 
01566     while ( *argv != NULL ){
01567               if ( strcmp( "-hud", *argv ) == 0 ) {
01568                   NEXT_ARG;
01569                   CHECK_ARG( "-hud", err_msg, item_spec_bail );              
01570 
01571                   if ( Tcl_GetInt( ip, *argv,
01572                          &hud) != TCL_OK ) {
01573                      Tcl_AppendResult(ip, argv[0], ": invalid HUD\n",
01574                                           (char *) 0);
01575                   }
01576               }else if ( strcmp( "-element", *argv ) == 0 ) {
01577                   NEXT_ARG;
01578                   CHECK_ARG( "-element", err_msg, item_spec_bail );          
01579 
01580                   if ( Tcl_GetInt( ip, *argv,
01581                          &element_num) != TCL_OK ) {
01582                      Tcl_AppendResult(ip, argv[0], ": invalid HUD element number\n",
01583                                           (char *) 0);
01584                   }
01585               }else if ( strcmp( "-type", *argv ) == 0 ) {
01586                      NEXT_ARG;
01587                      CHECK_ARG( "-typr", err_msg, item_spec_bail );
01588                      if(!strcmp("text",*argv)){
01589                             element.type=0;
01590                      }else if(!strcmp("fsb",*argv)){
01591                             element.type=1;
01592                      }else if(!strcmp("herring",*argv)){
01593                             element.type=2;
01594                      }else if(!strcmp("image",*argv)){
01595                             element.type=3;
01596                      }else if(!strcmp("time",*argv)){
01597                             element.type=4;
01598                      }else if(!strcmp("speed",*argv)){
01599                             element.type=5;      
01600                      }else if(!strcmp("gauge",*argv)){
01601                             element.type=6;      
01602                      }else if(!strcmp("energybar",*argv)){
01603                             element.type=7;      
01604                      }else if(!strcmp("speedbar",*argv)){
01605                             element.type=8;      
01606                      }else if(!strcmp("percentage",*argv)){
01607                             element.type=9;      
01608                      }else if(!strcmp("percentagebar",*argv)){
01609                             element.type=10;     
01610             }else if(!strcmp("objectives_time",*argv)){
01611                 element.type=11;
01612                      }else if(!strcmp("objectives_herring",*argv)){
01613                 element.type=12;
01614             }else if(!strcmp("objectives_time_outline",*argv)){
01615                 element.type=13;
01616                      }else if(!strcmp("objectives_herring_outline",*argv)){
01617                 element.type=14;
01618                      }else if(!strcmp("icon_time",*argv)){
01619                                 element.type=15;
01620                      }else if(!strcmp("icon_herring",*argv)){
01621                                 element.type=16;
01622 
01623                      }else{
01624                             err_msg = "invalid type";
01625                             goto item_spec_bail;
01626                      }                                  
01627               }else if ( strcmp( "-position", *argv ) == 0 ||
01628                   strcmp( "-position", *argv ) == 0 ){
01629                   NEXT_ARG;
01630                   CHECK_ARG( "-position", err_msg, item_spec_bail );
01631        
01632                   rtn = Tcl_SplitList(ip, *argv, &num_col, &indices);
01633               if( rtn != TCL_OK ) {
01634                             err_msg = "Must provide a list with x and y for -position";
01635                             goto item_spec_bail; 
01636                      }
01637                      if (num_col == 2) {
01638                             Tcl_GetInt(ip, indices[0], &convert_temp);
01639                             element.x = convert_temp;
01640                             Tcl_GetInt(ip, indices[1], &convert_temp);
01641                             element.y = convert_temp;
01642                   } else {
01643                             err_msg = "Position must have 2 elements";
01644                             goto item_spec_bail;
01645               }
01646                      Tcl_Free((char *) indices);
01647               indices = NULL;
01648               } else if ( strcmp( "-texture", *argv ) == 0 ) {
01649                   NEXT_ARG;
01650                      CHECK_ARG( "-texture", err_msg, item_spec_bail );
01651                      
01652                      if ( !get_texture_binding(*argv, &element.texture ) ) {
01653                             err_msg = "Couldn't get texture";
01654                             goto item_spec_bail;
01655                      }
01656               } else if ( strcmp( "-texture2", *argv ) == 0 ) {
01657                   NEXT_ARG;
01658                      CHECK_ARG( "-texture2", err_msg, item_spec_bail );
01659                      
01660                      if ( !get_texture_binding(*argv, &element.texture2 ) ) {
01661                             err_msg = "Couldn't get texture2";
01662                             goto item_spec_bail;
01663                      }
01664               } else if ( strcmp( "-font", *argv ) == 0 ) {
01665                   NEXT_ARG;
01666                      CHECK_ARG( "-font", err_msg, item_spec_bail );
01667        
01668                      element.font = pp::Font::get(*argv);
01669                      if( element.font==NULL){
01670                             err_msg = "Couldn't get font";
01671                             goto item_spec_bail;
01672                      }else{
01673                             element.height = int(element.font->ascender());                
01674                      }                    
01675               } else if ( strcmp( "-string", *argv ) == 0 ) {
01676                      NEXT_ARG;
01677                      CHECK_ARG( "-string", err_msg, item_spec_bail );
01678                      if ( element.string.empty() ) {
01679                             element.string = _(*argv);
01680               } else {
01681                             err_msg = "Specify only one string";
01682                             goto item_spec_bail;
01683               }
01684               } else if ( strcmp( "-angle", *argv ) == 0 ) {
01685                   NEXT_ARG;
01686                   CHECK_ARG( "-angle", err_msg, item_spec_bail );            
01687 
01688                   if ( Tcl_GetInt( ip, *argv,
01689                          &element.angle) != TCL_OK ) {
01690                      Tcl_AppendResult(ip, argv[0], ": invalid angle\n",
01691                                           (char *) 0);
01692                   }
01693               } else if ( strcmp( "-width", *argv ) == 0 ) {
01694                   NEXT_ARG;
01695                   CHECK_ARG( "-width", err_msg, item_spec_bail );            
01696 
01697                   if ( Tcl_GetInt( ip, *argv,
01698                          &element.width) != TCL_OK ) {
01699                      Tcl_AppendResult(ip, argv[0], ": invalid width\n",
01700                                           (char *) 0);
01701                   }
01702               } else if ( strcmp( "-height", *argv ) == 0 ) {
01703                   NEXT_ARG;
01704                   CHECK_ARG( "-height", err_msg, item_spec_bail );           
01705 
01706                   if ( Tcl_GetInt( ip, *argv,
01707                          &element.height) != TCL_OK ) {
01708                      Tcl_AppendResult(ip, argv[0], ": invalid height\n",
01709                                           (char *) 0);
01710                   }
01711               } else if ( strcmp( "-size", *argv ) == 0 ) {
01712                   NEXT_ARG;
01713                   CHECK_ARG( "-size", err_msg, item_spec_bail );             
01714 
01715                   if ( Tcl_GetInt( ip, *argv,
01716                          &element.size) != TCL_OK ) {
01717                      Tcl_AppendResult(ip, argv[0], ": invalid size\n",
01718                                           (char *) 0);
01719                   }
01720               }
01721               
01722               NEXT_ARG;
01723     }
01724        
01725        
01726        //in case of a text element we're precalculating
01727        //the width and the unicode string
01728        if(element.type==0 && element.font && !element.string.empty()){
01729               pp::Font::utf8ToUnicode(element.u_string,element.string.c_str());
01730               element.width = int(element.font->advance(element.u_string));
01731        }
01732        
01733        if(hud==2){
01734               //no multiplayer support
01735        }else{
01736               if (element_num==-1){
01737                      HUD1.add(element);
01738               }else{
01739                      HUD1.update(element_num,element);
01740               }
01741        }
01742        
01743     return TCL_OK;
01744 
01745 item_spec_bail:
01746     if ( indices ){
01747        Tcl_Free( (char*) indices );
01748        indices = NULL;
01749     }
01750 
01751     Tcl_AppendResult(
01752               ip,
01753               "Error in call to tux_hud: ", 
01754               err_msg,
01755               "\n",
01756               "Usage: tux_hud -hud <hud> -element <element number> -type <type> -position {x y} -binding <binding> -string <string> -width <width> -height <height> -size <size> -angle <angle>",
01757               "\n",
01758               "Valid types: text, fsb, herring, image, time, speed, gauge, energybar and speedbar",
01759               (NULL) );
01760     return TCL_ERROR;
01761 }
01762 
01763 
01764 void register_course_load_tcl_callbacks( Tcl_Interp *ip )
01765 {
01766     Tcl_CreateCommand (ip, "tux_course_dim", course_dim_cb,  0,0);
01767     Tcl_CreateCommand (ip, "tux_angle",      angle_cb,  0,0);
01768     Tcl_CreateCommand (ip, "tux_elev_scale", elev_scale_cb,   0,0);
01769     Tcl_CreateCommand (ip, "tux_elev",       elev_cb,        0,0);
01770     Tcl_CreateCommand (ip, "tux_load_terrain",    terrain_cb,   0,0);
01771     Tcl_CreateCommand (ip, "tux_trees",      trees_cb,   0,0);
01772     Tcl_CreateCommand (ip, "tux_bgnd_img",   bgnd_img_cb,   0,0);
01773        Tcl_CreateCommand (ip, "tux_terrain_tex",   terrain_tex_cb,   0,0);
01774     Tcl_CreateCommand (ip, "tux_snow_type",   snow_type_cb,   0,0);
01775     Tcl_CreateCommand (ip, "tux_wind_type",   wind_type_cb,   0,0);
01776     Tcl_CreateCommand (ip, "tux_start_pt",   start_pt_cb,   0,0);
01777     Tcl_CreateCommand (ip, "tux_friction",   friction_cb,   0,0);
01778     Tcl_CreateCommand (ip, "tux_course_author", course_author_cb, 0,0);
01779     Tcl_CreateCommand (ip, "tux_course_name", course_name_cb, 0,0);
01780     Tcl_CreateCommand (ip, "tux_base_height_value", base_height_value_cb, 0,0);
01781     Tcl_CreateCommand (ip, "tux_tree_model",  tree_model_cb,   0,0);
01782     Tcl_CreateCommand (ip, "tux_item_spec",  item_spec_cb,   0,0);
01783        Tcl_CreateCommand (ip, "tux_wind_velocity",  wind_velocity_cb,   0,0);
01784        Tcl_CreateCommand (ip, "tux_hud",  hud_cb,   0,0);
01785 }