stk-code_catmod/src/track.cpp
hikerstk 5365fcadda Blue boxes are no longer placed on a track when time trial
mode is used.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1864 178a84e3-b1eb-0310-8ba1-8eac791a3b58
2008-05-12 00:23:39 +00:00

1396 lines
52 KiB
C++

// $Id$
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004 Steve Baker <sjbaker1@airmail.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <plib/ssgAux.h>
#include "file_manager.hpp"
#include "loader.hpp"
#include "track.hpp"
#include "string_utils.hpp"
#include "lisp/lisp.hpp"
#include "lisp/parser.hpp"
#include "stk_config.hpp"
#include "translation.hpp"
#include "scene.hpp"
#include "moving_physics.hpp"
#include "world.hpp"
#include "material_manager.hpp"
#include "isect.hpp"
#include "ssg_help.hpp"
#include "user_config.hpp"
#include "herring.hpp"
#include "herring_manager.hpp"
#include "sound_manager.hpp"
#include "race_manager.hpp"
#if defined(WIN32) && !defined(__CYGWIN__)
# define snprintf _snprintf
#endif
const float Track::NOHIT = -99999.9f;
const int Track::QUAD_TRI_NONE = -1;
const int Track::QUAD_TRI_FIRST = 1;
const int Track::QUAD_TRI_SECOND = 2;
const int Track::UNKNOWN_SECTOR = -1;
// ----------------------------------------------------------------------------
Track::Track( std::string filename_, float w, float h, bool stretch )
{
m_filename = filename_;
m_herring_style = "";
m_track_2d_width = w;
m_track_2d_height = h;
m_do_stretch = stretch;
m_description = "";
m_designer = "";
m_screenshot = "";
m_top_view = "";
loadTrack(m_filename);
loadDriveline();
} // Track
//-----------------------------------------------------------------------------
Track::~Track()
{
} // ~Track
//-----------------------------------------------------------------------------
/** Removes the physical body from the world.
* Called at the end of a race.
*/
void Track::cleanup()
{
delete m_non_collision_mesh;
delete m_track_mesh;
// remove temporary materials loaded by the material manager
material_manager->popTempMaterial();
} // cleanup
//-----------------------------------------------------------------------------
/** Finds on which side of the line segment a given point is.
*/
inline float Track::pointSideToLine( const sgVec2 L1, const sgVec2 L2,
const sgVec2 P ) const
{
return ( L2[0]-L1[0] )*( P[1]-L1[1] )-( L2[1]-L1[1] )*( P[0]-L1[0] );
} // pointSideToLine
//-----------------------------------------------------------------------------
/** pointInQuad() works by checking if the given point is 'to the right'
* in clock-wise direction (which would be to look towards the inside of
* the quad) of each line segment that forms the quad. If it is to the
* left of all the segments, then the point is inside. This idea
* works for convex polygons, so we have to test it for the two
* triangles that compose the quad, in case that the quad is concave,
* not for the quad itself.
*/
int Track::pointInQuad
(
const sgVec2 A,
const sgVec2 B,
const sgVec2 C,
const sgVec2 D,
const sgVec2 POINT
) const
{
if(pointSideToLine( C, A, POINT ) >= 0.0 )
{
//Test the first triangle
if( pointSideToLine( A, B, POINT ) > 0.0 &&
pointSideToLine( B, C, POINT ) >= 0.0 )
return QUAD_TRI_FIRST;
return QUAD_TRI_NONE;
}
//Test the second triangle
if( pointSideToLine( C, D, POINT ) > 0.0 &&
pointSideToLine( D, A, POINT ) > 0.0 )
return QUAD_TRI_SECOND;
return QUAD_TRI_NONE;
} // pointInQuad
//-----------------------------------------------------------------------------
/** findRoadSector returns in which sector on the road the position
* xyz is. If xyz is not on top of the road, it returns
* UNKNOWN_SECTOR.
*
* The 'sector' could be defined as the number of the closest track
* segment to XYZ.
*/
void Track::findRoadSector( const sgVec3 XYZ, int *sector )const
{
if(*sector!=UNKNOWN_SECTOR)
{
int next = (unsigned)(*sector) + 1 < m_left_driveline.size() ? *sector + 1 : 0;
if(pointInQuad( m_left_driveline[*sector], m_right_driveline[*sector],
m_right_driveline[next], m_left_driveline[next],
XYZ ) != QUAD_TRI_NONE)
// Still in the same sector, no changes
return;
}
/* To find in which 'sector' of the track the kart is, we use a
'point in triangle' algorithm for each triangle in the quad
that forms each track segment.
*/
std::vector <SegmentTriangle> possible_segment_tris;
const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
int triangle;
int next;
for( size_t i = 0; i < DRIVELINE_SIZE ; ++i )
{
next = (unsigned int)i + 1 < DRIVELINE_SIZE ? (int)i + 1 : 0;
triangle = pointInQuad( m_left_driveline[i], m_right_driveline[i],
m_right_driveline[next], m_left_driveline[next],
XYZ );
if (triangle != QUAD_TRI_NONE && ((XYZ[2]-m_left_driveline[i][2]) < 1.0f))
{
possible_segment_tris.push_back(SegmentTriangle((int)i, triangle));
}
}
/* Since xyz can be on more than one 2D track segment, we have to
find on top of which one of the possible track segments it is.
*/
const int POS_SEG_SIZE = (int)possible_segment_tris.size();
if( POS_SEG_SIZE == 0 )
{
//xyz is not on the road
*sector = UNKNOWN_SECTOR;
return;
}
//POS_SEG_SIZE > 1
/* To find on top of which track segment the variable xyz is,
we get which of the possible triangles that are under xyz
has the lower distance on the height(Y or Z) axis.
*/
float dist;
float near_dist = 99999;
int nearest = QUAD_TRI_NONE;
size_t segment;
sgVec4 plane;
for( int i = 0; i < POS_SEG_SIZE; ++i )
{
segment = possible_segment_tris[i].segment;
next = segment + 1 < DRIVELINE_SIZE ? (int)segment + 1 : 0;
if( possible_segment_tris[i].triangle == QUAD_TRI_FIRST )
{
sgMakePlane( plane, m_left_driveline[segment],
m_right_driveline[segment], m_right_driveline[next] );
}
else //possible_segment_tris[i].triangle == QUAD_TRI_SECOND
{
sgMakePlane( plane, m_right_driveline[next],
m_left_driveline[next], m_left_driveline[segment] );
}
dist = sgHeightAbovePlaneVec3( plane, XYZ );
/* sgHeightAbovePlaneVec3 gives a negative dist if the plane
is on top, so we have to rule it out.
However, for some reason there are cases where we get
negative values for the track segment we should be on.
*/
if( dist > -2.0 && dist < near_dist)
{
near_dist = dist;
nearest = i;
}
}
if( nearest != QUAD_TRI_NONE )
{
*sector=possible_segment_tris[nearest].segment;
return;
}
*sector = UNKNOWN_SECTOR;
return; // This only happens if the position is
// under all the possible sectors
} // findRoadSector
//-----------------------------------------------------------------------------
/** findOutOfRoadSector finds the sector where XYZ is, but as it name
implies, it is more accurate for the outside of the track than the
inside, and for STK's needs the accuracy on top of the track is
unacceptable; but if this was a 2D function, the accuracy for out
of road sectors would be perfect.
To find the sector we look for the closest line segment from the
right and left drivelines, and the number of that segment will be
the sector.
The SIDE argument is used to speed up the function only; if we know
that XYZ is on the left or right side of the track, we know that
the closest driveline must be the one that matches that condition.
In reality, the side used in STK is the one from the previous frame,
but in order to move from one side to another a point would go
through the middle, that is handled by findRoadSector() which doesn't
has speed ups based on the side.
NOTE: This method of finding the sector outside of the road is *not*
perfect: if two line segments have a similar altitude (but enough to
let a kart get through) and they are very close on a 2D system,
if a kart is on the air it could be closer to the top line segment
even if it is supposed to be on the sector of the lower line segment.
Probably the best solution would be to construct a quad that reaches
until the next higher overlapping line segment, and find the closest
one to XYZ.
*/
int Track::findOutOfRoadSector
(
const sgVec3 XYZ,
const RoadSide SIDE,
const int CURR_SECTOR
) const
{
int sector = UNKNOWN_SECTOR;
float dist;
//FIXME: it can happen that dist is bigger than nearest_dist for all the
//the points we check (currently a limit of +/- 10), and if so, the
//function will return UNKNOWN_SECTOR, and if the AI get this, it will
//trigger an assertion. I increased the nearest_dist default value from
//99999 to 9999999, which is a lot more than the situation that caused
//the discovery of this problem, but the best way to solve this, is to
//find a better way of handling the shortcuts, and maybe a better way of
//calculating the distance.
float nearest_dist = 9999999;
const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
int begin_sector = 0;
int end_sector = DRIVELINE_SIZE - 1;
if(CURR_SECTOR != UNKNOWN_SECTOR )
{
const int LIMIT = 10; //The limit prevents shortcuts
if( CURR_SECTOR - LIMIT < 0 )
{
begin_sector = DRIVELINE_SIZE - 1 + CURR_SECTOR - LIMIT;
}
else begin_sector = CURR_SECTOR - LIMIT;
if( CURR_SECTOR + LIMIT > (int)DRIVELINE_SIZE - 1 )
{
end_sector = CURR_SECTOR + LIMIT - DRIVELINE_SIZE;
}
else end_sector = CURR_SECTOR + LIMIT;
}
sgLineSegment3 line_seg;
int next_sector;
for (int i = begin_sector ; i != end_sector ; i = next_sector )
{
next_sector = i + 1 == (int)DRIVELINE_SIZE ? 0 : i + 1;
if( SIDE != RS_RIGHT)
{
sgCopyVec3( line_seg.a, m_left_driveline[i] );
sgCopyVec3( line_seg.b, m_left_driveline[next_sector] );
dist = sgDistSquaredToLineSegmentVec3( line_seg, XYZ );
if ( dist < nearest_dist )
{
nearest_dist = dist;
sector = i ;
}
}
if( SIDE != RS_LEFT )
{
sgCopyVec3( line_seg.a, m_right_driveline[i] );
sgCopyVec3( line_seg.b, m_right_driveline[next_sector] );
dist = sgDistSquaredToLineSegmentVec3( line_seg, XYZ );
if ( dist < nearest_dist )
{
nearest_dist = dist;
sector = i ;
}
}
} // for i
if(sector==UNKNOWN_SECTOR)
{
printf("unknown sector found.\n");
}
return sector;
} // findOutOfRoadSector
//-----------------------------------------------------------------------------
/** spatialToTrack() takes absolute coordinates (coordinates in OpenGL
* space) and transforms them into coordinates based on the track. It is
* for 2D coordinates, thought it can be used on 3D vectors. The y-axis
* of the returned vector is how much of the track the point has gone
* through, the x-axis is on which side of the road it is, and the z-axis
* contains half the width of the track at this point. The return value
* is p1, i.e. the first of the two driveline points between which the
* kart is currently located.
*/
int Track::spatialToTrack
(
sgVec3 dst,
const sgVec2 POS,
const int SECTOR
) const
{
if( SECTOR == UNKNOWN_SECTOR )
{
std::cerr << "WARNING: UNKNOWN_SECTOR in spatialToTrack().\n";
return -1;
}
const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
const size_t PREV = SECTOR == 0 ? DRIVELINE_SIZE - 1 : SECTOR - 1;
const size_t NEXT = (size_t)SECTOR+1 >= DRIVELINE_SIZE ? 0 : SECTOR + 1;
const float DIST_PREV = sgDistanceVec2 ( m_driveline[PREV], POS );
const float DIST_NEXT = sgDistanceVec2 ( m_driveline[NEXT], POS );
size_t p1, p2;
if ( DIST_NEXT < DIST_PREV )
{
p1 = SECTOR; p2 = NEXT;
}
else
{
p1 = PREV; p2 = SECTOR;
}
sgVec3 line_eqn;
sgVec2 tmp;
sgMake2DLine ( line_eqn, m_driveline[p1], m_driveline[p2] );
dst[0] = sgDistToLineVec2 ( line_eqn, POS );
sgAddScaledVec2 ( tmp, POS, line_eqn, -dst [0] );
float dist_from_driveline_p1 = sgDistanceVec2 ( tmp, m_driveline[p1] );
dst[1] = dist_from_driveline_p1 + m_distance_from_start[p1];
// Set z-axis to half the width (linear interpolation between the
// width at p1 and p2) - m_path_width is actually already half the width
// of the track. This is used to determine if a kart is too far
// away from the road and is therefore considered taking a shortcut.
float fraction = dist_from_driveline_p1
/ (m_distance_from_start[p2]-m_distance_from_start[p1]);
dst[2] = m_path_width[p1]*(1-fraction)+fraction*m_path_width[p2];
return (int)p1;
} // spatialToTrack
//-----------------------------------------------------------------------------
void Track::trackToSpatial ( sgVec3 xyz, const int SECTOR ) const
{
sgCopyVec3 ( xyz, m_driveline [ SECTOR ] ) ;
} // trackToSpatial
//-----------------------------------------------------------------------------
/** Returns the start coordinates for a kart on a given position pos
(with 0<=pos).
*/
void Track::getStartCoords(unsigned int pos, sgCoord* coords) const {
// Bug fix/workaround: sometimes the first kart would be too close
// to the first driveline point and not to the last one -->
// This kart would not get any lap counting done in the first
// lap! Therefor -1.5 is subtracted from the y position - which
// is a somewhat arbitrary value.
coords->xyz[0] = pos<m_start_x.size() ? m_start_x[pos] : ((pos%2==0)?1.5f:-1.5f);
coords->xyz[1] = pos<m_start_y.size() ? m_start_y[pos] : -1.5f*pos-1.5f;
// height must be larger than the actual hight for which hot is computed.
coords->xyz[2] = pos<m_start_z.size() ? m_start_z[pos] : 1.0f;
coords->hpr[0] = pos<m_start_heading.size() ? m_start_heading[pos] : 0.0f;
coords->hpr[1] = 0.0f;
coords->hpr[2] = 0.0f;
btVector3 tmp_pos(coords->xyz[0],coords->xyz[1],coords->xyz[2]);
btVector3 normal;
const Material *material=NULL;
getTerrainInfo(tmp_pos, &(coords->xyz[2]), &normal, &material);
} // getStartCoords
//-----------------------------------------------------------------------------
/** Determines if a kart moving from sector OLDSEC to sector NEWSEC
* would be taking a shortcut, i.e. if the distance is larger
* than a certain detla
*/
bool Track::isShortcut(const int OLDSEC, const int NEWSEC) const
{
// If the kart was off the road, don't do any shortcuts
if(OLDSEC==UNKNOWN_SECTOR || NEWSEC==UNKNOWN_SECTOR) return false;
unsigned int distance_sectors = abs(OLDSEC-NEWSEC);
// Handle 'wrap around': if the distance is more than half the
// number of driveline poins, assume it's a 'wrap around'
if(2*distance_sectors > (unsigned int)m_driveline.size())
distance_sectors = (unsigned int)m_driveline.size() - distance_sectors;
return (distance_sectors>stk_config->m_shortcut_segments);
} // isShortcut
//-----------------------------------------------------------------------------
void Track::addDebugToScene(int type) const
{
if(type&1)
{
ssgaSphere *sphere;
sgVec3 center;
sgVec4 colour;
for(unsigned int i = 0; i < m_driveline.size(); ++i)
{
sphere = new ssgaSphere;
sgCopyVec3(center, m_driveline[i]);
sphere->setCenter(center);
sphere->setSize(getWidth()[i] / 4.0f);
if(i == 0)
{
colour[0] = colour[2] = colour[3] = 255;
colour[1] = 0;
}
else
{
colour[0] = colour[1] = colour[3] = 255;
colour[2] = 0;
}
sphere->setColour(colour);
scene->add(sphere);
} // for i
} /// type ==1
if(type&2)
{
ssgVertexArray* v_array = new ssgVertexArray();
ssgColourArray* c_array = new ssgColourArray();
for(unsigned int i = 0; i < m_driveline.size(); i++)
{
int ip1 = i==m_driveline.size()-1 ? 0 : i+1;
// The segment display must be slightly higher than the
// track, otherwise it's not clearly visible.
sgVec3 v;
sgCopyVec3(v,m_left_driveline [i ]); v[2]+=0.1f; v_array->add(v);
sgCopyVec3(v,m_right_driveline[i ]); v[2]+=0.1f; v_array->add(v);
sgCopyVec3(v,m_right_driveline[ip1]); v[2]+=0.1f; v_array->add(v);
sgCopyVec3(v,m_left_driveline [ip1]); v[2]+=0.1f; v_array->add(v);
sgVec4 vc;
vc[0] = i%2==0 ? 1.0f : 0.0f;
vc[1] = 1.0f-v[0];
vc[2] = 0.0f;
vc[3] = 0.1f;
c_array->add(vc);c_array->add(vc);c_array->add(vc);c_array->add(vc);
} // for i
// if GL_QUAD_STRIP is used, the colours are smoothed, so the changes
// from one segment to the next are not visible.
ssgVtxTable* l = new ssgVtxTable(GL_QUADS, v_array,
(ssgNormalArray*)NULL,
(ssgTexCoordArray*)NULL,
c_array);
scene->add(l);
}
} // addDebugToScene
//-----------------------------------------------------------------------------
/** It's not the nicest solution to have two very similar version of a function,
* i.e. drawScaled2D and draw2Dview - but to keep both versions const, the
* values m_scale_x/m_scale_y can not be changed, but they are needed in glVtx.
* So two functions are provided: one which uses temporary variables, and one
* which uses the pre-computed attributes (see constructor/loadDriveline)
* - which saves a bit of time at runtime as well.
* drawScaled2D is called from gui/TrackSel, draw2Dview from RaceGUI.
*/
void Track::drawScaled2D(float x, float y, float w, float h) const
{
sgVec2 sc;
sgSubVec2 ( sc, m_driveline_max, m_driveline_min );
float sx = w / sc[0];
float sy = h / sc[1];
if( sx > sy )
{
sx = sy;
x += w/2 - sc[0]*sx/2;
}
else
{
sy = sx;
}
const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
glPushAttrib ( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT );
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable (GL_TEXTURE_2D);
glColor4f ( 1, 1, 1, 0.5) ;
glBegin ( GL_QUAD_STRIP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy) ;
glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
}
glVertex2f ( x + ( m_left_driveline[0][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[0][1] - m_driveline_min[1] ) * sy) ;
glVertex2f ( x + ( m_right_driveline[0][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
glEnd () ;
glEnable( GL_LINE_SMOOTH );
glEnable( GL_POINT_SMOOTH );
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glLineWidth(1);
glPointSize(1);
glColor4f ( 0, 0, 0, 1 ) ;
glBegin ( GL_LINES ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE - 1 ; ++i )
{
/*Draw left driveline of the map*/
glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
glVertex2f ( x + ( m_left_driveline[i+1][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[i+1][1] - m_driveline_min[1] ) * sy ) ;
/*Draw left driveline of the map*/
glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
glVertex2f ( x + ( m_right_driveline[i+1][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[i+1][1] - m_driveline_min[1] ) * sy ) ;
}
//Close the left driveline
glVertex2f ( x + ( m_left_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * sy ) ;
glVertex2f ( x + ( m_left_driveline[0][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
//Close the right driveline
glVertex2f ( x + ( m_right_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * sy ) ;
glVertex2f ( x + ( m_right_driveline[0][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[0][1] - m_driveline_min[1] ) * sy ) ;
glEnd () ;
#if 0
//FIXME: We are not sure if it's a videocard problem, but on Linux with a
//Nvidia Geforce4 mx 440, we get problems with GL_LINE_LOOP;
//If this issue is solved, using GL_LINE_LOOP is a better solution than
//GL_LINES
glBegin ( GL_LINE_LOOP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
}
glEnd () ;
glBegin ( GL_LINE_LOOP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
}
glEnd () ;
#endif
glBegin ( GL_POINTS ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x + ( m_left_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_left_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
glVertex2f ( x + ( m_right_driveline[i][0] - m_driveline_min[0] ) * sx,
y + ( m_right_driveline[i][1] - m_driveline_min[1] ) * sy ) ;
}
glEnd () ;
glPopAttrib();
} // drawScaled2D
//-----------------------------------------------------------------------------
void Track::draw2Dview (float x_offset, float y_offset) const
{
const unsigned int DRIVELINE_SIZE = (unsigned int)m_driveline.size();
glPushAttrib ( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable (GL_TEXTURE_2D);
//TODO: maybe colors should be configurable, or at least the alpha value
glColor4f ( 1.0f,1.0f,1, 0.4f) ;
/*FIXME: Too much calculations here, we should be generating scaled driveline arrays
* in Track::loadDriveline so all we'd be doing is pumping out predefined
* vertexes in-game.
*/
/*Draw white filling of the map*/
glBegin ( GL_QUAD_STRIP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i ) {
glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y) ;
glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
}
glVertex2f ( x_offset + ( m_left_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_right_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
glEnd () ;
glEnable( GL_LINE_SMOOTH );
glEnable( GL_POINT_SMOOTH );
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glLineWidth(2);
glPointSize(2);
glColor4f ( 0,0,0,1) ;
glBegin ( GL_LINES ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE - 1 ; ++i )
{
/*Draw left driveline of the map*/
glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_left_driveline[i+1][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[i+1][1] - m_driveline_min[1] ) * m_scale_y ) ;
/*Draw left driveline of the map*/
glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_right_driveline[i+1][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[i+1][1] - m_driveline_min[1] ) * m_scale_y ) ;
}
//Close the left driveline
glVertex2f ( x_offset + ( m_left_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_left_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
//Close the right driveline
glVertex2f ( x_offset + ( m_right_driveline[DRIVELINE_SIZE - 1][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[DRIVELINE_SIZE - 1][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_right_driveline[0][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[0][1] - m_driveline_min[1] ) * m_scale_y ) ;
glEnd () ;
#if 0
//FIXME: We are not sure if it's a videocard problem, but on Linux with a
//Nvidia Geforce4 mx 440, we get problems with GL_LINE_LOOP;
//If this issue is solved, using GL_LINE_LOOP is a better solution than
//GL_LINES
glBegin ( GL_LINE_LOOP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
}
glEnd () ;
glBegin ( GL_LINE_LOOP ) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
}
glEnd () ;
#endif
/*Because of the way OpenGL draws lines of widths higher than 1,
*we have to draw the joints too, in order to fill small spaces
*between lines
*/
glBegin ( GL_POINTS) ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
glVertex2f ( x_offset + ( m_left_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_left_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
glVertex2f ( x_offset + ( m_right_driveline[i][0] - m_driveline_min[0] ) * m_scale_x,
y_offset + ( m_right_driveline[i][1] - m_driveline_min[1] ) * m_scale_y ) ;
}
glEnd () ;
glPopAttrib();
} // draw2Dview
//-----------------------------------------------------------------------------
void Track::loadTrack(std::string filename_)
{
m_filename = filename_;
m_ident = StringUtils::basename(StringUtils::without_extension(m_filename));
std::string path = StringUtils::without_extension(m_filename);
// Default values
m_use_fog = false;
sgSetVec4 ( m_fog_color , 0.3f, 0.7f, 0.9f, 1.0f ) ;
m_fog_density = 1.0f/100.0f;
m_fog_start = 0.0f;
m_fog_end = 1000.0f;
m_gravity = 9.80665f;
m_AI_angle_adjustment = 1.0f;
m_AI_curve_speed_adjustment = 1.0f;
sgSetVec3 ( m_sun_position, 0.4f, 0.4f, 0.4f );
sgSetVec4 ( m_sky_color, 0.3f, 0.7f, 0.9f, 1.0f );
sgSetVec4 ( m_fog_color, 0.3f, 0.7f, 0.9f, 1.0f );
sgSetVec4 ( m_ambient_col, 0.5f, 0.5f, 0.5f, 1.0f );
sgSetVec4 ( m_specular_col, 1.0f, 1.0f, 1.0f, 1.0f );
sgSetVec4 ( m_diffuse_col, 1.0f, 1.0f, 1.0f, 1.0f );
lisp::Parser parser;
const lisp::Lisp* const ROOT = parser.parse(m_filename);
const lisp::Lisp* const LISP = ROOT->getLisp("tuxkart-track");
if(!LISP)
{
delete ROOT;
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf(msg, sizeof(msg),
"Couldn't load map '%s': no tuxkart-track node.",
m_filename.c_str());
throw std::runtime_error(msg);
}
LISP->get ("name", m_name);
LISP->get ("description", m_description);
LISP->get ("designer", m_designer);
std::vector<std::string> filenames;
LISP->getVector("music", filenames);
getMusicInformation(filenames, m_music);
LISP->get ("herring", m_herring_style);
LISP->get ("screenshot", m_screenshot);
LISP->get ("topview", m_top_view);
LISP->get ("sky-color", m_sky_color);
LISP->getVector("start-x", m_start_x);
LISP->getVector("start-y", m_start_y);
LISP->getVector("start-z", m_start_z);
LISP->getVector("start-heading", m_start_heading);
LISP->get ("use-fog", m_use_fog);
LISP->get ("fog-color", m_fog_color);
LISP->get ("fog-density", m_fog_density);
LISP->get ("fog-start", m_fog_start);
LISP->get ("fog-end", m_fog_end);
LISP->get ("sun-position", m_sun_position);
LISP->get ("sun-ambient", m_ambient_col);
LISP->get ("sun-specular", m_specular_col);
LISP->get ("sun-diffuse", m_diffuse_col);
LISP->get ("gravity", m_gravity);
LISP->get ("AI-angle-adjust", m_AI_angle_adjustment);
LISP->get ("AI-curve-speed-adjust", m_AI_curve_speed_adjustment);
// Set the correct paths
m_screenshot = file_manager->getTrackFile(m_screenshot, getIdent());
m_top_view = file_manager->getTrackFile(m_top_view, getIdent());
delete ROOT;
} // loadTrack
//-----------------------------------------------------------------------------
void Track::getMusicInformation(std::vector<std::string>& filenames,
std::vector<MusicInformation*>& music )
{
for(int i=0; i<(int)filenames.size(); i++)
{
std::string full_path = file_manager->getTrackFile(filenames[i], getIdent());
MusicInformation* mi;
try
{
mi = sound_manager->getMusicInformation(full_path);
}
catch(std::runtime_error)
{
mi = sound_manager->getMusicInformation(file_manager->getMusicFile(filenames[i]));
}
if(!mi)
{
fprintf(stderr, "Music information file '%s' not found - ignored.\n",
filenames[i].c_str());
continue;
}
m_music.push_back(mi);
} // for i in filenames
} // getMusicInformation
//-----------------------------------------------------------------------------
void Track::startMusic() const {
sound_manager->startMusic(m_music[rand()% m_music.size()]);
} // startMusic
//-----------------------------------------------------------------------------
void
Track::loadDriveline()
{
readDrivelineFromFile(m_left_driveline, ".drvl");
const unsigned int DRIVELINE_SIZE = (unsigned int)m_left_driveline.size();
m_right_driveline.reserve(DRIVELINE_SIZE);
readDrivelineFromFile(m_right_driveline, ".drvr");
if(m_right_driveline.size() != m_left_driveline.size())
std::cout << "Error: driveline's sizes do not match, right " <<
"driveline is " << m_right_driveline.size() << " vertex long " <<
"and the left driveline is " << m_left_driveline.size()
<< " vertex long. Track is " << m_name << " ." << std::endl;
SGfloat width;
sgVec3 center_point, width_vector;
m_driveline.reserve(DRIVELINE_SIZE);
m_path_width.reserve(DRIVELINE_SIZE);
m_angle.reserve(DRIVELINE_SIZE);
for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
{
sgAddVec3(center_point, m_left_driveline[i], m_right_driveline[i]);
sgScaleVec3(center_point, 0.5f);
m_driveline.push_back(center_point);
sgSubVec3(width_vector, m_right_driveline[i], center_point);
width = sgLengthVec3(width_vector);
if(width > 0.0f) m_path_width.push_back(width);
else m_path_width.push_back(-width);
}
size_t next;
float adjacent_line, opposite_line;
SGfloat theta;
for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
{
next = i + 1 >= DRIVELINE_SIZE ? 0 : i + 1;
adjacent_line = m_driveline[next][0] - m_driveline[i][0];
opposite_line = m_driveline[next][1] - m_driveline[i][1];
theta = sgATan(opposite_line/adjacent_line);
theta += adjacent_line < 0.0f ? 90.0f : -90.0f;
m_angle.push_back(theta);
}
sgSetVec2 ( m_driveline_min, SG_MAX/2.0f, SG_MAX/2.0f ) ;
sgSetVec2 ( m_driveline_max, -SG_MAX/2.0f, -SG_MAX/2.0f ) ;
m_distance_from_start.reserve(DRIVELINE_SIZE);
float d = 0.0f ;
for ( size_t i = 0 ; i < DRIVELINE_SIZE ; ++i )
{
//Both drivelines must be checked to get the true size of
//the drivelines, and using the center driveline is not
//good enough.
if ( m_right_driveline[i][0] < m_driveline_min[0] )
m_driveline_min[0] = m_right_driveline[i][0] ;
if ( m_right_driveline[i][1] < m_driveline_min[1] )
m_driveline_min[1] = m_right_driveline[i][1] ;
if ( m_right_driveline[i][0] > m_driveline_max[0] )
m_driveline_max[0] = m_right_driveline[i][0] ;
if ( m_right_driveline[i][1] > m_driveline_max[1] )
m_driveline_max[1] = m_right_driveline[i][1] ;
if ( m_left_driveline[i][0] < m_driveline_min[0] )
m_driveline_min[0] = m_left_driveline[i][0] ;
if ( m_left_driveline[i][1] < m_driveline_min[1] )
m_driveline_min[1] = m_left_driveline[i][1] ;
if ( m_left_driveline[i][0] > m_driveline_max[0] )
m_driveline_max[0] = m_left_driveline[i][0] ;
if ( m_left_driveline[i][1] > m_driveline_max[1] )
m_driveline_max[1] = m_left_driveline[i][1] ;
m_distance_from_start.push_back(d); // dfs[i] is not valid in windows here!
if ( i == DRIVELINE_SIZE - 1 )
d += sgDistanceVec2 ( m_driveline[i], m_driveline[0] ) ;
else
d += sgDistanceVec2 ( m_driveline[i], m_driveline[i+1] ) ;
}
m_total_distance = d;
sgVec2 sc ;
sgSubVec2 ( sc, m_driveline_max, m_driveline_min ) ;
m_scale_x = m_track_2d_width / sc[0] ;
m_scale_y = m_track_2d_height / sc[1] ;
if(!m_do_stretch) m_scale_x = m_scale_y = std::min(m_scale_x, m_scale_y);
} // loadDriveline
//-----------------------------------------------------------------------------
void
Track::readDrivelineFromFile(std::vector<sgVec3Wrapper>& line, const std::string& file_ext)
{
std::string path = file_manager->getTrackFile(m_ident+file_ext);
FILE *fd = fopen ( path.c_str(), "r" ) ;
if ( fd == NULL )
{
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf (msg, sizeof(msg), "Can't open '%s' for reading.\n", path.c_str() ) ;
throw std::runtime_error(msg);
}
int prev_sector = UNKNOWN_SECTOR;
SGfloat prev_distance = 1.51f;
while(!feof(fd))
{
char s [ 1024 ] ;
if ( fgets ( s, 1023, fd ) == NULL )
break ;
if ( *s == '#' || *s < ' ' )
continue ;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if (sscanf ( s, "%f,%f,%f", &x, &y, &z ) != 3 )
{
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf (msg, sizeof(msg), "Syntax error in '%s'\n", path.c_str() ) ;
throw std::runtime_error(msg);
}
sgVec3 point;
point[0] = x;
point[1] = y;
point[2] = z;
if(prev_sector != UNKNOWN_SECTOR) prev_distance = sgDistanceVec2(
point, line[prev_sector] );
//1.5f was choosen because it's more or less the length of the tuxkart
if(prev_distance < 0.0000001)
{
fprintf(stderr, "File %s point %d is duplicated!.\n",
path.c_str(), prev_sector+1);
}
#if 0
else if(prev_distance < 1.5f)
{
fprintf(stderr,"File %s point %d is too close(<1.5) to previous point.\n",
path.c_str(), prev_sector + 1);
}
if(prev_distance > 15.0f)
{
fprintf(stderr,"In file %s point %d is too far(>15.0) from next point at %d.\n",
path, prev_sector, prev_distance);
}
#endif
line.push_back(point);
++prev_sector;
prev_distance -= 1.5f;
}
fclose ( fd ) ;
} // readDrivelineFromFile
// -----------------------------------------------------------------------------
//* Convert the ssg track tree into its physics equivalents.
void Track::createPhysicsModel()
{
if(!m_model) return;
m_track_mesh = new TriangleMesh();
m_non_collision_mesh = new TriangleMesh();
// Collect all triangles in the track_mesh
sgMat4 mat;
sgMakeIdentMat4(mat);
convertTrackToBullet(m_model, mat);
m_track_mesh->createBody();
m_non_collision_mesh->createBody(btCollisionObject::CF_NO_CONTACT_RESPONSE);
} // createPhysicsModel
// -----------------------------------------------------------------------------
//* Convert the ssg track tree into its physics equivalents.
void Track::convertTrackToBullet(ssgEntity *track, sgMat4 m)
{
if(!track) return;
MovingPhysics *mp = dynamic_cast<MovingPhysics*>(track);
if(mp)
{
// If the track contains obect of type MovingPhysics,
// these objects will be real rigid body and are already
// part of the world. So these objects must not be converted
// to triangle meshes.
}
else if(track->isAKindOf(ssgTypeLeaf()))
{
ssgLeaf *leaf = (ssgLeaf*)(track);
Material *material = material_manager->getMaterial(leaf);
// Don't convert triangles with material that is ignored (e.g. fuzzy_sand)
if(!material || material->isIgnore()) return;
for(int i=0; i<leaf->getNumTriangles(); i++)
{
short v1,v2,v3;
sgVec3 vv1, vv2, vv3;
leaf->getTriangle(i, &v1, &v2, &v3);
sgXformPnt3 ( vv1, leaf->getVertex(v1), m );
sgXformPnt3 ( vv2, leaf->getVertex(v2), m );
sgXformPnt3 ( vv3, leaf->getVertex(v3), m );
btVector3 vb1(vv1[0],vv1[1],vv1[2]);
btVector3 vb2(vv2[0],vv2[1],vv2[2]);
btVector3 vb3(vv3[0],vv3[1],vv3[2]);
if(material->isZipper())
{
m_non_collision_mesh->addTriangle(vb1, vb2, vb3, material);
}
else
{
m_track_mesh->addTriangle(vb1, vb2, vb3, material);
}
}
} // if(track isAKindOf leaf)
else if(track->isAKindOf(ssgTypeTransform()))
{
ssgBaseTransform *t = (ssgBaseTransform*)(track);
sgMat4 tmpT, tmpM;
t->getTransform(tmpT);
sgCopyMat4(tmpM, m);
sgPreMultMat4(tmpM,tmpT);
for(ssgEntity *e = t->getKid(0); e!=NULL; e=t->getNextKid())
{
convertTrackToBullet(e, tmpM);
} // for i
}
else if (track->isAKindOf(ssgTypeBranch()))
{
ssgBranch *b =(ssgBranch*)track;
for(ssgEntity* e=b->getKid(0); e!=NULL; e=b->getNextKid()) {
convertTrackToBullet(e, m);
} // for i<getNumKids
}
else
{
assert(!"Unkown ssg type in convertTrackToBullet");
}
} // convertTrackToBullet
// ----------------------------------------------------------------------------
void Track::loadTrackModel()
{
// Add the track directory to the texture search path
file_manager->pushTextureSearchPath(file_manager->getTrackFile("",getIdent()));
file_manager->pushModelSearchPath (file_manager->getTrackFile("",getIdent()));
// First read the temporary materials.dat file if it exists
try
{
std::string materials_file = file_manager->getTrackFile("materials.dat",getIdent());
material_manager->pushTempMaterial(materials_file);
}
catch (std::exception& e)
{
// no temporary materials.dat file, ignore
(void)e;
}
std::string path = file_manager->getTrackFile(getIdent()+".loc");
FILE *fd = fopen (path.c_str(), "r" );
if ( fd == NULL )
{
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf(msg, sizeof(msg),"Can't open track location file '%s'.\n",
path.c_str());
throw std::runtime_error(msg);
}
// Start building the scene graph
m_model = new ssgBranch ;
scene->add(m_model);
char s [ 1024 ] ;
while ( fgets ( s, 1023, fd ) != NULL )
{
if ( *s == '#' || *s < ' ' )
continue ;
int need_hat = false ;
int fit_skin = false ;
char fname [ 1024 ] ;
sgCoord loc ;
sgZeroVec3 ( loc.xyz ) ;
sgZeroVec3 ( loc.hpr ) ;
char htype = '\0' ;
if ( sscanf ( s, "%cHERRING,%f,%f,%f", &htype,
&(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]) ) == 4 )
{
herring_command(&loc.xyz, htype, false) ;
}
else if ( sscanf ( s, "%cHERRING,%f,%f", &htype,
&(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
{
herring_command (&loc.xyz, htype, true) ;
}
else if ( s[0] == '\"' )
{
if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f,%f,%f,%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]),
&(loc.hpr[0]), &(loc.hpr[1]), &(loc.hpr[2]) ) == 7 )
{
/* All 6 DOF specified */
need_hat = false;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f,%f,%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]),
&(loc.hpr[0]), &(loc.hpr[1]), &(loc.hpr[2])) == 6 )
{
/* All 6 DOF specified - but need height */
need_hat = true ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f,%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]), &(loc.xyz[2]),
&(loc.hpr[0]) ) == 5 )
{
/* No Roll/Pitch specified - assumed zero */
need_hat = false ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f,{},{}",
fname, &(loc.xyz[0]), &(loc.xyz[1]),
&(loc.hpr[0]) ) == 4 )
{
/* All 6 DOF specified - but need height, roll, pitch */
need_hat = true ;
fit_skin = true ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{},%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]),
&(loc.hpr[0]) ) == 4 )
{
/* No Roll/Pitch specified - but need height */
need_hat = true ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]),
&(loc.xyz[2]) ) == 4 )
{
/* No Heading/Roll/Pitch specified - but need height */
need_hat = false ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f,{}",
fname, &(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
{
/* No Roll/Pitch specified - but need height */
need_hat = true ;
}
else if ( sscanf ( s, "\"%[^\"]\",%f,%f",
fname, &(loc.xyz[0]), &(loc.xyz[1]) ) == 3 )
{
/* No Z/Heading/Roll/Pitch specified */
need_hat = false ;
}
else if ( sscanf ( s, "\"%[^\"]\"", fname ) == 1 )
{
/* Nothing specified */
need_hat = false ;
}
else
{
fclose(fd);
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf(msg, sizeof(msg), "Syntax error in '%s': %s",
path.c_str(), s);
throw std::runtime_error(msg);
}
if ( need_hat )
{
sgVec3 nrm ;
loc.xyz[2] = 1000.0f ;
loc.xyz[2] = getHeightAndNormal ( m_model, loc.xyz, nrm ) ;
if ( fit_skin )
{
float sy = sin ( -loc.hpr [ 0 ] * SG_DEGREES_TO_RADIANS ) ;
float cy = cos ( -loc.hpr [ 0 ] * SG_DEGREES_TO_RADIANS ) ;
loc.hpr[2] = SG_RADIANS_TO_DEGREES * atan2 ( nrm[0] * cy -
nrm[1] * sy, nrm[2] ) ;
loc.hpr[1] = -SG_RADIANS_TO_DEGREES * atan2 ( nrm[1] * cy +
nrm[0] * sy, nrm[2] ) ;
}
} // if need_hat
ssgEntity *obj = loader->load(file_manager->getModelFile(fname),
CB_TRACK,
/* optimise */ true,
/*is_full_path*/ true);
if(!obj)
{
fclose(fd);
char msg[MAX_ERROR_MESSAGE_LENGTH];
snprintf(msg, sizeof(msg), "Can't open track model '%s'",fname);
file_manager->popTextureSearchPath();
file_manager->popModelSearchPath ();
throw std::runtime_error(msg);
}
createDisplayLists(obj);
ssgRangeSelector *lod = new ssgRangeSelector ;
ssgTransform *trans = new ssgTransform ( & loc ) ;
float r [ 2 ] = { -10.0f, 2000.0f } ;
lod -> addKid(obj );
trans -> addKid(lod );
m_model-> addKid(trans );
lod -> setRanges(r, 2);
if(user_config->m_track_debug)
addDebugToScene(user_config->m_track_debug);
}
else
{
// fclose(fd);
// char msg[MAX_ERROR_MESSAGE_LENGTH];
// snprintf(msg, sizeof(msg), "Syntax error in '%s': %s",
fprintf(stderr, "Warning: Syntax error in '%s': %s",
path.c_str(), s);
// throw std::runtime_error(msg);
}
} // while fgets
fclose ( fd ) ;
file_manager->popTextureSearchPath();
file_manager->popModelSearchPath ();
createPhysicsModel();
} // loadTrack
//-----------------------------------------------------------------------------
void Track::herring_command (sgVec3 *xyz, char htype, int bNeedHeight )
{
// if only 2d coordinates are given, let the herring fall from very heigh
if(bNeedHeight) (*xyz)[2] = 1000000.0f;
// Even if 3d data are given, make sure that the herring is on the ground
(*xyz)[2] = getHeight ( m_model, *xyz ) + 0.06f;
herringType type=HE_GREEN;
if ( htype=='Y' || htype=='y' ) { type = HE_GOLD ;}
if ( htype=='G' || htype=='g' ) { type = HE_GREEN ;}
if ( htype=='R' || htype=='r' ) { type = HE_RED ;}
if ( htype=='S' || htype=='s' ) { type = HE_SILVER ;}
// Time trial does not have any red herrings
if(type==HE_RED && race_manager->getRaceMode()==RaceManager::RM_TIME_TRIAL)
return;
herring_manager->newHerring(type, xyz);
} // herring_command
// ----------------------------------------------------------------------------
void Track::getTerrainInfo(const btVector3 &pos, float *hot, btVector3* normal,
const Material **material) const
{
btVector3 to_pos(pos);
to_pos.setZ(-100000.f);
class MaterialCollision : public btCollisionWorld::ClosestRayResultCallback
{
public:
const Material* m_material;
MaterialCollision(btVector3 p1, btVector3 p2) :
btCollisionWorld::ClosestRayResultCallback(p1,p2) {m_material=NULL;}
virtual btScalar AddSingleResult(btCollisionWorld::LocalRayResult& rayResult,
bool normalInWorldSpace) {
if(rayResult.m_localShapeInfo && rayResult.m_localShapeInfo->m_shapePart>=0 )
{
m_material = ((TriangleMesh*)rayResult.m_collisionObject->getUserPointer())->getMaterial(rayResult.m_localShapeInfo->m_triangleIndex);
}
return btCollisionWorld::ClosestRayResultCallback::AddSingleResult(rayResult,
normalInWorldSpace);
} // AddSingleResult
}; // myCollision
MaterialCollision rayCallback(pos, to_pos);
world->getPhysics()->getPhysicsWorld()->rayTest(pos, to_pos, rayCallback);
if(!rayCallback.HasHit())
{
*hot = NOHIT;
*material = NULL;
return;
}
*hot = rayCallback.m_hitPointWorld.getZ();
*normal = rayCallback.m_hitNormalWorld;
*material = rayCallback.m_material;
// Note: material might be NULL. This happens if the ray cast does not
// hit the track, but anoterh rigid body (kart, moving_physics) - e.g.
// assume two karts falling down, one over the other. Bullet does not
// have any triangle/material information in this case!
} // getTerrainInfo