Back to index

supertuxkart  0.5+dfsg1
track.cpp
Go to the documentation of this file.
00001 //  $Id: track.cpp 2111 2008-05-31 07:04:30Z cosmosninja $
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  Copyright (C) 2004 Steve Baker <sjbaker1@airmail.net>
00005 //
00006 //  This program is free software; you can redistribute it and/or
00007 //  modify it under the terms of the GNU General Public License
00008 //  as published by the Free Software Foundation; either version 2
00009 //  of the License, or (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 
00020 #include <iostream>
00021 #include <stdexcept>
00022 #include <sstream>
00023 #include <plib/ssgAux.h>
00024 #include "file_manager.hpp"
00025 #include "loader.hpp"
00026 #include "track.hpp"
00027 #include "string_utils.hpp"
00028 #include "lisp/lisp.hpp"
00029 #include "lisp/parser.hpp"
00030 #include "stk_config.hpp"
00031 #include "translation.hpp"
00032 #include "scene.hpp"
00033 #include "moving_physics.hpp"
00034 #include "world.hpp"
00035 #include "material_manager.hpp"
00036 #include "isect.hpp"
00037 #include "ssg_help.hpp"
00038 #include "user_config.hpp"
00039 #include "herring.hpp"
00040 #include "herring_manager.hpp"
00041 #include "sound_manager.hpp"
00042 #include "race_manager.hpp"
00043 
00044 #if defined(WIN32) && !defined(__CYGWIN__)
00045 #  define snprintf _snprintf
00046 #endif
00047 
00048 const float Track::NOHIT           = -99999.9f;
00049 const int   Track::QUAD_TRI_NONE   = -1;
00050 const int   Track::QUAD_TRI_FIRST  =  1;
00051 const int   Track::QUAD_TRI_SECOND =  2;
00052 const int   Track::UNKNOWN_SECTOR  = -1;
00053 
00054 // ----------------------------------------------------------------------------
00055 Track::Track( std::string filename_, float w, float h, bool stretch )
00056 {
00057     m_filename        = filename_;
00058     m_herring_style   = "";
00059     m_track_2d_width  = w;
00060     m_track_2d_height = h;
00061     m_do_stretch      = stretch;
00062     m_description     = "";
00063     m_designer        = "";
00064     m_screenshot      = "";
00065     m_top_view        = "";
00066 
00067     loadTrack(m_filename);
00068     loadDriveline();
00069 
00070 }   // Track
00071 
00072 //-----------------------------------------------------------------------------
00073 Track::~Track()
00074 {
00075 }   // ~Track
00076 //-----------------------------------------------------------------------------
00080 void Track::cleanup()
00081 {
00082     delete m_non_collision_mesh;
00083     delete m_track_mesh;
00084 
00085     // remove temporary materials loaded by the material manager
00086     material_manager->popTempMaterial();
00087 }   // cleanup
00088 
00089 //-----------------------------------------------------------------------------
00092 inline float Track::pointSideToLine( const sgVec2 L1, const sgVec2 L2,
00093     const sgVec2 P ) const
00094 {
00095     return ( L2[0]-L1[0] )*( P[1]-L1[1] )-( L2[1]-L1[1] )*( P[0]-L1[0] );
00096 }   // pointSideToLine
00097 
00098 //-----------------------------------------------------------------------------
00107 int Track::pointInQuad
00108 (
00109     const sgVec2 A,
00110     const sgVec2 B,
00111     const sgVec2 C,
00112     const sgVec2 D,
00113     const sgVec2 POINT
00114 ) const
00115 {
00116     if(pointSideToLine( C, A, POINT ) >= 0.0 )
00117     {
00118         //Test the first triangle
00119         if( pointSideToLine( A, B, POINT ) >  0.0 &&
00120             pointSideToLine( B, C, POINT ) >= 0.0    )
00121             return QUAD_TRI_FIRST;
00122         return QUAD_TRI_NONE;
00123     }
00124 
00125     //Test the second triangle
00126     if( pointSideToLine( C, D, POINT ) > 0.0 &&
00127         pointSideToLine( D, A, POINT ) > 0.0     )
00128         return QUAD_TRI_SECOND;
00129 
00130     return QUAD_TRI_NONE;
00131 }   // pointInQuad
00132 
00133 //-----------------------------------------------------------------------------
00141 void Track::findRoadSector( const sgVec3 XYZ, int *sector )const
00142 {
00143     if(*sector!=UNKNOWN_SECTOR)
00144     {
00145         int next = (unsigned)(*sector) + 1 <  m_left_driveline.size() ? *sector + 1 : 0;
00146         if(pointInQuad( m_left_driveline[*sector], m_right_driveline[*sector],
00147                         m_right_driveline[next],   m_left_driveline[next], 
00148                         XYZ ) != QUAD_TRI_NONE)
00149             // Still in the same sector, no changes
00150             return;
00151     }
00152     /* To find in which 'sector' of the track the kart is, we use a
00153        'point in triangle' algorithm for each triangle in the quad
00154        that forms each track segment.
00155      */
00156     std::vector <SegmentTriangle> possible_segment_tris;
00157     const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
00158     int triangle;
00159     int next;
00160 
00161     for( size_t i = 0; i < DRIVELINE_SIZE ; ++i )
00162     {
00163         next = (unsigned int)i + 1 <  DRIVELINE_SIZE ? (int)i + 1 : 0;
00164         triangle = pointInQuad( m_left_driveline[i],     m_right_driveline[i],
00165                                 m_right_driveline[next], m_left_driveline[next], 
00166                                 XYZ );
00167 
00168         if (triangle != QUAD_TRI_NONE && ((XYZ[2]-m_left_driveline[i][2]) < 1.0f))
00169         {
00170             possible_segment_tris.push_back(SegmentTriangle((int)i, triangle));
00171         }
00172     }
00173 
00174     /* Since xyz can be on more than one 2D track segment, we have to
00175        find on top of which one of the possible track segments it is.
00176      */
00177     const int POS_SEG_SIZE = (int)possible_segment_tris.size();
00178     if( POS_SEG_SIZE == 0 )
00179     {
00180         //xyz is not on the road
00181         *sector = UNKNOWN_SECTOR;
00182         return;
00183     }
00184 
00185     //POS_SEG_SIZE > 1
00186     /* To find on top of which track segment the variable xyz is,
00187        we get which of the possible triangles that are under xyz
00188        has the lower distance on the height(Y or Z) axis.
00189     */
00190     float dist;
00191     float near_dist = 99999;
00192     int nearest = QUAD_TRI_NONE;
00193     size_t segment;
00194     sgVec4 plane;
00195     
00196     for( int i = 0; i < POS_SEG_SIZE; ++i )
00197     {
00198         segment = possible_segment_tris[i].segment;
00199         next = segment + 1 < DRIVELINE_SIZE ? (int)segment + 1 : 0;
00200         
00201         if( possible_segment_tris[i].triangle == QUAD_TRI_FIRST )
00202         {
00203             sgMakePlane( plane, m_left_driveline[segment],
00204                          m_right_driveline[segment], m_right_driveline[next] );
00205         }
00206         else //possible_segment_tris[i].triangle == QUAD_TRI_SECOND
00207         {
00208             sgMakePlane( plane, m_right_driveline[next],
00209                          m_left_driveline[next], m_left_driveline[segment] );
00210         }
00211         
00212         dist = sgHeightAbovePlaneVec3( plane, XYZ );
00213         
00214         /* sgHeightAbovePlaneVec3 gives a negative dist if the plane
00215            is on top, so we have to rule it out.
00216            
00217            However, for some reason there are cases where we get
00218            negative values for the track segment we should be on.
00219         */
00220         if( dist > -2.0 && dist < near_dist)
00221         {
00222             near_dist = dist;
00223             nearest = i;
00224         }
00225     }
00226     
00227     if( nearest != QUAD_TRI_NONE )
00228     {
00229         *sector=possible_segment_tris[nearest].segment;
00230         return;
00231     }
00232     *sector = UNKNOWN_SECTOR;
00233     return;                         // This only happens if the position is
00234                                     // under all the possible sectors
00235 }   // findRoadSector
00236 //-----------------------------------------------------------------------------
00264 int Track::findOutOfRoadSector
00265 (
00266     const sgVec3 XYZ,
00267     const RoadSide SIDE,
00268     const int CURR_SECTOR
00269 ) const
00270 {
00271     int sector = UNKNOWN_SECTOR;
00272     float dist;
00273     //FIXME: it can happen that dist is bigger than nearest_dist for all the
00274     //the points we check (currently a limit of +/- 10), and if so, the
00275     //function will return UNKNOWN_SECTOR, and if the AI get this, it will
00276     //trigger an assertion. I increased the nearest_dist default value from
00277     //99999 to 9999999, which is a lot more than the situation that caused
00278     //the discovery of this problem, but the best way to solve this, is to
00279     //find a better way of handling the shortcuts, and maybe a better way of
00280     //calculating the distance.
00281     float nearest_dist = 9999999;
00282     const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
00283 
00284     int begin_sector = 0;
00285     int end_sector = DRIVELINE_SIZE - 1;
00286 
00287     if(CURR_SECTOR != UNKNOWN_SECTOR )
00288     {
00289         const int LIMIT = 10; //The limit prevents shortcuts
00290         if( CURR_SECTOR - LIMIT < 0 )
00291         {
00292             begin_sector = DRIVELINE_SIZE - 1 + CURR_SECTOR - LIMIT;
00293         }
00294         else begin_sector = CURR_SECTOR - LIMIT;
00295 
00296         if( CURR_SECTOR + LIMIT > (int)DRIVELINE_SIZE - 1 )
00297         {
00298             end_sector = CURR_SECTOR + LIMIT - DRIVELINE_SIZE;
00299         }
00300         else end_sector = CURR_SECTOR + LIMIT;
00301     }
00302 
00303     sgLineSegment3 line_seg;
00304     int next_sector;
00305     for (int i = begin_sector ; i != end_sector ; i = next_sector )
00306     {
00307         next_sector = i + 1 == (int)DRIVELINE_SIZE ? 0 : i + 1;
00308 
00309         if( SIDE != RS_RIGHT)
00310         {
00311             sgCopyVec3( line_seg.a, m_left_driveline[i] );
00312             sgCopyVec3( line_seg.b, m_left_driveline[next_sector] );
00313 
00314             dist = sgDistSquaredToLineSegmentVec3( line_seg, XYZ );
00315 
00316             if ( dist < nearest_dist )
00317             {
00318                 nearest_dist = dist;
00319                 sector = i ;
00320             }
00321         }
00322 
00323         if( SIDE != RS_LEFT )
00324         {
00325             sgCopyVec3( line_seg.a, m_right_driveline[i] );
00326             sgCopyVec3( line_seg.b, m_right_driveline[next_sector] );
00327 
00328             dist = sgDistSquaredToLineSegmentVec3( line_seg, XYZ );
00329 
00330             if ( dist < nearest_dist )
00331             {
00332                 nearest_dist = dist;
00333                 sector = i ;
00334             }
00335         }
00336     }   // for i
00337 
00338     if(sector==UNKNOWN_SECTOR)
00339     {
00340         printf("unknown sector found.\n");
00341     }
00342     return sector;
00343 }   // findOutOfRoadSector
00344 
00345 //-----------------------------------------------------------------------------
00355 int Track::spatialToTrack
00356 (
00357     sgVec3 dst,
00358     const sgVec2 POS,
00359     const int SECTOR
00360 ) const
00361 {
00362     if( SECTOR == UNKNOWN_SECTOR )
00363     {
00364         std::cerr << "WARNING: UNKNOWN_SECTOR in spatialToTrack().\n";
00365         return -1;
00366     }
00367 
00368     const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
00369     const size_t PREV = SECTOR == 0 ? DRIVELINE_SIZE - 1 : SECTOR - 1;
00370     const size_t NEXT = (size_t)SECTOR+1 >= DRIVELINE_SIZE ? 0 : SECTOR + 1;
00371 
00372     const float DIST_PREV = sgDistanceVec2 ( m_driveline[PREV], POS );
00373     const float DIST_NEXT = sgDistanceVec2 ( m_driveline[NEXT], POS );
00374 
00375     size_t p1, p2;
00376     if ( DIST_NEXT < DIST_PREV )
00377     {
00378         p1 = SECTOR; p2 = NEXT;
00379     }
00380     else
00381     {
00382         p1 = PREV; p2 = SECTOR;
00383     }
00384 
00385     sgVec3 line_eqn;
00386     sgVec2 tmp;
00387 
00388     sgMake2DLine ( line_eqn, m_driveline[p1], m_driveline[p2] );
00389 
00390     dst[0] = sgDistToLineVec2 ( line_eqn, POS );
00391 
00392     sgAddScaledVec2 ( tmp, POS, line_eqn, -dst [0] );
00393 
00394     float dist_from_driveline_p1 = sgDistanceVec2 ( tmp, m_driveline[p1] );
00395     dst[1] = dist_from_driveline_p1 + m_distance_from_start[p1];
00396     // Set z-axis to half the width (linear interpolation between the
00397     // width at p1 and p2) - m_path_width is actually already half the width
00398     // of the track. This is used to determine if a kart is too far
00399     // away from the road and is therefore considered taking a shortcut.
00400 
00401     float fraction = dist_from_driveline_p1
00402                    / (m_distance_from_start[p2]-m_distance_from_start[p1]);
00403     dst[2] = m_path_width[p1]*(1-fraction)+fraction*m_path_width[p2];
00404 
00405     return (int)p1;
00406 }   // spatialToTrack
00407 
00408 //-----------------------------------------------------------------------------
00409 void Track::trackToSpatial ( sgVec3 xyz, const int SECTOR ) const
00410 {
00411     sgCopyVec3 ( xyz, m_driveline [ SECTOR ] ) ;
00412 }   // trackToSpatial
00413 
00414 //-----------------------------------------------------------------------------
00418 void Track::getStartCoords(unsigned int pos, sgCoord* coords) const {
00419   // Bug fix/workaround: sometimes the first kart would be too close
00420   // to the first driveline point and not to the last one -->
00421   // This kart would not get any lap counting done in the first
00422   // lap! Therefor -1.5 is subtracted from the y position - which
00423   // is a somewhat arbitrary value.
00424   coords->xyz[0] = pos<m_start_x.size() ? m_start_x[pos] : ((pos%2==0)?1.5f:-1.5f);
00425   coords->xyz[1] = pos<m_start_y.size() ? m_start_y[pos] : -1.5f*pos-1.5f;
00426    // height must be larger than the actual hight for which hot is computed.
00427   coords->xyz[2] = pos<m_start_z.size() ? m_start_z[pos] : 1.0f;
00428 
00429   coords->hpr[0] = pos<m_start_heading.size() ? m_start_heading[pos] : 0.0f;
00430   coords->hpr[1] = 0.0f;
00431   coords->hpr[2] = 0.0f;
00432 
00433   btVector3 tmp_pos(coords->xyz[0],coords->xyz[1],coords->xyz[2]);
00434 
00435   btVector3 normal;
00436   const Material *material=NULL;
00437   getTerrainInfo(tmp_pos, &(coords->xyz[2]), &normal, &material);
00438 
00439 }   // getStartCoords
00440 
00441 //-----------------------------------------------------------------------------
00446 bool Track::isShortcut(const int OLDSEC, const int NEWSEC) const
00447 {
00448     // If the kart was off the road, don't do any shortcuts
00449     if(OLDSEC==UNKNOWN_SECTOR || NEWSEC==UNKNOWN_SECTOR) return false;
00450     unsigned int distance_sectors = abs(OLDSEC-NEWSEC);
00451     // Handle 'wrap around': if the distance is more than half the 
00452     // number of driveline poins, assume it's a 'wrap around'
00453     if(2*distance_sectors > (unsigned int)m_driveline.size())
00454         distance_sectors = (unsigned int)m_driveline.size() - distance_sectors;
00455     return (distance_sectors>stk_config->m_shortcut_segments);
00456 }   // isShortcut
00457 
00458 //-----------------------------------------------------------------------------
00459 void Track::addDebugToScene(int type) const
00460 {
00461     if(type&1)
00462     {
00463         ssgaSphere *sphere;
00464         sgVec3 center;
00465         sgVec4 colour;
00466         for(unsigned int i = 0; i < m_driveline.size(); ++i)
00467         {
00468             sphere = new ssgaSphere;
00469             sgCopyVec3(center, m_driveline[i]);
00470             sphere->setCenter(center);
00471             sphere->setSize(getWidth()[i] / 4.0f);
00472         
00473             if(i == 0)
00474             {
00475                 colour[0] = colour[2] = colour[3] = 255;
00476                 colour[1] = 0;
00477             }
00478             else
00479             {
00480                 colour[0] = colour[1] = colour[3] = 255;
00481                 colour[2] = 0;
00482             }
00483             sphere->setColour(colour);
00484             scene->add(sphere);
00485         }   // for i
00486     }  
00487     if(type&2)
00488     {
00489         ssgVertexArray* v_array = new ssgVertexArray();
00490         ssgColourArray* c_array = new ssgColourArray();
00491         for(unsigned int i = 0; i < m_driveline.size(); i++)
00492         {
00493             int ip1 = i==m_driveline.size()-1 ? 0 : i+1;
00494             // The segment display must be slightly higher than the
00495             // track, otherwise it's not clearly visible.
00496             sgVec3 v;
00497             sgCopyVec3(v,m_left_driveline [i  ]); v[2]+=0.1f; v_array->add(v);
00498             sgCopyVec3(v,m_right_driveline[i  ]); v[2]+=0.1f; v_array->add(v);
00499             sgCopyVec3(v,m_right_driveline[ip1]); v[2]+=0.1f; v_array->add(v);
00500             sgCopyVec3(v,m_left_driveline [ip1]); v[2]+=0.1f; v_array->add(v);
00501             sgVec4 vc;
00502             vc[0] = i%2==0 ? 1.0f : 0.0f;
00503             vc[1] = 1.0f-v[0];
00504             vc[2] = 0.0f;
00505             vc[3] = 0.1f;
00506             c_array->add(vc);c_array->add(vc);c_array->add(vc);c_array->add(vc);
00507         }   // for i
00508         // if GL_QUAD_STRIP is used, the colours are smoothed, so the changes
00509         // from one segment to the next are not visible.
00510         ssgVtxTable* l = new ssgVtxTable(GL_QUADS, v_array,
00511                                          (ssgNormalArray*)NULL,
00512                                          (ssgTexCoordArray*)NULL,
00513                                          c_array);
00514         scene->add(l);
00515     }
00516 }   // addDebugToScene
00517 
00518 //-----------------------------------------------------------------------------
00528 void Track::drawScaled2D(float x, float y, float w, float h) const
00529 {
00530     sgVec2 sc;
00531     sgSubVec2 ( sc, m_driveline_max, m_driveline_min );
00532 
00533     float sx = w / sc[0];
00534     float sy = h / sc[1];
00535 
00536     if( sx > sy )
00537     {
00538         sx = sy;
00539         x += w/2 - sc[0]*sx/2;
00540     }
00541     else
00542     {
00543         sy = sx;
00544     }
00545 
00546     const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
00547 
00548     glPushAttrib ( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT );
00549 
00550     glEnable(GL_BLEND);
00551     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00552     glDisable (GL_TEXTURE_2D);
00553 
00554     glColor4f ( 1, 1, 1, 0.5) ;
00555 
00556     glBegin ( GL_QUAD_STRIP ) ;
00557 
00558     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00559     {
00560       glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
00561           y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy) ;
00562 
00563       glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
00564           y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00565     }
00566     glVertex2f ( x + ( m_left_driveline[0][0] - m_driveline_min[0] ) * sx,
00567         y + ( m_left_driveline[0][1] - m_driveline_min[1] ) * sy) ;
00568     glVertex2f ( x + ( m_right_driveline[0][0] - m_driveline_min[0] ) * sx,
00569         y + ( m_right_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
00570 
00571     glEnd () ;
00572 
00573     glEnable( GL_LINE_SMOOTH );
00574     glEnable( GL_POINT_SMOOTH );
00575     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
00576     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
00577 
00578     glLineWidth(1);
00579     glPointSize(1);
00580 
00581     glColor4f ( 0, 0, 0, 1 ) ;
00582 
00583     glBegin ( GL_LINES ) ;
00584     for ( size_t i = 0 ; i < DRIVELINE_SIZE - 1 ; ++i )
00585     {
00586         /*Draw left driveline of the map*/
00587         glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
00588             y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00589 
00590         glVertex2f ( x + ( m_left_driveline[i+1][0] - m_driveline_min[0] ) * sx,
00591             y + ( m_left_driveline[i+1][1] - m_driveline_min[1] ) * sy ) ;
00592 
00593 
00594         /*Draw left driveline of the map*/
00595         glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
00596                y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00597 
00598         glVertex2f ( x + ( m_right_driveline[i+1][0] - m_driveline_min[0] ) * sx,
00599                y + ( m_right_driveline[i+1][1] - m_driveline_min[1] ) * sy ) ;
00600     }
00601 
00602     //Close the left driveline
00603     glVertex2f ( x + ( m_left_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * sx,
00604         y + ( m_left_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * sy ) ;
00605 
00606     glVertex2f ( x + ( m_left_driveline[0][0] - m_driveline_min[0] ) * sx,
00607         y + ( m_left_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
00608 
00609 
00610     //Close the right driveline
00611     glVertex2f ( x + ( m_right_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * sx,
00612         y + ( m_right_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * sy ) ;
00613 
00614     glVertex2f ( x + ( m_right_driveline[0][0] - m_driveline_min[0] ) * sx,
00615         y + ( m_right_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
00616     glEnd () ;
00617 
00618 #if 0
00619   //FIXME: We are not sure if it's a videocard problem, but on Linux with a
00620   //Nvidia Geforce4 mx 440, we get problems with GL_LINE_LOOP;
00621   //If this issue is solved, using GL_LINE_LOOP is a better solution than
00622   //GL_LINES
00623     glBegin ( GL_LINE_LOOP ) ;
00624     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00625     {
00626         glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
00627             y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00628     }
00629     glEnd () ;
00630 
00631     glBegin ( GL_LINE_LOOP ) ;
00632     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00633     {
00634         glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
00635                y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00636     }
00637     glEnd () ;
00638 #endif
00639 
00640 
00641     glBegin ( GL_POINTS ) ;
00642     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00643     {
00644       glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
00645           y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00646 
00647       glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
00648           y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
00649     }
00650     glEnd () ;
00651 
00652     glPopAttrib();
00653 
00654 }   // drawScaled2D
00655 
00656 //-----------------------------------------------------------------------------
00657 void Track::draw2Dview (float x_offset, float y_offset) const
00658 {
00659 
00660     const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
00661 
00662     glPushAttrib ( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT );
00663 
00664     glEnable ( GL_BLEND );
00665     glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00666     glDisable (GL_TEXTURE_2D);
00667 
00668     //TODO: maybe colors should be configurable, or at least the alpha value
00669     glColor4f ( 1.0f,1.0f,1, 0.4f) ;
00670 
00671 
00672 /*FIXME: Too much calculations here, we should be generating scaled driveline arrays
00673  * in Track::loadDriveline so all we'd be doing is pumping out predefined
00674  * vertexes in-game.
00675  */
00676     /*Draw white filling of the map*/
00677     glBegin ( GL_QUAD_STRIP ) ;
00678 
00679     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i ) {
00680       glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00681           y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y) ;
00682       glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00683           y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00684     }
00685     glVertex2f ( x_offset + ( m_left_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
00686         y_offset + ( m_left_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
00687     glVertex2f ( x_offset + ( m_right_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
00688         y_offset + ( m_right_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
00689 
00690     glEnd () ;
00691 
00692 
00693     glEnable( GL_LINE_SMOOTH );
00694     glEnable( GL_POINT_SMOOTH );
00695     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
00696     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
00697 
00698     glLineWidth(2);
00699     glPointSize(2);
00700 
00701     glColor4f ( 0,0,0,1) ;
00702 
00703 
00704     glBegin ( GL_LINES ) ;
00705     for ( size_t i = 0 ; i < DRIVELINE_SIZE - 1 ; ++i )
00706     {
00707         /*Draw left driveline of the map*/
00708         glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00709             y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00710 
00711         glVertex2f ( x_offset + ( m_left_driveline[i+1][0] - m_driveline_min[0] ) * m_scale_x,
00712             y_offset + ( m_left_driveline[i+1][1] - m_driveline_min[1] ) * m_scale_y ) ;
00713 
00714 
00715         /*Draw left driveline of the map*/
00716         glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00717                y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00718 
00719         glVertex2f ( x_offset + ( m_right_driveline[i+1][0] - m_driveline_min[0] ) * m_scale_x,
00720                y_offset + ( m_right_driveline[i+1][1] - m_driveline_min[1] ) * m_scale_y ) ;
00721     }
00722 
00723     //Close the left driveline
00724     glVertex2f ( x_offset + ( m_left_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * m_scale_x,
00725         y_offset + ( m_left_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * m_scale_y ) ;
00726 
00727     glVertex2f ( x_offset + ( m_left_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
00728         y_offset + ( m_left_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
00729 
00730 
00731     //Close the right driveline
00732     glVertex2f ( x_offset + ( m_right_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * m_scale_x,
00733         y_offset + ( m_right_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * m_scale_y ) ;
00734 
00735     glVertex2f ( x_offset + ( m_right_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
00736         y_offset + ( m_right_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
00737     glEnd () ;
00738 
00739 #if 0
00740   //FIXME: We are not sure if it's a videocard problem, but on Linux with a
00741   //Nvidia Geforce4 mx 440, we get problems with GL_LINE_LOOP;
00742   //If this issue is solved, using GL_LINE_LOOP is a better solution than
00743   //GL_LINES
00744     glBegin ( GL_LINE_LOOP ) ;
00745     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00746     {
00747         glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00748             y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00749     }
00750     glEnd () ;
00751 
00752     glBegin ( GL_LINE_LOOP ) ;
00753     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00754     {
00755         glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00756                y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00757     }
00758     glEnd () ;
00759 #endif
00760 
00761     /*Because of the way OpenGL draws lines of widths higher than 1,
00762      *we have to draw the joints too, in order to fill small spaces
00763      *between lines
00764      */
00765     glBegin ( GL_POINTS) ;
00766     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00767     {
00768         glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00769             y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00770 
00771         glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
00772              y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
00773     }
00774     glEnd () ;
00775 
00776     glPopAttrib();
00777 
00778 }   // draw2Dview
00779 
00780 //-----------------------------------------------------------------------------
00781 void Track::loadTrack(std::string filename_)
00782 {
00783     m_filename      = filename_;
00784 
00785     m_ident = StringUtils::basename(StringUtils::without_extension(m_filename));
00786     std::string path = StringUtils::without_extension(m_filename);
00787 
00788     // Default values
00789     m_use_fog = false;
00790     sgSetVec4 ( m_fog_color  , 0.3f, 0.7f, 0.9f, 1.0f ) ;
00791     m_fog_density               = 1.0f/100.0f;
00792     m_fog_start                 = 0.0f;
00793     m_fog_end                   = 1000.0f;
00794     m_gravity                   = 9.80665f;
00795     m_AI_angle_adjustment       = 1.0f;
00796     m_AI_curve_speed_adjustment = 1.0f;
00797 
00798     sgSetVec3 ( m_sun_position,  0.4f, 0.4f, 0.4f      );
00799     sgSetVec4 ( m_sky_color,     0.3f, 0.7f, 0.9f, 1.0f );
00800     sgSetVec4 ( m_fog_color,     0.3f, 0.7f, 0.9f, 1.0f );
00801     sgSetVec4 ( m_ambient_col,   0.5f, 0.5f, 0.5f, 1.0f );
00802     sgSetVec4 ( m_specular_col,  1.0f, 1.0f, 1.0f, 1.0f );
00803     sgSetVec4 ( m_diffuse_col,   1.0f, 1.0f, 1.0f, 1.0f );
00804 
00805     lisp::Parser parser;
00806     const lisp::Lisp* const ROOT = parser.parse(m_filename);
00807 
00808     const lisp::Lisp* const LISP = ROOT->getLisp("tuxkart-track");
00809     if(!LISP)
00810     {
00811         delete ROOT;
00812         char msg[MAX_ERROR_MESSAGE_LENGTH];
00813         snprintf(msg, sizeof(msg), 
00814                  "Couldn't load map '%s': no tuxkart-track node.",
00815                  m_filename.c_str());
00816         throw std::runtime_error(msg);
00817     }
00818 
00819     LISP->get      ("name",                  m_name);
00820     LISP->get      ("description",           m_description);
00821     LISP->get      ("designer",              m_designer);
00822     std::vector<std::string> filenames;
00823     LISP->getVector("music",                 filenames);
00824     getMusicInformation(filenames, m_music);
00825     LISP->get      ("herring",               m_herring_style);
00826     LISP->get      ("screenshot",            m_screenshot);
00827     LISP->get      ("topview",               m_top_view);
00828     LISP->get      ("sky-color",             m_sky_color);
00829     LISP->getVector("start-x",               m_start_x);
00830     LISP->getVector("start-y",               m_start_y);
00831     LISP->getVector("start-z",               m_start_z);
00832     LISP->getVector("start-heading",         m_start_heading);
00833     LISP->get      ("use-fog",               m_use_fog);
00834     LISP->get      ("fog-color",             m_fog_color);
00835     LISP->get      ("fog-density",           m_fog_density);
00836     LISP->get      ("fog-start",             m_fog_start);
00837     LISP->get      ("fog-end",               m_fog_end);
00838     LISP->get      ("sun-position",          m_sun_position);
00839     LISP->get      ("sun-ambient",           m_ambient_col);
00840     LISP->get      ("sun-specular",          m_specular_col);
00841     LISP->get      ("sun-diffuse",           m_diffuse_col);
00842     LISP->get      ("gravity",               m_gravity);
00843     LISP->get      ("AI-angle-adjust",       m_AI_angle_adjustment);
00844     LISP->get      ("AI-curve-speed-adjust", m_AI_curve_speed_adjustment);
00845 
00846     // Set the correct paths
00847     m_screenshot = file_manager->getTrackFile(m_screenshot, getIdent());
00848     m_top_view   = file_manager->getTrackFile(m_top_view,   getIdent());
00849     
00850     delete ROOT;
00851 }   // loadTrack
00852 
00853 //-----------------------------------------------------------------------------
00854 void Track::getMusicInformation(std::vector<std::string>&       filenames, 
00855                                 std::vector<MusicInformation*>& music    )
00856 {
00857     for(int i=0; i<(int)filenames.size(); i++)
00858     {
00859         std::string full_path = file_manager->getTrackFile(filenames[i], getIdent());
00860         MusicInformation* mi;
00861         try
00862         {
00863             mi = sound_manager->getMusicInformation(full_path);
00864         }
00865         catch(std::runtime_error)
00866         {
00867             mi = sound_manager->getMusicInformation(file_manager->getMusicFile(filenames[i]));
00868         }
00869         if(!mi)
00870         {
00871             fprintf(stderr, "Music information file '%s' not found - ignored.\n",
00872                     filenames[i].c_str());
00873             continue;
00874         }
00875         m_music.push_back(mi);
00876     }   // for i in filenames
00877 }   // getMusicInformation
00878 
00879 //-----------------------------------------------------------------------------
00880 void Track::startMusic() const {
00881     sound_manager->startMusic(m_music[rand()% m_music.size()]);
00882 }   // startMusic
00883 
00884 //-----------------------------------------------------------------------------
00885 void
00886 Track::loadDriveline()
00887 {
00888     readDrivelineFromFile(m_left_driveline, ".drvl");
00889 
00890     const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
00891     m_right_driveline.reserve(DRIVELINE_SIZE);
00892     readDrivelineFromFile(m_right_driveline, ".drvr");
00893 
00894     if(m_right_driveline.size() != m_left_driveline.size())
00895         std::cout << "Error: driveline's sizes do not match, right " <<
00896         "driveline is " << m_right_driveline.size() << " vertex long " <<
00897         "and the left driveline is " << m_left_driveline.size()
00898         << " vertex long. Track is " << m_name << " ." << std::endl;
00899 
00900     SGfloat width;
00901     sgVec3 center_point, width_vector;
00902     m_driveline.reserve(DRIVELINE_SIZE);
00903     m_path_width.reserve(DRIVELINE_SIZE);
00904     m_angle.reserve(DRIVELINE_SIZE);
00905     for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
00906     {
00907         sgAddVec3(center_point, m_left_driveline[i], m_right_driveline[i]);
00908         sgScaleVec3(center_point, 0.5f);
00909         m_driveline.push_back(center_point);
00910 
00911         sgSubVec3(width_vector, m_right_driveline[i], center_point);
00912         width = sgLengthVec3(width_vector);
00913         if(width > 0.0f) m_path_width.push_back(width);
00914         else m_path_width.push_back(-width);
00915     }
00916 
00917     size_t next;
00918     float adjacent_line, opposite_line;
00919     SGfloat theta;
00920 
00921     for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
00922     {
00923         next = i + 1 >= DRIVELINE_SIZE ? 0 : i + 1;
00924         adjacent_line = m_driveline[next][0] - m_driveline[i][0];
00925         opposite_line = m_driveline[next][1] - m_driveline[i][1];
00926 
00927         theta = sgATan(opposite_line/adjacent_line);
00928         theta += adjacent_line < 0.0f ? 90.0f : -90.0f;
00929 
00930         m_angle.push_back(theta);
00931     }
00932 
00933     sgSetVec2 ( m_driveline_min,  SG_MAX/2.0f,  SG_MAX/2.0f ) ;
00934     sgSetVec2 ( m_driveline_max, -SG_MAX/2.0f, -SG_MAX/2.0f ) ;
00935 
00936 
00937     m_distance_from_start.reserve(DRIVELINE_SIZE);
00938     float d = 0.0f ;
00939     for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
00940     {
00941         //Both drivelines must be checked to get the true size of
00942         //the drivelines, and using the center driveline is not
00943         //good enough.
00944         if ( m_right_driveline[i][0] < m_driveline_min[0] )
00945             m_driveline_min[0] = m_right_driveline[i][0] ;
00946         if ( m_right_driveline[i][1] < m_driveline_min[1] )
00947             m_driveline_min[1] = m_right_driveline[i][1] ;
00948         if ( m_right_driveline[i][0] > m_driveline_max[0] )
00949             m_driveline_max[0] = m_right_driveline[i][0] ;
00950         if ( m_right_driveline[i][1] > m_driveline_max[1] )
00951             m_driveline_max[1] = m_right_driveline[i][1] ;
00952 
00953         if ( m_left_driveline[i][0] < m_driveline_min[0] )
00954             m_driveline_min[0] = m_left_driveline[i][0] ;
00955         if ( m_left_driveline[i][1] < m_driveline_min[1] )
00956             m_driveline_min[1] = m_left_driveline[i][1] ;
00957         if ( m_left_driveline[i][0] > m_driveline_max[0] )
00958             m_driveline_max[0] = m_left_driveline[i][0] ;
00959         if ( m_left_driveline[i][1] > m_driveline_max[1] )
00960             m_driveline_max[1] = m_left_driveline[i][1] ;
00961 
00962 
00963         m_distance_from_start.push_back(d);  // dfs[i] is not valid in windows here!
00964 
00965         if ( i == DRIVELINE_SIZE - 1 )
00966             d += sgDistanceVec2 ( m_driveline[i], m_driveline[0] ) ;
00967         else
00968             d += sgDistanceVec2 ( m_driveline[i], m_driveline[i+1] ) ;
00969     }
00970 
00971     m_total_distance = d;
00972 
00973     sgVec2 sc ;
00974     sgSubVec2 ( sc, m_driveline_max, m_driveline_min ) ;
00975 
00976     m_scale_x = m_track_2d_width  / sc[0] ;
00977     m_scale_y = m_track_2d_height / sc[1] ;
00978 
00979     if(!m_do_stretch) m_scale_x = m_scale_y = std::min(m_scale_x, m_scale_y);
00980     
00981 }   // loadDriveline
00982 
00983 //-----------------------------------------------------------------------------
00984 void
00985 Track::readDrivelineFromFile(std::vector<sgVec3Wrapper>& line, const std::string& file_ext)
00986 {
00987     std::string path = file_manager->getTrackFile(m_ident+file_ext);
00988     FILE *fd = fopen ( path.c_str(), "r" ) ;
00989 
00990     if ( fd == NULL )
00991     {
00992         char msg[MAX_ERROR_MESSAGE_LENGTH];
00993         snprintf (msg, sizeof(msg), "Can't open '%s' for reading.\n", path.c_str() ) ;
00994         throw std::runtime_error(msg);
00995     }
00996 
00997     int prev_sector = UNKNOWN_SECTOR;
00998     SGfloat prev_distance = 1.51f;
00999     while(!feof(fd))
01000     {
01001         char s [ 1024 ] ;
01002 
01003         if ( fgets ( s, 1023, fd ) == NULL )
01004             break ;
01005 
01006         if ( *s == '#' || *s < ' ' )
01007             continue ;
01008 
01009         float x = 0.0f;
01010         float y = 0.0f;
01011         float z = 0.0f;
01012 
01013         if (sscanf ( s, "%f,%f,%f", &x, &y, &z ) != 3 )
01014         {
01015             char msg[MAX_ERROR_MESSAGE_LENGTH];
01016             snprintf (msg, sizeof(msg), "Syntax error in '%s'\n", path.c_str() ) ;
01017             throw std::runtime_error(msg);
01018         }
01019 
01020         sgVec3 point;
01021         point[0] = x;
01022         point[1] = y;
01023         point[2] = z;
01024 
01025         if(prev_sector != UNKNOWN_SECTOR) prev_distance = sgDistanceVec2(
01026             point, line[prev_sector] );
01027 
01028         //1.5f was choosen because it's more or less the length of the tuxkart
01029         if(prev_distance < 0.0000001)
01030         {
01031             fprintf(stderr, "File %s point %d is duplicated!.\n",
01032                     path.c_str(), prev_sector+1);
01033         }
01034 #if 0
01035         else if(prev_distance < 1.5f)
01036         {
01037             fprintf(stderr,"File %s point %d is too close(<1.5) to previous point.\n",
01038                     path.c_str(), prev_sector + 1);
01039         }
01040         if(prev_distance > 15.0f)
01041         {
01042             fprintf(stderr,"In file %s point %d is too far(>15.0) from next point at %d.\n",
01043                     path, prev_sector, prev_distance);
01044         }
01045 #endif
01046 
01047         line.push_back(point);
01048         ++prev_sector;
01049         prev_distance -= 1.5f;
01050     }
01051 
01052     fclose ( fd ) ;
01053 }   // readDrivelineFromFile
01054 
01055 // -----------------------------------------------------------------------------
01056 //* Convert the ssg track tree into its physics equivalents.
01057 void Track::createPhysicsModel()
01058 {
01059     if(!m_model) return;
01060 
01061     m_track_mesh         = new TriangleMesh();
01062     m_non_collision_mesh = new TriangleMesh();
01063     
01064     // Collect all triangles in the track_mesh
01065     sgMat4 mat;
01066     sgMakeIdentMat4(mat);
01067     convertTrackToBullet(m_model, mat);
01068     m_track_mesh->createBody();
01069     m_non_collision_mesh->createBody(btCollisionObject::CF_NO_CONTACT_RESPONSE);
01070     
01071 }   // createPhysicsModel
01072 
01073 // -----------------------------------------------------------------------------
01074 //* Convert the ssg track tree into its physics equivalents.
01075 void Track::convertTrackToBullet(ssgEntity *track, sgMat4 m)
01076 {
01077     if(!track) return;
01078     MovingPhysics *mp = dynamic_cast<MovingPhysics*>(track);
01079     if(mp)
01080     {
01081         // If the track contains obect of type MovingPhysics,
01082         // these objects will be real rigid body and are already
01083         // part of the world. So these objects must not be converted
01084         // to triangle meshes.
01085     } 
01086     else if(track->isAKindOf(ssgTypeLeaf()))
01087     {
01088         ssgLeaf  *leaf     = (ssgLeaf*)(track);
01089         Material *material = material_manager->getMaterial(leaf);
01090         // Don't convert triangles with material that is ignored (e.g. fuzzy_sand)
01091         if(!material || material->isIgnore()) return;
01092 
01093         for(int i=0; i<leaf->getNumTriangles(); i++) 
01094         {
01095             short v1,v2,v3;
01096             sgVec3 vv1, vv2, vv3;
01097             
01098             leaf->getTriangle(i, &v1, &v2, &v3);
01099             sgXformPnt3 ( vv1, leaf->getVertex(v1), m );
01100             sgXformPnt3 ( vv2, leaf->getVertex(v2), m );
01101             sgXformPnt3 ( vv3, leaf->getVertex(v3), m );
01102             btVector3 vb1(vv1[0],vv1[1],vv1[2]);
01103             btVector3 vb2(vv2[0],vv2[1],vv2[2]);
01104             btVector3 vb3(vv3[0],vv3[1],vv3[2]);
01105             if(material->isZipper()) 
01106             {
01107                 m_non_collision_mesh->addTriangle(vb1, vb2, vb3, material);
01108             }
01109             else
01110             {
01111                 m_track_mesh->addTriangle(vb1, vb2, vb3, material);
01112             }
01113         }
01114         
01115     }   // if(track isAKindOf leaf)
01116     else if(track->isAKindOf(ssgTypeTransform()))
01117     {
01118         ssgBaseTransform *t = (ssgBaseTransform*)(track);
01119         sgMat4 tmpT, tmpM;
01120         t->getTransform(tmpT);
01121         sgCopyMat4(tmpM, m);
01122         sgPreMultMat4(tmpM,tmpT);
01123         for(ssgEntity *e = t->getKid(0); e!=NULL; e=t->getNextKid())
01124         {
01125             convertTrackToBullet(e, tmpM);
01126         }   // for i
01127     }
01128     else if (track->isAKindOf(ssgTypeBranch())) 
01129     {
01130         ssgBranch *b =(ssgBranch*)track;
01131         for(ssgEntity* e=b->getKid(0); e!=NULL; e=b->getNextKid()) {
01132             convertTrackToBullet(e, m);
01133         }   // for i<getNumKids
01134     }
01135     else
01136     {
01137         assert(!"Unkown ssg type in convertTrackToBullet");
01138     }
01139 }   // convertTrackToBullet
01140 
01141 // ----------------------------------------------------------------------------
01142 void Track::loadTrackModel()
01143 {
01144     // Add the track directory to the texture search path
01145     file_manager->pushTextureSearchPath(file_manager->getTrackFile("",getIdent()));
01146     file_manager->pushModelSearchPath  (file_manager->getTrackFile("",getIdent()));
01147     // First read the temporary materials.dat file if it exists
01148     try
01149     {
01150         std::string materials_file = file_manager->getTrackFile("materials.dat",getIdent());
01151         material_manager->pushTempMaterial(materials_file);
01152     }
01153     catch (std::exception& e)
01154     {
01155         // no temporary materials.dat file, ignore
01156         (void)e;
01157     }
01158     std::string path = file_manager->getTrackFile(getIdent()+".loc");
01159 
01160     FILE *fd = fopen (path.c_str(), "r" );
01161     if ( fd == NULL )
01162     {
01163         char msg[MAX_ERROR_MESSAGE_LENGTH];
01164         snprintf(msg, sizeof(msg),"Can't open track location file '%s'.\n",
01165                  path.c_str());
01166         throw std::runtime_error(msg);
01167     }
01168 
01169     // Start building the scene graph
01170     m_model = new ssgBranch ;
01171     scene->add(m_model);
01172 
01173     char s [ 1024 ] ;
01174 
01175     while ( fgets ( s, 1023, fd ) != NULL )
01176     {
01177         if ( *s == '#' || *s < ' ' )
01178             continue ;
01179 
01180         int need_hat = false ;
01181         int fit_skin = false ;
01182         char fname [ 1024 ] ;
01183         sgCoord loc ;
01184         sgZeroVec3 ( loc.xyz ) ;
01185         sgZeroVec3 ( loc.hpr ) ;
01186 
01187         char htype = '\0' ;
01188 
01189         if ( sscanf ( s, "%cHERRING,%f,%f,%f", &htype,
01190                       &(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]) ) == 4 )
01191         {
01192             herring_command(&loc.xyz, htype, false) ;
01193         }
01194         else if ( sscanf ( s, "%cHERRING,%f,%f", &htype,
01195                            &(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
01196         {
01197             herring_command (&loc.xyz, htype, true) ;
01198         }
01199         else if ( s[0] == '\"' )
01200         {
01201             if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f,%f,%f,%f",
01202                           fname, &(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]),
01203                           &(loc.hpr[0]), &(loc.hpr[1]), &(loc.hpr[2]) ) == 7 )
01204             {
01205                 /* All 6 DOF specified */
01206                 need_hat = false;
01207             }
01208             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f,%f,%f",
01209                                fname, &(loc.xyz[0]), &(loc.xyz[1]),
01210                                &(loc.hpr[0]), &(loc.hpr[1]), &(loc.hpr[2])) == 6 )
01211             {
01212                 /* All 6 DOF specified - but need height */
01213                 need_hat = true ;
01214             }
01215             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f,%f",
01216                                fname, &(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]),
01217                                &(loc.hpr[0]) ) == 5 )
01218             {
01219                 /* No Roll/Pitch specified - assumed zero */
01220                 need_hat = false ;
01221             }
01222             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f,{},{}",
01223                                fname, &(loc.xyz[0]), &(loc.xyz[1]),
01224                                &(loc.hpr[0]) ) == 4 )
01225             {
01226                 /* All 6 DOF specified - but need height, roll, pitch */
01227                 need_hat = true ;
01228                 fit_skin = true ;
01229             }
01230             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f",
01231                                fname, &(loc.xyz[0]), &(loc.xyz[1]),
01232                                &(loc.hpr[0]) ) == 4 )
01233             {
01234                 /* No Roll/Pitch specified - but need height */
01235                 need_hat = true ;
01236             }
01237             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f",
01238                                fname, &(loc.xyz[0]), &(loc.xyz[1]),
01239                                &(loc.xyz[2]) ) == 4 )
01240             {
01241                 /* No Heading/Roll/Pitch specified - but need height */
01242                 need_hat = false ;
01243             }
01244             else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{}",
01245                                fname, &(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
01246             {
01247                 /* No Roll/Pitch specified - but need height */
01248                 need_hat = true ;
01249             }
01250             else if ( sscanf ( s, "\"%[^\"]\",%f,%f",
01251                                fname, &(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
01252             {
01253                 /* No Z/Heading/Roll/Pitch specified */
01254                 need_hat = false ;
01255             }
01256             else if ( sscanf ( s, "\"%[^\"]\"", fname ) == 1 )
01257             {
01258                 /* Nothing specified */
01259                 need_hat = false ;
01260             }
01261             else
01262             {
01263                 fclose(fd);
01264                 char msg[MAX_ERROR_MESSAGE_LENGTH];
01265                 snprintf(msg, sizeof(msg), "Syntax error in '%s': %s",
01266                          path.c_str(), s);
01267                 throw std::runtime_error(msg);
01268             }
01269 
01270             if ( need_hat )
01271             {
01272                 sgVec3 nrm ;
01273 
01274                 loc.xyz[2] = 1000.0f ;
01275                 loc.xyz[2] = getHeightAndNormal ( m_model, loc.xyz, nrm ) ;
01276 
01277                 if ( fit_skin )
01278                 {
01279                     float sy = sin ( -loc.hpr [ 0 ] * SG_DEGREES_TO_RADIANS ) ;
01280                     float cy = cos ( -loc.hpr [ 0 ] * SG_DEGREES_TO_RADIANS ) ;
01281 
01282                     loc.hpr[2] =  SG_RADIANS_TO_DEGREES * atan2 ( nrm[0] * cy -
01283                                   nrm[1] * sy, nrm[2] ) ;
01284                     loc.hpr[1] = -SG_RADIANS_TO_DEGREES * atan2 ( nrm[1] * cy +
01285                                  nrm[0] * sy, nrm[2] ) ;
01286                 }
01287             }   // if need_hat
01288 
01289             ssgEntity        *obj   = loader->load(file_manager->getModelFile(fname),
01290                                                    CB_TRACK,
01291                                                    /* optimise   */  true,
01292                                                    /*is_full_path*/  true);
01293             if(!obj)
01294             {
01295                 fclose(fd);
01296                 char msg[MAX_ERROR_MESSAGE_LENGTH];
01297                 snprintf(msg, sizeof(msg), "Can't open track model '%s'",fname);
01298                 file_manager->popTextureSearchPath();
01299                 file_manager->popModelSearchPath  ();
01300                 throw std::runtime_error(msg);
01301             }
01302             createDisplayLists(obj);
01303             ssgRangeSelector *lod   = new ssgRangeSelector ;
01304             ssgTransform     *trans = new ssgTransform ( & loc ) ;
01305 
01306             float r [ 2 ] = { -10.0f, 2000.0f } ;
01307 
01308             lod    -> addKid(obj   );
01309             trans  -> addKid(lod   );
01310             m_model-> addKid(trans );
01311             lod    -> setRanges(r, 2);
01312             if(user_config->m_track_debug)
01313                 addDebugToScene(user_config->m_track_debug);
01314 
01315         }
01316         else
01317         {
01318 //            fclose(fd);
01319 //            char msg[MAX_ERROR_MESSAGE_LENGTH];
01320 //            snprintf(msg, sizeof(msg), "Syntax error in '%s': %s",
01321             fprintf(stderr, "Warning: Syntax error in '%s': %s",
01322                      path.c_str(), s);
01323 //            throw std::runtime_error(msg);
01324         }
01325     }   // while fgets
01326 
01327     fclose ( fd ) ;
01328     file_manager->popTextureSearchPath();
01329     file_manager->popModelSearchPath  ();
01330 
01331     createPhysicsModel();
01332 }   // loadTrack
01333 
01334 //-----------------------------------------------------------------------------
01335 void Track::herring_command (sgVec3 *xyz, char htype, int bNeedHeight )
01336 {
01337 
01338     // if only 2d coordinates are given, let the herring fall from very heigh
01339     if(bNeedHeight) (*xyz)[2] = 1000000.0f;
01340 
01341     // Even if 3d data are given, make sure that the herring is on the ground
01342     (*xyz)[2] = getHeight ( m_model, *xyz ) + 0.06f;
01343     herringType type=HE_GREEN;
01344     if ( htype=='Y' || htype=='y' ) { type = HE_GOLD   ;}
01345     if ( htype=='G' || htype=='g' ) { type = HE_GREEN  ;}
01346     if ( htype=='R' || htype=='r' ) { type = HE_RED    ;}
01347     if ( htype=='S' || htype=='s' ) { type = HE_SILVER ;}
01348 
01349     // Time trial does not have any red herrings
01350     if(type==HE_RED && race_manager->getRaceMode()==RaceManager::RM_TIME_TRIAL) 
01351         return;
01352     herring_manager->newHerring(type, xyz);
01353 }   // herring_command
01354 
01355 // ----------------------------------------------------------------------------
01356 void  Track::getTerrainInfo(const btVector3 &pos, float *hot, btVector3* normal, 
01357                             const Material **material) const
01358 {
01359     btVector3 to_pos(pos);
01360     to_pos.setZ(-100000.f);
01361 
01362     class MaterialCollision : public btCollisionWorld::ClosestRayResultCallback
01363     {
01364     public:
01365         const Material* m_material;
01366         MaterialCollision(btVector3 p1, btVector3 p2) : 
01367             btCollisionWorld::ClosestRayResultCallback(p1,p2) {m_material=NULL;}
01368         virtual btScalar AddSingleResult(btCollisionWorld::LocalRayResult& rayResult,
01369                                          bool normalInWorldSpace) {
01370              if(rayResult.m_localShapeInfo && rayResult.m_localShapeInfo->m_shapePart>=0 )
01371              {
01372                  m_material = ((TriangleMesh*)rayResult.m_collisionObject->getUserPointer())->getMaterial(rayResult.m_localShapeInfo->m_triangleIndex);
01373              }
01374              return btCollisionWorld::ClosestRayResultCallback::AddSingleResult(rayResult, 
01375                                                                                 normalInWorldSpace);
01376         }   // AddSingleResult
01377     };   // myCollision
01378     MaterialCollision rayCallback(pos, to_pos);
01379     world->getPhysics()->getPhysicsWorld()->rayTest(pos, to_pos, rayCallback);
01380 
01381     if(!rayCallback.HasHit()) 
01382     {
01383         *hot      = NOHIT;
01384         *material = NULL;
01385         return;
01386     }
01387 
01388     *hot      = rayCallback.m_hitPointWorld.getZ();
01389     *normal   = rayCallback.m_hitNormalWorld;
01390     *material = rayCallback.m_material;
01391     // Note: material might be NULL. This happens if the ray cast does not
01392     // hit the track, but anoterh rigid body (kart, moving_physics) - e.g.
01393     // assume two karts falling down, one over the other. Bullet does not
01394     // have any triangle/material information in this case!
01395 }   // getTerrainInfo