1) More redesign of drivelines into quad graphs. Moved
graph related functions from Track to QuadGraph, removed plib usage. The AI is now able to drive a loop (re-using the same quad, before continuing on the original path). The beack track currently defines a simple look at the first right hand turn (the AI will drive down the hill, then up again and then keep on going). 2) During the conversion process some AI called function were significanly sped up, but it has to be tested if this has any ill effects for certain tracks or certain karts. 3) Tried to add support for moving sky domes, but that doesn't work yet. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/irrlicht@3527 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
6042c2cb63
commit
864c1d6fff
@ -220,6 +220,8 @@ supertuxkart_SOURCES = \
|
||||
physics/triangle_mesh.hpp \
|
||||
robots/default_robot.cpp \
|
||||
robots/default_robot.hpp \
|
||||
tracks/graph_node.hpp\
|
||||
tracks/graph_node.cpp\
|
||||
tracks/quad.hpp \
|
||||
tracks/quad.cpp \
|
||||
tracks/quad_graph.hpp \
|
||||
|
@ -20,6 +20,11 @@
|
||||
#include "graphics/moving_texture.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
|
||||
/** Constructor for an animated texture.
|
||||
* \param matrix The texture matrix to modify.
|
||||
* \param node An XML node containing dx and dy attributes to set the
|
||||
* speed of the animation.
|
||||
*/
|
||||
MovingTexture::MovingTexture(core::matrix4 *matrix, const XMLNode &node)
|
||||
: m_matrix(matrix)
|
||||
{
|
||||
@ -34,11 +39,33 @@ MovingTexture::MovingTexture(core::matrix4 *matrix, const XMLNode &node)
|
||||
} // MovingTexture
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Constructor for an animated texture, specifying the speed of the animation
|
||||
* directly.
|
||||
* \param matrix The texture matrix to modify.
|
||||
* \param dx Speed of the animation in X direction.
|
||||
* \param dy Speed of the animation in Y direction.
|
||||
*/
|
||||
MovingTexture::MovingTexture(core::matrix4 *matrix, float dx, float dy)
|
||||
: m_matrix(matrix)
|
||||
{
|
||||
m_dx = dx;
|
||||
m_dy = dy;
|
||||
core::vector3df v = m_matrix->getTranslation();
|
||||
m_x = v.X;
|
||||
m_y = v.Y;
|
||||
} // Moving Texture
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Destructor for an animated texture.
|
||||
*/
|
||||
MovingTexture::~MovingTexture()
|
||||
{
|
||||
} // ~MovingTexture
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates the transform of an animated texture.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void MovingTexture::update(float dt)
|
||||
{
|
||||
m_x = m_x + dt*m_dx;
|
||||
|
@ -38,6 +38,7 @@ private:
|
||||
|
||||
public:
|
||||
MovingTexture(core::matrix4 *matrix, const XMLNode &node);
|
||||
MovingTexture(core::matrix4 *matrix, float dx, float dy);
|
||||
~MovingTexture();
|
||||
void update (float dt);
|
||||
void init () {};
|
||||
|
@ -608,6 +608,10 @@
|
||||
<Filter
|
||||
Name="tracks"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\tracks\graph_node.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\tracks\quad.cpp"
|
||||
>
|
||||
@ -1206,6 +1210,10 @@
|
||||
<Filter
|
||||
Name="tracks"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\tracks\graph_node.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\tracks\quad.hpp"
|
||||
>
|
||||
|
@ -194,6 +194,15 @@ int XMLNode::get(const std::string &attribute, int *value) const
|
||||
return 1;
|
||||
} // get(int)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
int XMLNode::get(const std::string &attribute, unsigned int *value) const
|
||||
{
|
||||
std::string s;
|
||||
if(!get(attribute, &s)) return 0;
|
||||
*value = atoi(s.c_str());
|
||||
return 1;
|
||||
} // get(unsigned int)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
int XMLNode::get(const std::string &attribute, float *value) const
|
||||
{
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
unsigned int getNumNodes() const {return m_nodes.size(); }
|
||||
int get(const std::string &attribute, std::string *value) const;
|
||||
int get(const std::string &attribute, int *value) const;
|
||||
int get(const std::string &attribute, unsigned int *value) const;
|
||||
int get(const std::string &attribute, float *value) const;
|
||||
int get(const std::string &attribute, bool *value) const;
|
||||
int get(const std::string &attribute, Vec3 *value) const;
|
||||
|
@ -610,7 +610,7 @@ void Kart::update(float dt)
|
||||
{
|
||||
// let kart fall a bit before rescuing
|
||||
if(fabs( getXYZ().getZ()
|
||||
-RaceManager::getTrack()->getQuadGraph().getCenterOfQuad(0).getZ() ) > 17)
|
||||
-RaceManager::getTrack()->getQuadGraph().getQuad(0).getCenter().getZ() ) > 17)
|
||||
forceRescue();
|
||||
}
|
||||
else if(material)
|
||||
|
@ -43,24 +43,25 @@ void LinearWorld::init()
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
KartInfo info;
|
||||
info.m_track_sector = Track::UNKNOWN_SECTOR;
|
||||
info.m_last_valid_sector = Track::UNKNOWN_SECTOR;
|
||||
info.m_track_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
info.m_last_valid_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
info.m_last_valid_race_lap = -1;
|
||||
info.m_lap_start_time = -1.0f;
|
||||
m_track->findRoadSector(m_kart[n]->getXYZ(), &info.m_track_sector);
|
||||
m_track->getQuadGraph().findRoadSector(m_kart[n]->getXYZ(),
|
||||
&info.m_track_sector);
|
||||
|
||||
//If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of
|
||||
//the road, so we have to use another function to find the sector.
|
||||
info.m_on_road = info.m_track_sector != Track::UNKNOWN_SECTOR;
|
||||
info.m_on_road = info.m_track_sector != QuadGraph::UNKNOWN_SECTOR;
|
||||
if (!info.m_on_road)
|
||||
{
|
||||
info.m_track_sector =
|
||||
m_track->findOutOfRoadSector(m_kart[n]->getXYZ(),
|
||||
Track::RS_DONT_KNOW,
|
||||
Track::UNKNOWN_SECTOR );
|
||||
QuadGraph::UNKNOWN_SECTOR );
|
||||
}
|
||||
|
||||
m_track->spatialToTrack(info.m_curr_track_coords,
|
||||
m_track->getQuadGraph().spatialToTrack(&info.m_curr_track_coords,
|
||||
m_kart[n]->getXYZ(),
|
||||
info.m_track_sector );
|
||||
|
||||
@ -94,24 +95,24 @@ void LinearWorld::restartRace()
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
KartInfo& info = m_kart_info[n];
|
||||
info.m_track_sector = Track::UNKNOWN_SECTOR;
|
||||
info.m_last_valid_sector = Track::UNKNOWN_SECTOR;
|
||||
info.m_track_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
info.m_last_valid_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
info.m_lap_start_time = -1.0f;
|
||||
m_track->findRoadSector(m_kart[n]->getXYZ(),
|
||||
m_track->getQuadGraph().findRoadSector(m_kart[n]->getXYZ(),
|
||||
&info.m_track_sector);
|
||||
|
||||
//If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of
|
||||
//the road, so we have to use another function to find the sector.
|
||||
info.m_on_road = info.m_track_sector != Track::UNKNOWN_SECTOR;
|
||||
info.m_on_road = info.m_track_sector != QuadGraph::UNKNOWN_SECTOR;
|
||||
if (!info.m_on_road)
|
||||
{
|
||||
info.m_track_sector =
|
||||
m_track->findOutOfRoadSector(m_kart[n]->getXYZ(),
|
||||
Track::RS_DONT_KNOW,
|
||||
Track::UNKNOWN_SECTOR );
|
||||
QuadGraph::UNKNOWN_SECTOR );
|
||||
}
|
||||
|
||||
m_track->spatialToTrack(info.m_curr_track_coords,
|
||||
m_track->getQuadGraph().spatialToTrack(&info.m_curr_track_coords,
|
||||
m_kart[n]->getXYZ(),
|
||||
info.m_track_sector );
|
||||
// This assignmet is important, otherwise (depending on previous
|
||||
@ -149,11 +150,12 @@ void LinearWorld::update(float delta)
|
||||
|
||||
// update sector variables
|
||||
int prev_sector = kart_info.m_track_sector;
|
||||
m_track->findRoadSector( kart->getXYZ(), &kart_info.m_track_sector);
|
||||
m_track->getQuadGraph().findRoadSector(kart->getXYZ(),
|
||||
&kart_info.m_track_sector);
|
||||
|
||||
// Check if the kart is taking a shortcut (if it's not already doing one):
|
||||
// -----------------------------------------------------------------------
|
||||
kart_info.m_on_road = kart_info.m_track_sector != Track::UNKNOWN_SECTOR;
|
||||
kart_info.m_on_road = kart_info.m_track_sector != QuadGraph::UNKNOWN_SECTOR;
|
||||
if(!kart_info.m_on_road)
|
||||
{
|
||||
// Kart off road. Find the closest sector instead.
|
||||
@ -167,7 +169,7 @@ void LinearWorld::update(float delta)
|
||||
|
||||
// Update track coords (=progression)
|
||||
m_kart_info[n].m_last_track_coords = m_kart_info[n].m_curr_track_coords;
|
||||
m_track->spatialToTrack(kart_info.m_curr_track_coords,
|
||||
m_track->getQuadGraph().spatialToTrack(&kart_info.m_curr_track_coords,
|
||||
kart->getXYZ(),
|
||||
kart_info.m_track_sector );
|
||||
|
||||
|
@ -66,6 +66,7 @@ DefaultRobot::DefaultRobot(const std::string& kart_name,
|
||||
next.clear();
|
||||
m_quad_graph->getSuccessors(i, next);
|
||||
int indx = rand() % next.size();
|
||||
indx = next.size()-1;
|
||||
m_successor_index.push_back(indx);
|
||||
m_next_quad_index.push_back(next[indx]);
|
||||
}
|
||||
@ -152,7 +153,7 @@ void DefaultRobot::update(float dt)
|
||||
|
||||
// This should not happen (anymore :) ), but it keeps the game running
|
||||
// in case that m_future_sector becomes undefined.
|
||||
if(m_future_sector == Track::UNKNOWN_SECTOR )
|
||||
if(m_future_sector == QuadGraph::UNKNOWN_SECTOR)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"DefaultRobot: m_future_sector is undefined.\n");
|
||||
@ -245,7 +246,7 @@ void DefaultRobot::handleBraking()
|
||||
return;
|
||||
}
|
||||
|
||||
const float MIN_SPEED = m_track->getWidth()[m_track_sector];
|
||||
const float MIN_SPEED = m_track->getQuadGraph().getNode(m_track_sector).getPathWidth();
|
||||
KartInfo &kart_info = m_world->m_kart_info[ getWorldKartId() ];
|
||||
//We may brake if we are about to get out of the road, but only if the
|
||||
//kart is on top of the road, and if we won't slow down below a certain
|
||||
@ -269,7 +270,8 @@ void DefaultRobot::handleBraking()
|
||||
//if the curve angle is bigger than what the kart can steer, brake
|
||||
//even if we are in the inside, because the kart would be 'thrown'
|
||||
//out of the curve.
|
||||
if(!(m_world->getDistanceToCenterForKart(getWorldKartId()) > m_track->getWidth()[m_track_sector] *
|
||||
if(!(m_world->getDistanceToCenterForKart(getWorldKartId())
|
||||
> m_track->getQuadGraph().getNode(m_track_sector).getPathWidth() *
|
||||
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE(getMaxSteerAngle())))
|
||||
{
|
||||
m_controls.m_brake = false;
|
||||
@ -278,7 +280,8 @@ void DefaultRobot::handleBraking()
|
||||
}
|
||||
else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
|
||||
{
|
||||
if(!(m_world->getDistanceToCenterForKart( getWorldKartId() ) < m_track->getWidth()[m_track_sector] *
|
||||
if(!(m_world->getDistanceToCenterForKart( getWorldKartId() )
|
||||
< m_track->getQuadGraph().getNode(m_track_sector).getPathWidth() *
|
||||
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE(getMaxSteerAngle())))
|
||||
{
|
||||
m_controls.m_brake = false;
|
||||
@ -316,9 +319,10 @@ void DefaultRobot::handleSteering(float dt)
|
||||
*/
|
||||
//Reaction to being outside of the road
|
||||
if( fabsf(m_world->getDistanceToCenterForKart( getWorldKartId() )) + 0.5f >
|
||||
m_track->getWidth()[m_track_sector] )
|
||||
m_track->getQuadGraph().getNode(m_track_sector).getPathWidth() )
|
||||
{
|
||||
steer_angle = steerToPoint(m_quad_graph->getCenterOfQuad(next), dt );
|
||||
steer_angle = steerToPoint(m_quad_graph->getQuad(next).getCenter(),
|
||||
dt );
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
std::cout << "- Outside of road: steer to center point." <<
|
||||
@ -840,7 +844,8 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
}
|
||||
|
||||
/*Find if we crash with the drivelines*/
|
||||
m_track->findRoadSector(step_coord, &m_sector);
|
||||
m_track->getQuadGraph().findRoadSector(step_coord, &m_sector,
|
||||
/* max look ahead */ 10);
|
||||
|
||||
#undef SHOW_FUTURE_PATH
|
||||
#ifdef SHOW_FUTURE_PATH
|
||||
@ -861,7 +866,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
center[2] = pos[2];
|
||||
sphere->setCenter( center );
|
||||
sphere->setSize( m_kart_properties->getKartModel()->getLength() );
|
||||
if( m_sector == Track::UNKNOWN_SECTOR )
|
||||
if( m_sector == QuadGraph::UNKNOWN_SECTOR ))
|
||||
{
|
||||
sgVec4 colour;
|
||||
colour[0] = colour[3] = 255;
|
||||
@ -880,7 +885,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
|
||||
m_future_location = Vec3( step_coord[0], step_coord[1], 0 );
|
||||
|
||||
if( m_sector == Track::UNKNOWN_SECTOR )
|
||||
if( m_sector == QuadGraph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_future_sector = m_track->findOutOfRoadSector( step_coord,
|
||||
Track::RS_DONT_KNOW, m_future_sector );
|
||||
@ -920,7 +925,7 @@ void DefaultRobot::findNonCrashingPoint(Vec3 *result)
|
||||
target_sector = m_next_quad_index[sector];
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = m_quad_graph->getCenterOfQuad(target_sector) - getXYZ();
|
||||
direction = m_quad_graph->getQuad(target_sector).getCenter() - getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
steps = int( len / m_kart_length );
|
||||
@ -937,16 +942,17 @@ void DefaultRobot::findNonCrashingPoint(Vec3 *result)
|
||||
{
|
||||
step_coord = getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
m_track->spatialToTrack( step_track_coord, step_coord,
|
||||
m_track->getQuadGraph().spatialToTrack(&step_track_coord, step_coord,
|
||||
sector );
|
||||
|
||||
distance = step_track_coord[0] > 0.0f ? step_track_coord[0]
|
||||
: -step_track_coord[0];
|
||||
|
||||
//If we are outside, the previous sector is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f > m_track->getWidth()[sector] )
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> m_track->getQuadGraph().getNode(sector).getPathWidth() )
|
||||
{
|
||||
*result = m_quad_graph->getCenterOfQuad(sector);
|
||||
*result = m_quad_graph->getQuad(sector).getCenter();
|
||||
|
||||
#ifdef SHOW_NON_CRASHING_POINT
|
||||
ssgaSphere *sphere = new ssgaSphere;
|
||||
@ -984,7 +990,7 @@ void DefaultRobot::reset()
|
||||
{
|
||||
m_time_since_last_shot = 0.0f;
|
||||
m_start_kart_crash_direction = 0;
|
||||
m_sector = Track::UNKNOWN_SECTOR;
|
||||
m_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
m_inner_curve = 0;
|
||||
m_curve_target_speed = getMaxSpeedOnTerrain();
|
||||
m_curve_angle = 0.0;
|
||||
@ -1029,7 +1035,7 @@ int DefaultRobot::calcSteps()
|
||||
if( fabsf(m_controls.m_steer) > 0.95 )
|
||||
{
|
||||
const int WIDTH_STEPS =
|
||||
(int)( m_track->getWidth()[m_future_sector]
|
||||
(int)( m_track->getQuadGraph().getNode(m_future_sector).getPathWidth()
|
||||
/( m_kart_length * 2.0 ) );
|
||||
|
||||
steps += WIDTH_STEPS;
|
||||
|
104
src/tracks/graph_node.cpp
Executable file
104
src/tracks/graph_node.cpp
Executable file
@ -0,0 +1,104 @@
|
||||
// $Id$
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 Joerg Henrichs
|
||||
//
|
||||
// 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 3
|
||||
// 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, B
|
||||
|
||||
#include "tracks/quad_graph.hpp"
|
||||
|
||||
#include "user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
|
||||
|
||||
// A static variable that gives a single graph node easy access to
|
||||
// all quads and avoids unnecessary parameters in many calls.
|
||||
QuadSet *GraphNode::m_all_quads=NULL;
|
||||
|
||||
// This static variable gives a node access to the graph, and therefore
|
||||
// to the quad to which a graph node index belongs.
|
||||
QuadGraph *GraphNode::m_all_nodes=NULL;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. Saves the quad index which belongs to this graph node.
|
||||
* \param index Index of the quad to use for this node (in m_all_quads).
|
||||
*/
|
||||
GraphNode::GraphNode(unsigned int index)
|
||||
{
|
||||
assert(index<m_all_quads->getNumberOfQuads());
|
||||
m_index = index;
|
||||
m_distance_from_start = 0;
|
||||
const Quad &quad = m_all_quads->getQuad(m_index);
|
||||
// FIXME: those two values should probably depend on the actual
|
||||
// orientation of the quad.
|
||||
// The width is the average width at the beginning and at the end.
|
||||
m_width = ( (quad[1]-quad[0]).length()
|
||||
+ (quad[3]-quad[2]).length() ) * 0.5f;
|
||||
Vec3 lower = (quad[m_index][0]+quad[m_index][1]) * 0.5f;
|
||||
Vec3 upper = (quad[m_index][2]+quad[m_index][3]) * 0.5f;
|
||||
m_line = core::line2df(lower.getX(), lower.getY(),
|
||||
upper.getX(), upper.getY() );
|
||||
// Only this 2d point is needed later
|
||||
m_lower_center = core::vector2df(lower.getX(), lower.getY());
|
||||
} // GraphNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Adds a successor to a node. This function will also pre-compute certain
|
||||
* values (like distance from this node to the successor, angle (in world)
|
||||
* between this node and the successor.
|
||||
* \param to The index of the successor.
|
||||
*/
|
||||
void GraphNode::addSuccessor(unsigned int to)
|
||||
{
|
||||
m_vertices.push_back(to);
|
||||
// m_index is the quad index, so we use m_all_quads
|
||||
const Quad &this_quad = m_all_quads->getQuad(m_index);
|
||||
// to is the graph node, so we have to use m_all_nodes to get the right quad
|
||||
const Quad &next_quad = m_all_nodes->getQuad(to);
|
||||
|
||||
Vec3 diff = next_quad.getCenter() - this_quad.getCenter();
|
||||
m_distance_to_next.push_back(diff.length());
|
||||
|
||||
float theta = -atan2(diff.getX(), diff.getY());
|
||||
m_angle_to_next.push_back(theta);
|
||||
|
||||
// The length of this quad is the average of the left and right side
|
||||
float distance_to_next = ( (this_quad[2]-this_quad[1]).length()
|
||||
+ (this_quad[3]-this_quad[0]).length() ) *0.5f;
|
||||
// The distance from start for the successor node
|
||||
m_all_nodes->getNode(to).m_distance_from_start =
|
||||
std::max(m_all_nodes->getNode(to).m_distance_from_start,
|
||||
m_distance_from_start+distance_to_next);
|
||||
} // addSuccessor
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the distance a point has from this quad in forward and sidewards
|
||||
* direction, i.e. how far forwards the point is from the beginning of the
|
||||
* quad, and how far to the side from the line connecting the center points
|
||||
* is it. All these computations are done in 2D only.
|
||||
* \param xyz The coordinates of the point.
|
||||
* \param result The X coordinate contains the sidewards distance, the
|
||||
* y coordinate the forward distance.
|
||||
*/
|
||||
void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result)
|
||||
{
|
||||
core::vector2df xyz2d(xyz.getX(), xyz.getY());
|
||||
core::vector2df closest = m_line.getClosestPoint(xyz2d);
|
||||
result->setX( (closest-xyz2d).getLength());
|
||||
result->setY( (closest-m_lower_center).getLength());
|
||||
} // getDistanceFromLine
|
114
src/tracks/graph_node.hpp
Executable file
114
src/tracks/graph_node.hpp
Executable file
@ -0,0 +1,114 @@
|
||||
// $Id$
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 Joerg Henrichs
|
||||
//
|
||||
// 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 3
|
||||
// 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, B
|
||||
|
||||
#ifndef HEADER_GRAPH_NODE_HPP
|
||||
#define HEADER_GRAPH_NODE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "irrlicht.h"
|
||||
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
class QuadSet;
|
||||
class QuadGraph;
|
||||
|
||||
/** This class stores a node of the graph, i.e. a list of successor
|
||||
* edges.
|
||||
*/
|
||||
class GraphNode
|
||||
{
|
||||
/** Index of this node in the set of quads. Several graph nodes can use
|
||||
* the same quad, meaning it is possible to use a quad more than once,
|
||||
* e.g. a figure 8 like track. */
|
||||
unsigned int m_index;
|
||||
/** The list of successors. */
|
||||
std::vector<int> m_vertices;
|
||||
/** The distance to each of the successors. */
|
||||
std::vector<float> m_distance_to_next;
|
||||
/** The angle of the line from this node to each neighbour. */
|
||||
std::vector<float> m_angle_to_next;
|
||||
|
||||
/** Distance from the start to the beginning of this quad. */
|
||||
float m_distance_from_start;
|
||||
|
||||
/** Width of the track, which is the average of the width at the
|
||||
* beginning and at the end. FIXME: for now the width is independent
|
||||
* of the orientation (e.g. a quad used more than once might once
|
||||
* be used from top to bottom, one from left to right, so it should
|
||||
* have a different width then). */
|
||||
float m_width;
|
||||
|
||||
/** The center point of the lower two points (e.g. points 0 and 1).
|
||||
* This saves some computations in getDistances later. Only the
|
||||
* start point is needed, and only in 2d.
|
||||
* FIXME: this should be set depending on orientation, e.g. a quad
|
||||
* might be driven on from the left to the right (esp. if a quad is
|
||||
* used more than once). */
|
||||
core::vector2df m_lower_center;
|
||||
|
||||
/** Line between lower and upper center, saves computation in
|
||||
* getDistanceFromLine() later. The line is 2d only since otherwise
|
||||
* taller karts would have a larger distance from the center. It also
|
||||
* saves computation, and it is only needed to determine the distance
|
||||
* from the center of the drivelines anyway. */
|
||||
core::line2df m_line;
|
||||
public:
|
||||
/** Keep a shared pointer so that some asserts and tests can be
|
||||
* done without adding additional parameters. */
|
||||
static QuadSet *m_all_quads;
|
||||
/** Keep a shared pointer to the graph structure so that each node
|
||||
* has access to the actual quad to which a node points. */
|
||||
static QuadGraph *m_all_nodes;
|
||||
|
||||
GraphNode(unsigned int index);
|
||||
void addSuccessor (unsigned int to);
|
||||
void getDistances(const Vec3 &xyz, Vec3 *result);
|
||||
|
||||
/** Returns the i-th successor. */
|
||||
unsigned int getSuccessor(unsigned int i) const
|
||||
{ return m_vertices[i]; }
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the number of successors. */
|
||||
unsigned int getNumberOfSuccessors() const
|
||||
{ return (unsigned int)m_vertices.size(); }
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the index in the quad_set of this node. */
|
||||
int getIndex() const { return m_index; }
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the distance to the j-th. successor. */
|
||||
float getDistanceToSuccessor(unsigned int j) const
|
||||
{ return m_distance_to_next[j]; }
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the angle from this node to the j-th. successor. */
|
||||
float getAngleToSuccessor(unsigned int j) const
|
||||
{ return m_angle_to_next[j]; }
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the distance from start. */
|
||||
float getDistanceFromStart() const
|
||||
{ return m_distance_from_start; }
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns the width of the part for this quad. */
|
||||
float getPathWidth() const { return m_width; }
|
||||
}; // GraphNode
|
||||
|
||||
#endif
|
@ -26,6 +26,8 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
|
||||
{
|
||||
m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3;
|
||||
m_center = 0.25f*(p0+p1+p2+p3);
|
||||
m_min_height = std::min ( std::min(p0.getZ(), p1.getZ()),
|
||||
std::min(p0.getZ(), p1.getZ()) );
|
||||
} // Quad
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -63,5 +65,28 @@ void Quad::setVertices(video::S3DVertex *v, const video::SColor &color) const
|
||||
v[3].Color = color;
|
||||
} // setVertices
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Returns wether a point is to the left or to the right of a line.
|
||||
* While all arguments are 3d, only the x and y coordinates are actually used.
|
||||
*/
|
||||
float Quad::sideOfLine2D(const Vec3& l1, const Vec3& l2, const Vec3& p) const
|
||||
{
|
||||
return (l2.getX()-l1.getX())*(p.getY()-l1.getY()) -
|
||||
(l2.getY()-l1.getY())*(p.getX()-l1.getX());
|
||||
} // sideOfLine
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool Quad::pointInQuad(const Vec3& p) const
|
||||
{
|
||||
if(sideOfLine2D(m_p[0], m_p[2], p)<0) {
|
||||
return sideOfLine2D(m_p[0], m_p[1], p) > 0.0 &&
|
||||
sideOfLine2D(m_p[1], m_p[2], p) >= 0.0;
|
||||
} else {
|
||||
return sideOfLine2D(m_p[2], m_p[3], p) > 0.0 &&
|
||||
sideOfLine2D(m_p[3], m_p[0], p) >= 0.0;
|
||||
}
|
||||
} // pointInQuad
|
||||
|
||||
|
||||
#include "quad.hpp"
|
||||
|
||||
|
@ -26,19 +26,34 @@
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
class Quad {
|
||||
public:
|
||||
class Quad
|
||||
{
|
||||
private:
|
||||
/** The four points of a quad. */
|
||||
Vec3 m_p[4];
|
||||
/** The center, which is used by the AI. This saves some
|
||||
computations at runtime. */
|
||||
|
||||
/** The center of all four points, which is used by the AI.
|
||||
* This saves some computations at runtime. */
|
||||
Vec3 m_center;
|
||||
|
||||
/** The minimum height of the quad, used in case that several quads
|
||||
* are on top of each other when determining the sector a kart is on. */
|
||||
float m_min_height;
|
||||
float sideOfLine2D(const Vec3& l1, const Vec3& l2, const Vec3& p) const;
|
||||
|
||||
public:
|
||||
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
|
||||
void setVertices(video::S3DVertex *v, const video::SColor &color) const;
|
||||
bool pointInQuad(const Vec3& p) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the i-th. point of a quad. */
|
||||
const Vec3& operator[](int i) const {return m_p[i]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center of a quad. */
|
||||
const Vec3& getCenter () const {return m_center; }
|
||||
void setVertices(video::S3DVertex *v, const video::SColor &color) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the minimum height of a quad. */
|
||||
float getMinHeight() const { return m_min_height; }
|
||||
}; // class Quad
|
||||
#endif
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "io/xml_node.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
|
||||
const int QuadGraph::UNKNOWN_SECTOR = -1;
|
||||
|
||||
/** Constructor, loads the graph information for a given set of quads
|
||||
* from a graph file.
|
||||
* \param quad_file_name Name of the file of all quads
|
||||
@ -37,10 +39,8 @@ QuadGraph::QuadGraph(const std::string &quad_file_name,
|
||||
m_mesh = NULL;
|
||||
m_mesh_buffer = NULL;
|
||||
m_all_quads = new QuadSet(quad_file_name);
|
||||
// First create all nodes
|
||||
for(unsigned int i=0; i<m_all_quads->getNumberOfQuads(); i++) {
|
||||
m_all_nodes.push_back(new GraphNode(i));
|
||||
}
|
||||
GraphNode::m_all_quads = m_all_quads;
|
||||
GraphNode::m_all_nodes = this;
|
||||
load(graph_file_name);
|
||||
} // QuadGraph
|
||||
|
||||
@ -65,24 +65,71 @@ void QuadGraph::load(const std::string &filename)
|
||||
if(!xml)
|
||||
{
|
||||
// No graph file exist, assume a default loop X -> X+1
|
||||
// i.e. each quad is part of the graph exactly once.
|
||||
// First create an empty graph node for each quad:
|
||||
for(unsigned int i=0; i<m_all_quads->getNumberOfQuads(); i++)
|
||||
m_all_nodes.push_back(new GraphNode(i));
|
||||
// Then set the default loop:
|
||||
setDefaultSuccessors();
|
||||
return;
|
||||
}
|
||||
|
||||
// The graph file exist, so read it in. The graph file must first contain
|
||||
// the node definitions, before the edges can be set.
|
||||
for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *node = xml->getNode(i);
|
||||
if(node->getName()!="edge")
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
// First graph node definitions:
|
||||
// -----------------------------
|
||||
if(xml_node->getName()=="node-list")
|
||||
{
|
||||
// A list of quads is connected to a list of graph nodes:
|
||||
unsigned int from, to;
|
||||
xml_node->get("from-quad", &from);
|
||||
xml_node->get("to-quad", &to);
|
||||
for(unsigned int i=from; i<=to; i++)
|
||||
{
|
||||
m_all_nodes.push_back(new GraphNode(i));
|
||||
}
|
||||
}
|
||||
else if(xml_node->getName()=="node")
|
||||
{
|
||||
// A single quad is connected to a single graph node.
|
||||
unsigned int id;
|
||||
xml_node->get("quad", &id);
|
||||
m_all_nodes.push_back(new GraphNode(id));
|
||||
}
|
||||
|
||||
// Then the definition of edges between the graph nodes:
|
||||
// -----------------------------------------------------
|
||||
else if(xml_node->getName()=="edge-loop")
|
||||
{
|
||||
// A closed loop:
|
||||
unsigned int from, to;
|
||||
xml_node->get("from", &from);
|
||||
xml_node->get("to", &to);
|
||||
for(unsigned int i=from; i<=to; i++)
|
||||
{
|
||||
assert(i!=to ? i+1 : from <m_all_nodes.size());
|
||||
m_all_nodes[i]->addSuccessor(i!=to ? i+1 : from);
|
||||
}
|
||||
}
|
||||
else if(xml_node->getName()=="edge")
|
||||
{
|
||||
// Adds a single edge to the graph:
|
||||
unsigned int from, to;
|
||||
xml_node->get("from", &from);
|
||||
xml_node->get("to", &to);
|
||||
assert(to<m_all_nodes.size());
|
||||
m_all_nodes[from]->addSuccessor(to);
|
||||
} // edge
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Incorrect specification in '%s': '%s' ignored\n",
|
||||
filename.c_str(), node->getName().c_str());
|
||||
filename.c_str(), xml_node->getName().c_str());
|
||||
continue;
|
||||
} // incorrect specification
|
||||
}
|
||||
int from, to;
|
||||
node->get("from", &from);
|
||||
node->get("to", &to);
|
||||
assert(from>=0 && from <(int)m_all_nodes.size());
|
||||
m_all_nodes[from]->addSuccessor(to, *m_all_quads);
|
||||
} // from
|
||||
delete xml;
|
||||
|
||||
setDefaultSuccessors();
|
||||
@ -96,8 +143,7 @@ void QuadGraph::setDefaultSuccessors()
|
||||
{
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++) {
|
||||
if(m_all_nodes[i]->getNumberOfSuccessors()==0) {
|
||||
m_all_nodes[i]->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1,
|
||||
*m_all_quads);
|
||||
m_all_nodes[i]->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1);
|
||||
} // if size==0
|
||||
} // for i<m_allNodes.size()
|
||||
} // setDefaultSuccessors
|
||||
@ -163,24 +209,108 @@ void QuadGraph::createDebugMesh()
|
||||
void QuadGraph::getSuccessors(int node_number, std::vector<unsigned int>& succ) const {
|
||||
const GraphNode *v=m_all_nodes[node_number];
|
||||
for(unsigned int i=0; i<v->getNumberOfSuccessors(); i++) {
|
||||
succ.push_back((*v)[i]);
|
||||
succ.push_back(v->getSuccessor(i));
|
||||
}
|
||||
} // getSuccessors
|
||||
|
||||
// ============================================================================
|
||||
/** Adds a successor to a node. This function will also pre-compute certain
|
||||
* values (like distance from this node to the successor, angle (in world)
|
||||
* between this node and the successor.
|
||||
* \param to The index of the successor.
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function 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. The Z axis
|
||||
* is not changed.
|
||||
* \param dst Returns the results in the X and Y coordinates.
|
||||
* \param xyz The position of the kart.
|
||||
* \param sector The graph node the position is on.
|
||||
*/
|
||||
void QuadGraph::GraphNode::addSuccessor(int to, const QuadSet &quad_set)
|
||||
void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz,
|
||||
const int sector) const
|
||||
{
|
||||
m_vertices.push_back(to);
|
||||
Vec3 this_xyz = quad_set.getCenterOfQuad(m_index);
|
||||
Vec3 next_xyz = quad_set.getCenterOfQuad(to);
|
||||
Vec3 diff = next_xyz - this_xyz;
|
||||
m_distance_to_next.push_back(diff.length());
|
||||
if(sector == UNKNOWN_SECTOR )
|
||||
{
|
||||
std::cerr << "WARNING: UNKNOWN_SECTOR in spatialToTrack().\n";
|
||||
return;
|
||||
}
|
||||
|
||||
float theta = -atan2(diff.getX(), diff.getY());
|
||||
m_angle_to_next.push_back(theta);
|
||||
} // addSuccessor
|
||||
getNode(sector).getDistances(xyz, dst);
|
||||
|
||||
} // spatialToTrack
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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.
|
||||
* \param XYZ Position for which the segment should be determined.
|
||||
* \param sector Contains the previous sector (as a shortcut, since usually
|
||||
* the sector is the same as the last one), and on return the result
|
||||
* \param max_lookahead Maximum number of graph nodes that are to be searched
|
||||
* from the current sector number. This is used by the AI to make sure
|
||||
* the AI does not skip any parts of the track (e.g. if the graph has
|
||||
* a loop, the AI code skip the whole loop otherwise and continue
|
||||
* on the normal path). Only used ith sector is not UNKNOWN_SECTOR.
|
||||
* Defaults to -1, which indicates to use all graph nodes. The actual
|
||||
* value might depend on the track, the size of the drivelines etc.
|
||||
*/
|
||||
void QuadGraph::findRoadSector(const Vec3& xyz, int *sector,
|
||||
int max_lookahead) const
|
||||
{
|
||||
if(*sector!=UNKNOWN_SECTOR)
|
||||
{
|
||||
// Most likely the kart will still be on the sector it was before,
|
||||
// so this simple case is tested first.
|
||||
if(getQuad(*sector).pointInQuad(xyz) )
|
||||
return;
|
||||
|
||||
// Then check all immediate neighbours. If it's any of them,
|
||||
// immediately return without any further tests.
|
||||
const GraphNode &node = *m_all_nodes[*sector];
|
||||
for(unsigned int i=0; i<node.getNumberOfSuccessors(); i++)
|
||||
{
|
||||
int succ = node.getSuccessor(i);
|
||||
if(getQuad(succ).pointInQuad(xyz))
|
||||
{
|
||||
*sector = succ;
|
||||
return;
|
||||
} // if pointInQuad
|
||||
} // for i<node.getNumberOfSuccessors()
|
||||
|
||||
} // if *sector!=UNKNOWN_SECTOR
|
||||
|
||||
// Now we search through all graph nodes, starting with
|
||||
// the current one
|
||||
int indx = *sector;
|
||||
float min_dist = 999999.9f;
|
||||
|
||||
// If a current sector is given, and max_lookahead is specify, only test
|
||||
// the next max_lookahead graph nodes instead of testing the whole graph.
|
||||
// This is necessary for the AI: if the track contains a loop, e.g.:
|
||||
// -A--+---B---+----F--------
|
||||
// E C
|
||||
// +---D---+
|
||||
// and the track is supposed to be driven: ABCDEBF, the AI might find
|
||||
// the node on F, and then keep on going straight ahead instead of
|
||||
// using the loop at all.
|
||||
unsigned int max_count = (*sector!=UNKNOWN_SECTOR && max_lookahead>0)
|
||||
? max_lookahead
|
||||
: m_all_nodes.size();
|
||||
*sector = UNKNOWN_SECTOR;
|
||||
for(unsigned int i=0; i<max_count; i++)
|
||||
{
|
||||
indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0;
|
||||
const Quad &q = getQuad(indx);
|
||||
float dist = xyz.getZ() - q.getMinHeight();
|
||||
// While negative distances are unlikely, we allow some small netative
|
||||
// numbers in case that the kart is partly in the track.
|
||||
if(q.pointInQuad(xyz) && dist < min_dist && dist>-1.0f)
|
||||
{
|
||||
min_dist = dist;
|
||||
*sector = indx;
|
||||
}
|
||||
} // for i<m_all_nodes.size()
|
||||
|
||||
return;
|
||||
} // findRoadSector
|
||||
|
@ -23,44 +23,13 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "tracks/graph_node.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
|
||||
/** This class stores a graph of quads. */
|
||||
class QuadGraph {
|
||||
/** This class stores a node of the graph, i.e. a list of successor
|
||||
* edges. */
|
||||
class GraphNode
|
||||
{
|
||||
/** Index of this node in m_all_quads. */
|
||||
int m_index;
|
||||
/** The list of successors. */
|
||||
std::vector<int> m_vertices;
|
||||
/** The distance to each of the successors. */
|
||||
std::vector<float> m_distance_to_next;
|
||||
/** The angle of the line from this node to each neighbour. */
|
||||
std::vector<float> m_angle_to_next;
|
||||
public:
|
||||
/** Constructor, stores the index.
|
||||
* \param index Index of this node in m_all_quads. */
|
||||
GraphNode(int index) { m_index = index; }
|
||||
void addSuccessor (int to, const QuadSet &quad_set);
|
||||
/** Returns the i-th successor. */
|
||||
unsigned int operator[] (int i) const {return m_vertices[i]; }
|
||||
/** Returns the number of successors. */
|
||||
unsigned int getNumberOfSuccessors() const
|
||||
{ return (unsigned int)m_vertices.size(); }
|
||||
/** Returns the index in the quad_set of this node. */
|
||||
int getIndex() const { return m_index; }
|
||||
/** Returns the distance to the j-th. successor. */
|
||||
float getDistanceToSuccessor(int j) const
|
||||
{ return m_distance_to_next[j]; }
|
||||
/** Returns the angle from this node to the j-th. successor. */
|
||||
float getAngleToSuccessor(int j) const
|
||||
{ return m_angle_to_next[j]; }
|
||||
}; // GraphNode
|
||||
|
||||
// ========================================================================
|
||||
protected:
|
||||
private:
|
||||
/** The actual graph data structure. */
|
||||
std::vector<GraphNode*> m_all_nodes;
|
||||
/** The set of all quads. */
|
||||
@ -75,31 +44,45 @@ protected:
|
||||
void setDefaultSuccessors();
|
||||
void load (const std::string &filename);
|
||||
public:
|
||||
static const int UNKNOWN_SECTOR;
|
||||
|
||||
QuadGraph (const std::string &quad_file_name,
|
||||
const std::string graph_file_name);
|
||||
~QuadGraph ();
|
||||
void createDebugMesh();
|
||||
void getSuccessors(int quadNumber,
|
||||
std::vector<unsigned int>& succ) const;
|
||||
void spatialToTrack(Vec3 *dst, const Vec3& xyz,
|
||||
const int sector) const;
|
||||
void findRoadSector(const Vec3& XYZ, int *sector,
|
||||
int max_lookahead=-1) const;
|
||||
|
||||
/** Returns the number of nodes in the graph. */
|
||||
unsigned int getNumNodes() const { return m_all_nodes.size(); }
|
||||
/** Returns the quad set for this graph. */
|
||||
const QuadSet* getQuads() const { return m_all_quads; }
|
||||
/** Returns the center of a quad.
|
||||
* \param n Index of the quad. */
|
||||
const Vec3& getCenterOfQuad(int n) const
|
||||
{ return m_all_quads->getCenterOfQuad(n); }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Return the distance to the j-th successor of node n. */
|
||||
float getDistanceToNext(int n, int j) const
|
||||
{ return m_all_nodes[n]->getDistanceToSuccessor(j);}
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the angle of the line between node n and its j-th.
|
||||
* successor. */
|
||||
float getAngleToNext(int n, int j) const
|
||||
{ return m_all_nodes[n]->getAngleToSuccessor(j); }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the number of successors of a node n. */
|
||||
int getNumberOfSuccessors(int n) const
|
||||
{ return m_all_nodes[n]->getNumberOfSuccessors(); }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a graph node. */
|
||||
const Quad& getQuad(unsigned int j) const
|
||||
{ return m_all_quads->getQuad(m_all_nodes[j]->getIndex()); }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a graph node. */
|
||||
GraphNode& getNode(unsigned int j) const{ return *m_all_nodes[j]; }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the distance from the start to the beginning of a quad. */
|
||||
float getDistanceFromStart(int j) const
|
||||
{ return m_all_nodes[j]->getDistanceFromStart(); }
|
||||
}; // QuadGraph
|
||||
|
||||
#endif
|
||||
|
@ -71,6 +71,8 @@ void QuadSet::load(const std::string &filename) {
|
||||
}
|
||||
for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
||||
{
|
||||
if(i==293)
|
||||
printf("XX");
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if(xml_node->getName()!="quad")
|
||||
{
|
||||
|
@ -53,7 +53,6 @@ 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 )
|
||||
@ -156,118 +155,6 @@ int Track::pointInQuad
|
||||
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.
|
||||
* \param XYZ Position for which the segment should be determined.
|
||||
* \param sector Contains the previous sector (as a shortcut, since usually
|
||||
* the sector is the same as the last one), and on return the result
|
||||
*/
|
||||
void Track::findRoadSector(const Vec3& 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.getZ()-m_left_driveline[i].getZ()) < 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;
|
||||
|
||||
// Note: we can make the plane with the normal driveliens
|
||||
// (not the one with tolerance), since the driveliens with
|
||||
// tolerance lie in the same plane.
|
||||
if( possible_segment_tris[i].triangle == QUAD_TRI_FIRST )
|
||||
{
|
||||
sgMakePlane( plane, m_left_driveline[segment].toFloat(),
|
||||
m_right_driveline[segment].toFloat(),
|
||||
m_right_driveline[next].toFloat() );
|
||||
}
|
||||
else //possible_segment_tris[i].triangle == QUAD_TRI_SECOND
|
||||
{
|
||||
sgMakePlane( plane, m_right_driveline[next].toFloat(),
|
||||
m_left_driveline[next].toFloat(),
|
||||
m_left_driveline[segment].toFloat() );
|
||||
}
|
||||
|
||||
dist = sgHeightAbovePlaneVec3( plane, XYZ.toFloat() );
|
||||
|
||||
/* 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
|
||||
@ -303,7 +190,7 @@ int Track::findOutOfRoadSector
|
||||
const int CURR_SECTOR
|
||||
) const
|
||||
{
|
||||
int sector = UNKNOWN_SECTOR;
|
||||
int sector = QuadGraph::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
|
||||
@ -318,7 +205,7 @@ int Track::findOutOfRoadSector
|
||||
|
||||
int begin_sector = 0;
|
||||
int count = DRIVELINE_SIZE;
|
||||
if(CURR_SECTOR != UNKNOWN_SECTOR )
|
||||
if(CURR_SECTOR != QuadGraph::UNKNOWN_SECTOR )
|
||||
{
|
||||
const int LIMIT = 10; //The limit prevents shortcuts
|
||||
if( CURR_SECTOR - LIMIT < 0 )
|
||||
@ -361,76 +248,13 @@ int Track::findOutOfRoadSector
|
||||
begin_sector = next_sector;
|
||||
} // for j
|
||||
|
||||
if(sector==UNKNOWN_SECTOR || sector >=DRIVELINE_SIZE)
|
||||
if(sector==QuadGraph::UNKNOWN_SECTOR || sector >=DRIVELINE_SIZE)
|
||||
{
|
||||
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
|
||||
(
|
||||
Vec3& dst, /* out */
|
||||
const Vec3& 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 = (m_driveline[PREV]-POS).length2_2d();
|
||||
const float DIST_NEXT = (m_driveline[NEXT]-POS).length2_2d();
|
||||
|
||||
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].toFloat(), m_driveline[p2].toFloat() );
|
||||
|
||||
dst.setX(sgDistToLineVec2 ( line_eqn, POS.toFloat() ) );
|
||||
|
||||
sgAddScaledVec2 ( tmp, POS.toFloat(), line_eqn, -dst.getX() );
|
||||
|
||||
float dist_from_driveline_p1 = sgDistanceVec2 ( tmp, m_driveline[p1].toFloat() );
|
||||
dst.setY(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.setZ(m_path_width[p1]*(1-fraction)+fraction*m_path_width[p2]);
|
||||
|
||||
return (int)p1;
|
||||
} // spatialToTrack
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Vec3& Track::trackToSpatial(const int SECTOR ) const
|
||||
{
|
||||
@ -477,73 +301,6 @@ btTransform Track::getStartTransform(unsigned int pos) const
|
||||
return start;
|
||||
} // getStartTransform
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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].toFloat());
|
||||
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);
|
||||
stk_scene->add(sphere);
|
||||
} // for i
|
||||
*/
|
||||
} /// type ==1
|
||||
// 2: drivelines
|
||||
if(type & 2)
|
||||
{
|
||||
/*
|
||||
ssgVertexArray* v_array = new ssgVertexArray();
|
||||
ssgColourArray* c_array = new ssgColourArray();
|
||||
const std::vector<Vec3> &left = m_left_driveline;
|
||||
const std::vector<Vec3> &right = m_right_driveline;
|
||||
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,left [i ].toFloat()); v[2]+=0.1f; v_array->add(v);
|
||||
sgCopyVec3(v,right[i ].toFloat()); v[2]+=0.1f; v_array->add(v);
|
||||
sgCopyVec3(v,right[ip1].toFloat()); v[2]+=0.1f; v_array->add(v);
|
||||
sgCopyVec3(v,left [ip1].toFloat()); 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);
|
||||
stk_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
|
||||
@ -870,8 +627,8 @@ void Track::loadTrack(const std::string &filename)
|
||||
m_camera_final_hpr.degreeToRad();
|
||||
|
||||
m_sky_type = SKY_NONE;
|
||||
const XMLNode *node = root->getNode("sky-dome");
|
||||
if(node)
|
||||
const XMLNode *xml_node = root->getNode("sky-dome");
|
||||
if(xml_node)
|
||||
{
|
||||
m_sky_type = SKY_DOME;
|
||||
m_sky_vert_segments = 16;
|
||||
@ -879,19 +636,19 @@ void Track::loadTrack(const std::string &filename)
|
||||
m_sky_sphere_percent = 1.0f;
|
||||
m_sky_texture_percent = 1.0f;
|
||||
std::string s;
|
||||
node->get("texture", &s );
|
||||
xml_node->get("texture", &s );
|
||||
m_sky_textures.push_back(s);
|
||||
node->get("vertical", &m_sky_vert_segments );
|
||||
node->get("horizontal", &m_sky_hori_segments );
|
||||
node->get("sphere-percent", &m_sky_sphere_percent );
|
||||
node->get("texture-percent", &m_sky_texture_percent);
|
||||
xml_node->get("vertical", &m_sky_vert_segments );
|
||||
xml_node->get("horizontal", &m_sky_hori_segments );
|
||||
xml_node->get("sphere-percent", &m_sky_sphere_percent );
|
||||
xml_node->get("texture-percent", &m_sky_texture_percent);
|
||||
|
||||
} // if sky-dome
|
||||
node = root->getNode("sky-box");
|
||||
if(node)
|
||||
xml_node = root->getNode("sky-box");
|
||||
if(xml_node)
|
||||
{
|
||||
std::string s;
|
||||
node->get("texture", &s);
|
||||
xml_node->get("texture", &s);
|
||||
m_sky_textures = StringUtils::split(s, ' ');
|
||||
if(m_sky_textures.size()!=6)
|
||||
{
|
||||
@ -970,14 +727,10 @@ void Track::loadDriveline()
|
||||
<< " vertex long. Track is " << m_name << " ." << std::endl;
|
||||
|
||||
m_driveline.reserve(DRIVELINE_SIZE);
|
||||
m_path_width.reserve(DRIVELINE_SIZE);
|
||||
for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
|
||||
{
|
||||
Vec3 center_point = (m_left_driveline[i]+m_right_driveline[i])*0.5;
|
||||
m_driveline.push_back(center_point);
|
||||
|
||||
float width = ( m_right_driveline[i] - center_point ).length();
|
||||
m_path_width.push_back(width);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < DRIVELINE_SIZE; ++i)
|
||||
@ -1032,7 +785,7 @@ Track::readDrivelineFromFile(std::vector<Vec3>& line, const std::string& file_ex
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
|
||||
int prev_sector = UNKNOWN_SECTOR;
|
||||
int prev_sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
SGfloat prev_distance = 1.51f;
|
||||
while(!feof(fd))
|
||||
{
|
||||
@ -1057,7 +810,7 @@ Track::readDrivelineFromFile(std::vector<Vec3>& line, const std::string& file_ex
|
||||
|
||||
Vec3 point(x,y,z);
|
||||
|
||||
if(prev_sector != UNKNOWN_SECTOR)
|
||||
if(prev_sector != QuadGraph::UNKNOWN_SECTOR)
|
||||
prev_distance = (point-line[prev_sector]).length2_2d();
|
||||
|
||||
//1.5f was choosen because it's more or less the length of the tuxkart
|
||||
@ -1151,17 +904,17 @@ void Track::convertTrackToBullet(const scene::IMesh *mesh)
|
||||
* scene might use raycast on this track model to determine the actual
|
||||
* height of the terrain.
|
||||
*/
|
||||
bool Track::loadMainTrack(const XMLNode &node)
|
||||
bool Track::loadMainTrack(const XMLNode &xml_node)
|
||||
{
|
||||
std::string model_name;
|
||||
node.get("model", &model_name);
|
||||
xml_node.get("model", &model_name);
|
||||
std::string full_path = file_manager->getTrackFile(model_name,
|
||||
getIdent());
|
||||
scene::IMesh *mesh = irr_driver->getAnimatedMesh(full_path);
|
||||
if(!mesh)
|
||||
{
|
||||
fprintf(stderr, "Warning: Main track model '%s' in '%s' not found, aborting.\n",
|
||||
node.getName().c_str(), model_name.c_str());
|
||||
xml_node.getName().c_str(), model_name.c_str());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -1177,12 +930,12 @@ bool Track::loadMainTrack(const XMLNode &node)
|
||||
|
||||
scene::ISceneNode *scene_node = irr_driver->addOctTree(mesh);
|
||||
core::vector3df xyz(0,0,0);
|
||||
node.getXYZ(&xyz);
|
||||
xml_node.getXYZ(&xyz);
|
||||
core::vector3df hpr(0,0,0);
|
||||
node.getHPR(&hpr);
|
||||
xml_node.getHPR(&hpr);
|
||||
scene_node->setPosition(xyz);
|
||||
scene_node->setRotation(hpr);
|
||||
handleAnimatedTextures(scene_node, node);
|
||||
handleAnimatedTextures(scene_node, xml_node);
|
||||
m_all_nodes.push_back(scene_node);
|
||||
scene_node->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
|
||||
@ -1621,8 +1374,6 @@ void Track::loadTrackModel()
|
||||
trans -> addKid(lod );
|
||||
m_model-> addKid(trans );
|
||||
lod -> setRanges(r, 2);
|
||||
if(user_config->m_track_debug)
|
||||
addDebugToScene(user_config->m_track_debug);
|
||||
|
||||
}
|
||||
else
|
||||
@ -1637,6 +1388,23 @@ void Track::loadTrackModel()
|
||||
|
||||
if(m_sky_type==SKY_DOME)
|
||||
{
|
||||
scene::ISceneNode *node = irr_driver->addSkyDome(m_sky_textures[0],
|
||||
m_sky_hori_segments,
|
||||
m_sky_vert_segments,
|
||||
m_sky_texture_percent,
|
||||
m_sky_sphere_percent);
|
||||
for(unsigned int i=0; i<node->getMaterialCount(); i++)
|
||||
{
|
||||
video::SMaterial &irrMaterial=node->getMaterial(i);
|
||||
for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
|
||||
{
|
||||
video::ITexture* t=irrMaterial.getTexture(j);
|
||||
if(!t) continue;
|
||||
core::matrix4 *m = &irrMaterial.getTextureMatrix(j);
|
||||
m_animated_textures.push_back(new MovingTexture(m, 0.5f, 0.5f));
|
||||
} // for j<MATERIAL_MAX_TEXTURES
|
||||
} // for i<getMaterialCount
|
||||
|
||||
m_all_nodes.push_back(irr_driver->addSkyDome(m_sky_textures[0],
|
||||
m_sky_hori_segments,
|
||||
m_sky_vert_segments,
|
||||
|
@ -106,8 +106,6 @@ public:
|
||||
static const int QUAD_TRI_FIRST;
|
||||
static const int QUAD_TRI_SECOND;
|
||||
|
||||
static const int UNKNOWN_SECTOR;
|
||||
|
||||
struct SegmentTriangle
|
||||
{
|
||||
int segment;
|
||||
@ -166,20 +164,15 @@ public:
|
||||
~Track ();
|
||||
bool isArena () const { return m_is_arena; }
|
||||
void cleanup ();
|
||||
void addDebugToScene (int type ) const;
|
||||
void draw2Dview (float x_offset,
|
||||
float y_offset ) const;
|
||||
void drawScaled2D (float x, float y, float w,
|
||||
float h ) const;
|
||||
|
||||
void findRoadSector (const Vec3& XYZ, int *sector) const;
|
||||
int findOutOfRoadSector(const Vec3& XYZ,
|
||||
const RoadSide SIDE,
|
||||
const int CURR_SECTOR
|
||||
) const;
|
||||
int spatialToTrack (Vec3& dst,
|
||||
const Vec3& POS,
|
||||
const int SECTOR ) const;
|
||||
const Vec3& trackToSpatial (const int SECTOR) const;
|
||||
void loadTrackModel ();
|
||||
void addMusic (MusicInformation* mi)
|
||||
@ -207,7 +200,6 @@ public:
|
||||
const std::string& getDescription () const {return m_description; }
|
||||
const std::string& getDesigner () const {return m_designer; }
|
||||
const std::string& getScreenshotFile () const {return m_screenshot; }
|
||||
const std::vector<float>& getWidth () const {return m_path_width; }
|
||||
const std::string& getItemStyle () const {return m_item_style; }
|
||||
bool hasFinalCamera () const {return m_has_final_camera; }
|
||||
const Vec3& getCameraPosition () const {return m_camera_final_position;}
|
||||
|
Loading…
Reference in New Issue
Block a user