Merged reverse-mode branch back into trunk.

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10867 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-02-16 11:45:22 +00:00
commit 7ad18d4815
39 changed files with 570 additions and 347 deletions

View File

@ -42,8 +42,17 @@
</div>
<!-- Right pane -->
<placeholder proportion="1" height="100%" id="screenshot_div">
</placeholder>
<div proportion="1" height="100%" layout="vertical-row">
<placeholder proportion="1" width="100%" height="100%" id="screenshot_div">
</placeholder>
<div width="75%" height="fit" layout="horizontal-row" >
<spacer width="40" height="2" />
<checkbox id="reverse"/>
<spacer width="20" height="2" />
<label id="reverse-text" height="100%" I18N="Drive the track reverse" text="Reverse"/>
</div>
</div>
</div>
<spacer width="1" height="5%"/>

View File

@ -114,7 +114,12 @@ void Camera::readEndCamera(const XMLNode &root)
m_end_cameras.clear();
for(unsigned int i=0; i<root.getNumNodes(); i++)
{
const XMLNode *node = root.getNode(i);
unsigned int index = i;
// In reverse mode, reverse the order in which the
// end cameras are read.
if(QuadGraph::get()->isReverse())
index = root.getNumNodes() - 1 - i;
const XMLNode *node = root.getNode(index);
EndCameraInformation eci;
if(!eci.readXML(*node)) continue;
m_end_cameras.push_back(eci);
@ -220,12 +225,8 @@ void Camera::setMode(Mode mode)
{
m_next_end_camera = m_end_cameras.size()>1 ? 1 : 0;
m_current_end_camera = 0;
if(m_end_cameras.size()>0 &&
m_end_cameras[0].m_type==EndCameraInformation::EC_STATIC_FOLLOW_KART)
{
m_camera->setPosition(m_end_cameras[0].m_position.toIrrVector());
m_camera->setTarget(m_kart->getXYZ().toIrrVector());
}
m_camera->setFOV(m_fov);
handleEndCamera(0);
} // mode==CM_FINAL
m_mode = mode;
@ -255,8 +256,9 @@ void Camera::reset()
*/
void Camera::setInitialTransform()
{
m_camera->setPosition( m_kart->getXYZ().toIrrVector()
+ core::vector3df(0, 25, -50) );
Vec3 start_offset(0, 25, -50);
Vec3 xx = m_kart->getTrans()(start_offset);
m_camera->setPosition( xx.toIrrVector());
// Reset the target from the previous target (in case of a restart
// of a race) - otherwise the camera will initially point in the wrong
// direction till smoothMoveCamera has corrected this. Setting target
@ -491,7 +493,8 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle,
if (race_manager->getNumLocalPlayers() < 2)
{
sfx_manager->positionListener(m_camera->getPosition(), wanted_target - m_camera->getPosition());
sfx_manager->positionListener(m_camera->getPosition(),
wanted_target - m_camera->getPosition());
}
}
@ -505,6 +508,25 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle,
*/
void Camera::handleEndCamera(float dt)
{
// First test if the kart is close enough to the next end camera, and
// if so activate it.
if( m_end_cameras.size()>0 &&
m_end_cameras[m_next_end_camera].isReached(m_kart->getXYZ()))
{
m_current_end_camera = m_next_end_camera;
if(m_end_cameras[m_current_end_camera].m_type
==EndCameraInformation::EC_STATIC_FOLLOW_KART)
{
m_camera->setPosition(
m_end_cameras[m_current_end_camera].m_position.toIrrVector()
);
}
m_camera->setFOV(m_fov);
m_next_end_camera++;
if(m_next_end_camera>=(unsigned)m_end_cameras.size())
m_next_end_camera = 0;
}
EndCameraInformation::EndCameraType info
= m_end_cameras.size()==0 ? EndCameraInformation::EC_AHEAD_OF_KART
: m_end_cameras[m_current_end_camera].m_type;
@ -513,7 +535,11 @@ void Camera::handleEndCamera(float dt)
{
case EndCameraInformation::EC_STATIC_FOLLOW_KART:
{
const core::vector3df &cp = m_camera->getAbsolutePosition();
// Since the camera has no parents, we can use the relative
// position here (otherwise we need to call updateAbsolutePosition
// after changing the relative position in order to get the right
// position here).
const core::vector3df &cp = m_camera->getPosition();
const Vec3 &kp = m_kart->getXYZ();
// Estimate the fov, assuming that the vector from the camera to
// the kart and the kart length are orthogonal to each other
@ -541,22 +567,7 @@ void Camera::handleEndCamera(float dt)
default: break;
} // switch
// Now test if the kart is close enough to the next end camera, and
// if so activate it.
if( m_end_cameras.size()>0 &&
m_end_cameras[m_next_end_camera].isReached(m_kart->getXYZ()))
{
m_current_end_camera = m_next_end_camera;
if(m_end_cameras[m_current_end_camera].m_type
==EndCameraInformation::EC_STATIC_FOLLOW_KART)
m_camera->setPosition(
m_end_cameras[m_current_end_camera].m_position.toIrrVector()
);
m_camera->setFOV(m_fov);
m_next_end_camera++;
if(m_next_end_camera>=(unsigned)m_end_cameras.size()) m_next_end_camera = 0;
}
} // checkForNextEndCamera
} // handleEndCamera
// ----------------------------------------------------------------------------
/** Sets viewport etc. for this camera. Called from irr_driver just before

View File

@ -173,6 +173,7 @@ void LODNode::OnRegisterSceneNode()
{
scene::IMeshBuffer* mb = mesh->getMeshBuffer(n);
video::ITexture* t = mb->getMaterial().getTexture(0);
if(!t) continue;
Material* m = material_manager->getMaterialFor(t, mb);
if (m != NULL)
{

View File

@ -225,22 +225,7 @@ void RubberBall::getNextControlPoint()
m_length_cp_2_3 = dist;
const Quad &quad =
QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node);
Vec3 aim = quad.getCenter();
// If we are close enough for the ball to hop faster, adjust the position
// relative to the center of the track depending on where the target is:
// if(m_fast_ping)
if(0)
{
LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
float r = world->getTrackSector(m_target->getWorldKartId())
.getRelativeDistanceToCenter();
aim -= m_st_early_target_factor * r
* QuadGraph::get()->getNode(m_last_aimed_graph_node)
.getCenterToRightVector();
}
m_control_points[3] = aim;
m_control_points[3] = quad.getCenter();
} // getNextControlPoint
// ----------------------------------------------------------------------------

View File

@ -78,6 +78,14 @@ void AIBaseController::computePath()
next.clear();
// Get all successors the AI is allowed to take.
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
// In case of short cuts hidden for the AI it can be that a node
// might not have a successor (since the first and last edge of
// a hidden shortcut is ignored). Since in the case that the AI
// ends up on a short cut (e.g. by accident) and doesn't have an
// allowed way to drive, it should still be able to drive, so add
// the non-AI successors of that node in this case.
if(next.size()==0)
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
// For now pick one part on random, which is not adjusted during the
// race. Long term statistics might be gathered to determine the
// best way, potentially depending on race position etc.

View File

@ -134,6 +134,19 @@ void EndController::reset()
}
} // reset
//-----------------------------------------------------------------------------
/** Callback when a new lap is triggered. It is used to switch to the first
* end camera (which is esp. useful in fixing up end cameras in reverse mode,
* since otherwise the switch to the first end camera usually happens way too
* late)
*/
void EndController::newLap(int lap)
{
// This will implicitely trigger setting the first end camera to be active.
if(m_kart->getCamera())
m_kart->getCamera()->setMode(Camera::CM_FINAL);
} // newLap
//-----------------------------------------------------------------------------
/** The end controller must forward 'fire' presses to the race gui.
*/

View File

@ -86,6 +86,7 @@ public:
* to the right player. */
virtual bool isPlayerController () const {return m_player!=NULL;}
virtual void action (PlayerAction action, int value);
virtual void newLap (int lap);
}; // EndKart

View File

@ -361,6 +361,7 @@ void cmdLineHelp (char* invocation)
" --list-karts Show available karts.\n"
" --laps N Define number of laps to N.\n"
" --mode N N=1 novice, N=2 driver, N=3 racer.\n"
" --reverse Play track in reverse (if allowed)\n"
// TODO: add back "--players" switch
// " --players n Define number of players to between 1 and 4.\n"
" --item STYLE Use STYLE as your item style.\n"
@ -766,6 +767,10 @@ int handleCmdLine(int argc, char **argv)
}
i++;
}
else if( !strcmp(argv[i], "--reverse"))
{
race_manager->setReverseTrack(true);
}
else if( (!strcmp(argv[i], "--track") || !strcmp(argv[i], "-t"))
&& i+1<argc )
{

View File

@ -216,10 +216,15 @@ void LinearWorld::newLap(unsigned int kart_index)
KartInfo &kart_info = m_kart_info[kart_index];
Kart *kart = m_karts[kart_index];
// Don't do anything if a kart that has already finished the race
// crosses the start line again. This avoids 'fastest lap' messages
// if the end controller does a fastest lap.
if(kart->hasFinishedRace()) return;
// Only update the kart controller if a kart that has already finished
// the race crosses the start line again. This avoids 'fastest lap'
// messages if the end controller does a fastest lap, but especially
// allows the end controller to switch end cameras
if(kart->hasFinishedRace())
{
kart->getController()->newLap(kart_info.m_race_lap);
return;
}
const int lap_count = race_manager->getNumLaps();
@ -496,12 +501,10 @@ float LinearWorld::estimateFinishTimeForKart(Kart* kart)
// In case that a kart is rescued behind start line, or ...
if(distance_covered<0) distance_covered =1.0f;
const float full_distance = race_manager->getNumLaps()
* m_track->getTrackLength();
const float average_speed = distance_covered/getTime();
// Finish time is the time needed for the whole race with
// the average speed computed above.
float full_distance = race_manager->getNumLaps()
* m_track->getTrackLength();
if(full_distance == 0)
full_distance = 1; // For 0 lap races avoid warning below
#ifdef DEBUG
if(full_distance < distance_covered)
{
@ -510,9 +513,17 @@ float LinearWorld::estimateFinishTimeForKart(Kart* kart)
printf("%f < %f\n", full_distance, distance_covered);
}
#endif
// Avoid potential problems (floating point issues, coding bug?) if a
// kart has driven more than the full distance, but not finished:
// Return the current time plus initial position to spread arrival
// times a bit. This code should generally not be used at all, it's
// just here to avoid invalid finishing times.
if(full_distance < distance_covered)
return getTime() + kart->getInitialPosition();
// Finish time is the time needed for the whole race with
// the computed average speed computed.
const float average_speed = distance_covered/getTime();
return getTime() + (full_distance - distance_covered) / average_speed;
} // estimateFinishTimeForKart

View File

@ -137,7 +137,7 @@ public:
virtual bool haveBonusBoxes(){ return true; }
// ------------------------------------------------------------------------
/** Override settings from base class */
virtual bool computeChecklineRequirements() const { return true; }
virtual bool useChecklineRequirements() const { return true; }
// ------------------------------------------------------------------------
/** Returns true if the kart is on a valid driveline quad.
* \param kart_index Index of the kart. */

View File

@ -72,7 +72,7 @@ public:
virtual void onFirePressed(Controller* who);
// ------------------------------------------------------------------------
/** Override settings from base class */
virtual bool computeChecklineRequirements() const { return false; }
virtual bool useChecklineRequirements() const { return false; }
// ------------------------------------------------------------------------
void scheduleReturnToGarage() { m_return_to_garage = true; }
};

View File

@ -108,7 +108,7 @@ void World::init()
m_eliminated_karts = 0;
m_eliminated_players = 0;
m_num_players = 0;
// Create the race gui before anything else is attached to the scene node
// (which happens when the track is loaded). This allows the race gui to
// do any rendering on texture.
@ -133,7 +133,7 @@ void World::init()
// Load the track models - this must be done before the karts so that the
// karts can be positioned properly on (and not in) the tracks.
m_track->loadTrackModel(this);
m_track->loadTrackModel(this, race_manager->getReverseTrack());
for(unsigned int i=0; i<num_karts; i++)
{

View File

@ -284,7 +284,7 @@ public:
// ------------------------------------------------------------------------
/** Whether to compute checkline requirements for each world on the
* quadgraph. Override to change value. */
virtual bool computeChecklineRequirements() const { return false; }
virtual bool useChecklineRequirements() const { return false; }
// ------------------------------------------------------------------------
void delayedSelfDestruct();

View File

@ -55,6 +55,7 @@ RaceManager::RaceManager()
m_minor_mode = MINOR_MODE_NORMAL_RACE;
m_track_number = 0;
m_coin_target = 0;
setReverseTrack(false);
setTrack("jungle");
m_default_ai_list.clear();
setNumLocalPlayers(0);
@ -351,7 +352,7 @@ void RaceManager::startNextRace()
else if(m_minor_mode==MINOR_MODE_FOLLOW_LEADER)
World::setWorld(new FollowTheLeaderRace());
else if(m_minor_mode==MINOR_MODE_NORMAL_RACE ||
m_minor_mode==MINOR_MODE_TIME_TRIAL)
m_minor_mode==MINOR_MODE_TIME_TRIAL)
World::setWorld(new StandardRace());
else if(m_minor_mode==MINOR_MODE_3_STRIKES)
World::setWorld(new ThreeStrikesBattle());
@ -617,7 +618,8 @@ void RaceManager::startGP(const GrandPrixData* gp)
//-----------------------------------------------------------------------------
void RaceManager::startSingleRace(const std::string trackIdent, const int num_laps)
void RaceManager::startSingleRace(const std::string trackIdent,
const int num_laps)
{
StateManager::get()->enterGameState();
setTrack(trackIdent.c_str());

View File

@ -250,6 +250,9 @@ private:
/** The number of laps for each track of a GP (only one element
* is used if only a single track is used. */
std::vector<int> m_num_laps;
/** Whether a track should be reversed */
std::vector<bool> m_reverse_track;
/** The points given to a kart on a given position (index is
* 0 based, so using race-position - 1. */
@ -315,6 +318,10 @@ public:
void setNumLaps(int num) { m_num_laps.clear();
m_num_laps.push_back(num); }
void setReverseTrack(bool r_t) { m_reverse_track.clear();
m_reverse_track.push_back(r_t); }
void setMajorMode(MajorRaceModeType mode)
{ m_major_mode = mode; }
void setMinorMode(MinorRaceModeType mode)
@ -358,6 +365,9 @@ public:
return 9999;
return m_num_laps[m_track_number];
} // getNumLaps
// ------------------------------------------------------------------------
/** \return whether the track should be reversed */
bool getReverseTrack() const { return m_reverse_track[m_track_number]; }
Difficulty getDifficulty() const { return m_difficulty; }
const std::string& getTrackName() const { return m_tracks[m_track_number]; }
@ -481,9 +491,11 @@ public:
void startGP(const GrandPrixData* gp);
/**
* \brief Higher-level method to start a GP without having to care about the exact startup sequence
* \brief Higher-level method to start a GP without having to care about
* the exact startup sequence.
* \param trackIdent Internal name of the track to race on
* \param num_laps Number of laps to race, or -1 if number of laps is not relevant in current mode
* \param num_laps Number of laps to race, or -1 if number of laps is
* not relevant in current mode
*/
void startSingleRace(const std::string trackIdent, const int num_laps);

View File

@ -116,6 +116,21 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
}
// Reverse track
const bool reverse_available = track->reverseAvailable();
if (reverse_available)
{
m_checkbox = getWidget<CheckBoxWidget>("reverse");
m_checkbox->setState(race_manager->getReverseTrack());
}
else
{
getWidget<CheckBoxWidget>("reverse")->setVisible(false);
getWidget<LabelWidget>("reverse-text")->setVisible(false);
m_checkbox = NULL;
race_manager->setReverseTrack(false);
}
// ---- High Scores
if (has_highscores)
{
@ -144,7 +159,7 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
getWidget<ButtonWidget>("start")->setFocusForPlayer( PLAYER_ID_GAME_MASTER );
}
} // TrackInfoDialog
// ------------------------------------------------------------------------------------------------------
@ -158,7 +173,7 @@ TrackInfoDialog::~TrackInfoDialog()
((TracksScreen*)curr_screen)->setFocusOnTrack(m_ribbon_item);
}
}
} // ~TrackInfoDialog
// ------------------------------------------------------------------------------------------------------
@ -214,17 +229,23 @@ void TrackInfoDialog::updateHighScores()
m_highscore_entries[n]->setText( line.c_str(), false );
}
}
} // updateHighScores
// ------------------------------------------------------------------------------------------------------
void TrackInfoDialog::onEnterPressedInternal()
{
ModalDialog::dismiss();
// Create a copy of member variables we still need, since they will
// not be accessible after dismiss:
const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue());
race_manager->startSingleRace(m_track_ident, num_laps);
}
const bool reverse_track = m_checkbox == NULL ? false
: m_checkbox->getState();
race_manager->setReverseTrack(reverse_track);
std::string track_ident = m_track_ident;
ModalDialog::dismiss();
race_manager->startSingleRace(track_ident, num_laps);
} // onEnterPressedInternal
// ------------------------------------------------------------------------------------------------------
@ -232,14 +253,13 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
{
if (eventSource == "start" )
{
// Create a copy of member variables we still need, since they will
// not be accessible after dismiss:
const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue());
std::string track_ident = m_track_ident;
ModalDialog::dismiss();
race_manager->startSingleRace(track_ident, num_laps);
onEnterPressedInternal();
return GUIEngine::EVENT_BLOCK;
}
else if (eventSource == "reversecheckbox")
{
race_manager->setReverseTrack(m_checkbox->getState());
}
else if (eventSource == "lapcountspinner")
{
assert(m_spinner != NULL);
@ -250,6 +270,6 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
}
return GUIEngine::EVENT_LET;
}
} // processEvent
// ------------------------------------------------------------------------------------------------------

View File

@ -20,6 +20,7 @@
#define HEADER_TRACKINFO_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "guiengine/widgets/check_box_widget.hpp"
static const int HIGHSCORE_COUNT = 3;
@ -42,6 +43,7 @@ class TrackInfoDialog : public GUIEngine::ModalDialog
// When there is no need to tab through / click on images/labels, we can add directly
// irrlicht labels (more complicated uses require the use of our widget set)
GUIEngine::SpinnerWidget* m_spinner;
GUIEngine::CheckBoxWidget* m_checkbox;
GUIEngine::IconButtonWidget* m_kart_icons[HIGHSCORE_COUNT];
GUIEngine::LabelWidget* m_highscore_entries[HIGHSCORE_COUNT];

View File

@ -28,14 +28,11 @@
#include "tracks/track.hpp"
/** Constructor for a checksphere.
* \param check_manager Pointer to the check manager, which is needed when
* resetting e.g. new lap counters.
* \param node XML node containing the parameters for this checkline.
*/
AmbientLightSphere::AmbientLightSphere(CheckManager *check_manager,
const XMLNode &node,
AmbientLightSphere::AmbientLightSphere(const XMLNode &node,
unsigned int index)
: CheckSphere(check_manager, node, index)
: CheckSphere(node, index)
{
m_ambient_color = video::SColor(255, 0, 255, 0); // green
m_inner_radius2 = 1;

View File

@ -49,8 +49,7 @@ private:
* inner radius. */
video::SColor m_ambient_color;
public:
AmbientLightSphere(CheckManager *check_manager,
const XMLNode &node, unsigned int index);
AmbientLightSphere(const XMLNode &node, unsigned int index);
virtual ~AmbientLightSphere() {};
virtual void update(float dt);
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,

View File

@ -23,7 +23,6 @@
#include "io/xml_node.hpp"
#include "modes/linear_world.hpp"
#include "race/race_manager.hpp"
#include "tracks/check_manager.hpp"
#include "tracks/track.hpp"
/** Constructor for a lap line.
@ -31,9 +30,8 @@
* resetting e.g. new lap counters.
* \param node XML node containing the parameters for this checkline.
*/
CheckLap::CheckLap(CheckManager *check_manager, const XMLNode &node,
unsigned int index)
: CheckStructure(check_manager, node, index)
CheckLap::CheckLap(const XMLNode &node, unsigned int index)
: CheckStructure(node, index)
{
// Note that when this is called the karts have not been allocated
// in world, so we can't call world->getNumKarts()

View File

@ -37,8 +37,7 @@ private:
std::vector<float> m_previous_distance;
public:
CheckLap(CheckManager *check_manager, const XMLNode &node,
unsigned int index);
CheckLap(const XMLNode &node, unsigned int index);
virtual ~CheckLap() {};
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
int indx);

View File

@ -27,13 +27,11 @@
#include "race/race_manager.hpp"
/** Constructor for a checkline.
* \param check_manager Pointer to the check manager, which is needed when
* resetting e.g. new lap counters.
* \param node XML node containing the parameters for this checkline.
* \param index Index of this check structure in the check manager.
*/
CheckLine::CheckLine(CheckManager *check_manager, const XMLNode &node,
unsigned int index)
: CheckStructure(check_manager, node, index)
CheckLine::CheckLine(const XMLNode &node, unsigned int index)
: CheckStructure(node, index)
{
// Note that when this is called the karts have not been allocated
// in world, so we can't call world->getNumKarts()

View File

@ -64,8 +64,7 @@ private:
* quad and still considered to be able to cross it. */
static const int m_over_min_height = 4;
public:
CheckLine(CheckManager *check_manager, const XMLNode &node,
unsigned int index);
CheckLine(const XMLNode &node, unsigned int index);
virtual ~CheckLine();
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, int indx);
virtual void reset(const Track &track);

View File

@ -19,6 +19,7 @@
#include "tracks/check_manager.hpp"
#include <string>
#include <algorithm>
#include "io/xml_node.hpp"
#include "tracks/ambient_light_sphere.hpp"
@ -27,7 +28,11 @@
#include "tracks/check_structure.hpp"
#include "tracks/track.hpp"
CheckManager::CheckManager(const XMLNode &node, Track *track)
CheckManager *CheckManager::m_check_manager = NULL;
/** Loads all check structure informaiton from the specified xml file.
*/
void CheckManager::load(const XMLNode &node)
{
for(unsigned int i=0; i<node.getNumNodes(); i++)
{
@ -35,31 +40,61 @@ CheckManager::CheckManager(const XMLNode &node, Track *track)
const std::string &type = check_node->getName();
if(type=="check-line")
{
CheckLine *cl = new CheckLine(this, *check_node, i);
CheckLine *cl = new CheckLine(*check_node, i);
m_all_checks.push_back(cl);
} // checkline
else if(type=="check-lap")
{
m_all_checks.push_back(new CheckLap(this, *check_node, i));
m_all_checks.push_back(new CheckLap(*check_node, i));
}
else if(type=="check-sphere")
{
AmbientLightSphere *cs = new AmbientLightSphere(this, *check_node,
AmbientLightSphere *cs = new AmbientLightSphere(*check_node,
i);
m_all_checks.push_back(cs);
} // checksphere
else
printf("Unknown check structure '%s' - ignored.\n", type.c_str());
} // for i<node.getNumNodes
} // CheckManager
// Now set all 'successors', i.e. check structures that need to get a
// state change when a check structure is triggered. This can't be
// done in the CheckStructures easily, since reversing a track changes
// the direction of the dependencies.
for(unsigned int i=0; i<node.getNumNodes(); i++)
{
const XMLNode *check_node = node.getNode(i);
std::vector<int> check_structures_to_change_state;
check_node->get("other-ids", &check_structures_to_change_state);
// Backwards compatibility to tracks exported with older versions of
// the track exporter
if(check_structures_to_change_state.size()==0)
check_node->get("other-id", &check_structures_to_change_state);
std::vector<int>::iterator it;
for(it=check_structures_to_change_state.begin();
it != check_structures_to_change_state.end(); it++)
{
if(QuadGraph::get()->isReverse())
m_all_checks[*it]->addSuccessor(i);
else
m_all_checks[i]->addSuccessor(*it);
}
}
} // load
// ----------------------------------------------------------------------------
/** Private destructor (to make sure it is only called using the static
* destroy function). Frees all check structures.
*/
CheckManager::~CheckManager()
{
for(unsigned int i=0; i<m_all_checks.size(); i++)
{
delete m_all_checks[i];
}
m_check_manager = NULL;
} // ~CheckManager
// ----------------------------------------------------------------------------
@ -83,3 +118,41 @@ void CheckManager::update(float dt)
(*i)->update(dt);
} // update
// ----------------------------------------------------------------------------
/** Returns the index of the first check structures that triggers a new
* lap to be counted. It aborts if no lap structure is defined.
*/
unsigned int CheckManager::getLapLineIndex() const
{
for (unsigned int i=0; i<getCheckStructureCount(); i++)
{
CheckStructure* c = getCheckStructure(i);
if (dynamic_cast<CheckLap*>(c) != NULL) return i;
}
fprintf(stderr, "Error, no lap line for track found, aborting.\n");
exit(-1);
} // getLapLineIndex
// ----------------------------------------------------------------------------
/** Returns the check line index that is triggered when going from 'from'
* to 'to'. If no check line is triggered, -1 will be returned.
* \param from Coordinates to start from.
* \param to Coordinates to go to.
*/
int CheckManager::getChecklineTriggering(const Vec3 &from,
const Vec3 &to) const
{
for (unsigned int i=0; i<getCheckStructureCount(); i++)
{
CheckStructure* c = getCheckStructure(i);
// FIXME: why is the lapline skipped?
if (dynamic_cast<CheckLap*>(c) != NULL) continue;
if (c->isTriggered(from, to, 0 /* kart id */))
return i;
}
return -1;
} // getChecklineTriggering

View File

@ -19,33 +19,57 @@
#ifndef HEADER_CHECK_MANAGER_HPP
#define HEADER_CHECK_MANAGER_HPP
#include <vector>
#include <string>
#include "utils/no_copy.hpp"
#include <assert.h>
#include <string>
#include <vector>
class XMLNode;
class CheckStructure;
class Track;
class XMLNode;
class Vec3;
/**
* \brief Controls all checks structures of a track.
* \ingroup tracks
*/
class CheckManager
class CheckManager : public NoCopy
{
private:
std::vector<CheckStructure*> m_all_checks;
static CheckManager *m_check_manager;
/** Private constructor, to make sure it is only called via
* the static create function. */
CheckManager() {m_all_checks.clear();};
~CheckManager();
public:
CheckManager(const XMLNode &node, Track *track);
~CheckManager();
void update(float dt);
void reset(const Track &track);
int getCheckStructureCount() const { return m_all_checks.size(); }
/** Returns the nth. check structure. */
CheckStructure *getCheckStructure(unsigned int n)
void load(const XMLNode &node);
void update(float dt);
void reset(const Track &track);
unsigned int getLapLineIndex() const;
int getChecklineTriggering(const Vec3 &from, const Vec3 &to) const;
// ------------------------------------------------------------------------
/** Creates an instance of the check manager. */
static void create()
{
if (n >= m_all_checks.size()) return NULL;
assert(!m_check_manager);
m_check_manager = new CheckManager();
} // create
// ------------------------------------------------------------------------
/** Returns the instance of the check manager. */
static CheckManager* get() { return m_check_manager; }
// ------------------------------------------------------------------------
/** Destroys the check manager. */
static void destroy() { delete m_check_manager; m_check_manager = NULL; }
// ------------------------------------------------------------------------
/** Returns the number of check structures defined. */
unsigned int getCheckStructureCount() const { return m_all_checks.size(); }
// ------------------------------------------------------------------------
/** Returns the nth. check structure. */
CheckStructure *getCheckStructure(unsigned int n) const
{
assert(n < m_all_checks.size());
return m_all_checks[n];
}
}; // CheckManager

View File

@ -30,9 +30,8 @@
* resetting e.g. new lap counters.
* \param node XML node containing the parameters for this checkline.
*/
CheckSphere::CheckSphere(CheckManager *check_manager, const XMLNode &node,
unsigned int index)
: CheckStructure(check_manager, node, index)
CheckSphere::CheckSphere(const XMLNode &node, unsigned int index)
: CheckStructure(node, index)
{
m_radius2 = 1;

View File

@ -45,8 +45,7 @@ private:
* This saves some computations. */
std::vector<float> m_distance2;
public:
CheckSphere(CheckManager *check_manager, const XMLNode &node,
unsigned int index);
CheckSphere(const XMLNode &node, unsigned int index);
virtual ~CheckSphere() {};
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
int kart_id);

View File

@ -28,11 +28,16 @@
#include "tracks/check_manager.hpp"
CheckStructure::CheckStructure(CheckManager *check_manager,
const XMLNode &node, unsigned int index)
CheckStructure::CheckStructure(const XMLNode &node, unsigned int index)
{
m_index = index;
m_check_manager = check_manager;
m_check_type = CT_NEW_LAP;
// This structure is actually filled by the check manager (necessary
// in order to support track reversing).
m_check_structures_to_change_state.clear();
m_same_group.clear();
std::string kind;
node.get("kind", &kind);
if(kind=="lap")
@ -47,14 +52,7 @@ CheckStructure::CheckStructure(CheckManager *check_manager,
{
printf("Unknown check structure '%s' - ignored.\n", kind.c_str());
}
m_check_structures_to_change_state.clear();
node.get("other-ids", &m_check_structures_to_change_state);
// Backwards compatibility to tracks exported with older versions of
// the track exporter
if(m_check_structures_to_change_state.size()==0)
node.get("other-id", &m_check_structures_to_change_state);
m_same_group.clear();
node.get("same-group", &m_same_group);
// Make sure that the index of this check structure is included in
// the same_group list. While this should be guaranteed by the
@ -115,7 +113,7 @@ void CheckStructure::update(float dt)
// ----------------------------------------------------------------------------
/** Changes the status (active/inactive) of all check structures contained
* in the index list indices.
* \param indices List of index of check structures in check_manager that
* \param indices List of index of check structures in the CheckManager that
* are to be changed.
* \param int kart_index For which the status should be changed.
* \param change_state How to change the state (active, deactivate, toggle).
@ -131,7 +129,7 @@ void CheckStructure::changeStatus(const std::vector<int> indices,
for(unsigned int i=0; i<indices.size(); i++)
{
CheckStructure *cs =
m_check_manager->getCheckStructure(indices[i]);
CheckManager::get()->getCheckStructure(indices[i]);
if (cs == NULL) continue;
switch(change_state)
@ -177,9 +175,9 @@ void CheckStructure::changeStatus(const std::vector<int> indices,
/*
printf("--------\n");
for (int n=0; n<m_check_manager->getCheckStructureCount(); n++)
for (int n=0; n<CheckManager::get()->getCheckStructureCount(); n++)
{
CheckStructure *cs = m_check_manager->getCheckStructure(n);
CheckStructure *cs = CheckManager::get()->getCheckStructure(n);
if (dynamic_cast<CheckLap*>(cs) != NULL)
printf("Checkline %i (LAP) : %i\n", n, (int)cs->m_is_active[kart_index]);
else

View File

@ -73,9 +73,6 @@ protected:
bool m_active_at_reset;
private:
/** Stores a pointer to the check manager. */
CheckManager *m_check_manager;
/** The type of this checkline. */
CheckType m_check_type;
@ -100,8 +97,7 @@ private:
ChangeState change_state);
public:
CheckStructure(CheckManager *check_manager, const XMLNode &node,
unsigned int index);
CheckStructure(const XMLNode &node, unsigned int index);
virtual ~CheckStructure() {};
virtual void update(float dt);
virtual void changeDebugColor(bool is_active) {}
@ -112,13 +108,20 @@ public:
* \param indx Index of the kart, can be used to store kart specific
* additional data.
*/
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, int indx)=0;
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
int indx)=0;
virtual void trigger(unsigned int kart_index);
virtual void reset(const Track &track);
// ------------------------------------------------------------------------
/** Returns the type of this check structure. */
CheckType getType() const { return m_check_type; }
// ------------------------------------------------------------------------
/** Adds the index of a successor check structure which will get triggered
* by this check structure. */
void addSuccessor(unsigned int i) {
m_check_structures_to_change_state.push_back(i);
} // addSuccessor
}; // CheckStructure
#endif

View File

@ -25,45 +25,41 @@
#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).
* \param index Index of the quad to use for this node (in QuadSet).
*/
GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index)
{
assert(quad_index<m_all_quads->getNumberOfQuads());
assert(quad_index<QuadSet::get()->getNumberOfQuads());
m_quad_index = quad_index;
m_node_index = node_index;
m_predecessor = -1;
m_distance_from_start = 0;
m_distance_from_start = -1.0f;
const Quad &quad = m_all_quads->getQuad(m_quad_index);
// FIXME: the following values should depend on the actual orientation
const Quad &quad = QuadSet::get()->getQuad(m_quad_index);
// The following values should depend on the actual orientation
// of the quad. ATM we always assume that indices 0,1 are the lower end,
// and 2,3 are the upper end.
// and 2,3 are the upper end (or the reverse if reverse mode is selected).
// 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;
m_lower_center = (quad[0]+quad[1]) * 0.5f;
m_upper_center = (quad[2]+quad[3]) * 0.5f;
if(QuadGraph::get()->isReverse())
{
m_lower_center = (quad[2]+quad[3]) * 0.5f;
m_upper_center = (quad[0]+quad[1]) * 0.5f;
}
else
{
m_lower_center = (quad[0]+quad[1]) * 0.5f;
m_upper_center = (quad[2]+quad[3]) * 0.5f;
}
m_line = core::line2df(m_lower_center.getX(), m_lower_center.getZ(),
m_upper_center.getX(), m_upper_center.getZ() );
// Only this 2d point is needed later
m_lower_center_2d = core::vector2df(m_lower_center.getX(),
m_lower_center.getZ() );
// The vector from the center of the quad to the middle of the right
// side of the quad
m_center_to_right = (quad[1]+quad[2])*0.5f - quad.getCenter();
} // GraphNode
// ----------------------------------------------------------------------------
@ -75,50 +71,22 @@ GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index)
void GraphNode::addSuccessor(unsigned int to)
{
m_successor_node.push_back(to);
// m_quad_index is the quad index, so we use m_all_quads
const Quad &this_quad = m_all_quads->getQuad(m_quad_index);
// to is the graph node, so we have to use m_all_nodes to get the right quad
GraphNode &gn = m_all_nodes->getNode(to);
const Quad &next_quad = m_all_nodes->getQuadOfNode(to);
// m_quad_index is the quad index
const Quad &this_quad = QuadSet::get()->getQuad(m_quad_index);
// to is the graph node
GraphNode &gn = QuadGraph::get()->getNode(to);
const Quad &next_quad = QuadGraph::get()->getQuadOfNode(to);
// Keep the first predecessor, which is usually the most 'natural' one.
if(gn.m_predecessor==-1)
gn.m_predecessor = m_node_index;
core::vector2df d2 = m_lower_center_2d
- m_all_nodes->getNode(to).m_lower_center_2d;
Vec3 diff = next_quad.getCenter() - this_quad.getCenter();
m_distance_to_next.push_back(d2.getLength());
float theta = atan2(diff.getX(), diff.getZ());
m_angle_to_next.push_back(theta);
Vec3 d = m_lower_center - QuadGraph::get()->getNode(to).m_lower_center;
m_distance_to_next.push_back(d.length());
Vec3 diff = next_quad.getCenter() - this_quad.getCenter();
m_angle_to_next.push_back(atan2(diff.getX(), diff.getZ()));
// The length of this quad is the average of the left and right side
float distance_to_next = ( this_quad[2].distance(this_quad[1])
+ this_quad[3].distance(this_quad[0]) ) *0.5f;
// The distance from start for the successor node
if(to!=0)
{
float distance_for_next = m_distance_from_start+distance_to_next;
// If the successor node does not have a distance from start defined,
// update its distance. Otherwise if the node already has a distance,
// it is potentially necessary to update its distance from start:
// without this the length of the track (as taken by the distance
// from start of the last node) could be smaller than some of the
// paths. This can result in incorrect results for the arrival time
// estimation of the AI karts. See trac #354 for details.
if(m_all_nodes->getNode(to).m_distance_from_start==0)
{
m_all_nodes->getNode(to).m_distance_from_start = distance_for_next;
}
else if(m_all_nodes->getNode(to).m_distance_from_start
< distance_for_next)
{
float delta = distance_for_next
- m_all_nodes->getNode(to).getDistanceFromStart();
m_all_nodes->updateDistancesForAllSuccessors(to, delta);
}
}
} // addSuccessor
// ----------------------------------------------------------------------------
@ -203,7 +171,8 @@ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result)
result->setX( (closest-xyz2d).getLength()); // to the right
else
result->setX(-(closest-xyz2d).getLength()); // to the left
result->setZ( m_distance_from_start + (closest-m_lower_center_2d).getLength());
result->setZ( m_distance_from_start +
(closest-m_lower_center_2d).getLength());
} // getDistances
// ----------------------------------------------------------------------------

View File

@ -109,13 +109,6 @@ class GraphNode
std::vector< int > m_checkline_requirements;
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 quad_index, unsigned int node_index);
void addSuccessor (unsigned int to);
void getDistances(const Vec3 &xyz, Vec3 *result);
@ -136,14 +129,14 @@ public:
// ------------------------------------------------------------------------
/** Returns the quad of this graph node. */
const Quad& getQuad() const {return m_all_quads->getQuad(m_quad_index);}
const Quad& getQuad() const {return QuadSet::get()->getQuad(m_quad_index);}
// ------------------------------------------------------------------------
/** Returns the i-th. point of a quad. ATM this just returns the vertices
* from the quads, but if necessary this method will also consider
* rotated quads. So index 0 will always be lower left point, then
* counterclockwise. */
const Vec3& operator[](int i) const
{return m_all_quads->getQuad(m_quad_index)[i];}
{return QuadSet::get()->getQuad(m_quad_index)[i];}
// ------------------------------------------------------------------------
/** Returns the distance to the j-th. successor. */
float getDistanceToSuccessor(unsigned int j) const
@ -183,7 +176,7 @@ public:
* \param index Index of the successor. */
bool ignoreSuccessorForAI(unsigned int i) const
{
return m_all_quads->getQuad(m_successor_node[i]).letAIIgnore();
return QuadSet::get()->getQuad(m_successor_node[i]).letAIIgnore();
};
// ------------------------------------------------------------------------
/** Returns a predecessor for this node. */
@ -200,10 +193,6 @@ public:
return m_path_to_node.size()>0 ? m_path_to_node[n] : 0;
} // getSuccesorToReach
// ------------------------------------------------------------------------
/** Returns a vector from the center of the node to the middle of the
* right side. */
const Vec3 &getCenterToRightVector() const { return m_center_to_right; }
// ------------------------------------------------------------------------
/** Returns the checkline requirements of this graph node. */
const std::vector<int>& getChecklineRequirements() const
{ return m_checkline_requirements; }

View File

@ -131,3 +131,4 @@ void Quad::transform(const btTransform &t, Quad *result) const
std::min(result->m_p[2].getY(),
result->m_p[3].getY()) );
} // transform

View File

@ -75,5 +75,6 @@ public:
// ------------------------------------------------------------------------
/** True if this quad should be ignored by the AI. */
bool letAIIgnore() const { return m_ai_ignore; }
}; // class Quad
#endif

View File

@ -43,16 +43,17 @@ QuadGraph *QuadGraph::m_quad_graph = NULL;
* \param graph_file_name Name of the file describing the actual graph
*/
QuadGraph::QuadGraph(const std::string &quad_file_name,
const std::string graph_file_name)
const std::string graph_file_name,
const bool reverse) : m_reverse(reverse)
{
m_node = NULL;
m_mesh = NULL;
m_mesh_buffer = NULL;
m_lap_length = 0;
m_all_quads = new QuadSet(quad_file_name);
QuadSet::create();
QuadSet::get()->init(quad_file_name);
m_quad_filename = quad_file_name;
GraphNode::m_all_quads = m_all_quads;
GraphNode::m_all_nodes = this;
m_quad_graph = this;
load(graph_file_name);
} // QuadGraph
@ -60,7 +61,7 @@ QuadGraph::QuadGraph(const std::string &quad_file_name,
/** Destructor, removes all nodes of the graph. */
QuadGraph::~QuadGraph()
{
delete m_all_quads;
QuadSet::destroy();
for(unsigned int i=0; i<m_all_nodes.size(); i++) {
delete m_all_nodes[i];
}
@ -68,6 +69,15 @@ QuadGraph::~QuadGraph()
cleanupDebugMesh();
} // ~QuadGraph
// -----------------------------------------------------------------------------
void QuadGraph::addSuccessor(unsigned int from, unsigned int to) {
if(m_reverse)
m_all_nodes[to]->addSuccessor(from);
else
m_all_nodes[from]->addSuccessor(to);
} // addSuccessor
// -----------------------------------------------------------------------------
/** Loads a quad graph from a file.
* \param filename Name of the file to load.
@ -81,7 +91,7 @@ void QuadGraph::load(const std::string &filename)
// 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++)
for(unsigned int i=0; i<QuadSet::get()->getNumberOfQuads(); i++)
m_all_nodes.push_back(new GraphNode(i, m_all_nodes.size()));
// Then set the default loop:
setDefaultSuccessors();
@ -137,7 +147,8 @@ void QuadGraph::load(const std::string &filename)
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);
addSuccessor(i,(i!=to ? i+1 : from));
//~ m_all_nodes[i]->addSuccessor(i!=to ? i+1 : from);
}
}
else if(xml_node->getName()=="edge-line")
@ -148,7 +159,8 @@ void QuadGraph::load(const std::string &filename)
xml_node->get("to", &to);
for(unsigned int i=from; i<to; i++)
{
m_all_nodes[i]->addSuccessor(i+1);
addSuccessor(i,i+1);
//~ m_all_nodes[i]->addSuccessor(i+1);
}
}
else if(xml_node->getName()=="edge")
@ -158,7 +170,8 @@ void QuadGraph::load(const std::string &filename)
xml_node->get("from", &from);
xml_node->get("to", &to);
assert(to<m_all_nodes.size());
m_all_nodes[from]->addSuccessor(to);
addSuccessor(from,to);
//~ m_all_nodes[from]->addSuccessor(to);
} // edge
else
{
@ -169,70 +182,62 @@ void QuadGraph::load(const std::string &filename)
}
delete xml;
// Define the track length
setDefaultSuccessors();
computeDistanceFromStart(getStartNode(), 0.0f);
// Define the track length as the maximum at the end of a quad
// (i.e. distance_from_start + length till successor 0).
m_lap_length = -1;
for(unsigned int i=0; i<m_all_nodes.size(); i++)
{
if(m_all_nodes[i]->getSuccessor(0)==0)
{
m_lap_length = m_all_nodes[i]->getDistanceFromStart()
float l = m_all_nodes[i]->getDistanceFromStart()
+ m_all_nodes[i]->getDistanceToSuccessor(0);
break;
}
if(l > m_lap_length)
m_lap_length = l;
}
setDefaultSuccessors();
} // load
// ----------------------------------------------------------------------------
/**
* Finds which checklines must be visited before driving on this quad
* (useful for rescue)
*/
void QuadGraph::setChecklineRequirements(GraphNode* node, int latest_checkline)
/** Returns the index of the first graph node (i.e. the graph node which
* will trigger a new lap when a kart first enters it). This is always
* 0 for normal direction (this is guaranteed by the track exporter),
* but in reverse mode (where node 0 is actually the end of the track)
* this is 0's successor.
*/
unsigned int QuadGraph::getStartNode() const
{
Track* t = World::getWorld()->getTrack();
CheckManager* cm = t->getCheckManager();
assert(cm != NULL);
// Find lapline
if (latest_checkline == -1)
{
for (int i=0; i<cm->getCheckStructureCount(); i++)
{
CheckStructure* c = cm->getCheckStructure(i);
if (dynamic_cast<CheckLap*>(c) != NULL)
{
latest_checkline = i;
break;
}
}
}
return m_reverse ? m_all_nodes[0]->getSuccessor(0)
: 0;
} // getStartNode
// ----------------------------------------------------------------------------
void QuadGraph::computeChecklineRequirements()
{
computeChecklineRequirements(m_all_nodes[0],
CheckManager::get()->getLapLineIndex());
} // computeChecklineRequirements
// ----------------------------------------------------------------------------
/** Finds which checklines must be visited before driving on this quad
* (useful for rescue)
*/
void QuadGraph::computeChecklineRequirements(GraphNode* node,
int latest_checkline)
{
for (unsigned int n=0; n<node->getNumberOfSuccessors(); n++)
{
const int succ_id = node->getSuccessor(n);
// warp-around
if (succ_id == 0) break;
int new_latest_checkline = latest_checkline;
GraphNode* succ = m_all_nodes[succ_id];
for (int i=0; i<cm->getCheckStructureCount(); i++)
{
CheckStructure* c = cm->getCheckStructure(i);
// skip lapline
if (dynamic_cast<CheckLap*>(c) != NULL) continue;
if (c->isTriggered(node->getCenter(), succ->getCenter(), 0 /* kart id */))
{
new_latest_checkline = i;
break;
}
}
GraphNode* succ = m_all_nodes[succ_id];
int new_latest_checkline =
CheckManager::get()->getChecklineTriggering(node->getCenter(),
succ->getCenter() );
if(new_latest_checkline==-1)
new_latest_checkline = latest_checkline;
/*
printf("Quad %i : checkline %i\n", succ_id, new_latest_checkline);
@ -246,9 +251,9 @@ void QuadGraph::setChecklineRequirements(GraphNode* node, int latest_checkline)
if (new_latest_checkline != -1)
succ->setChecklineRequirements(new_latest_checkline);
setChecklineRequirements(succ, new_latest_checkline);
computeChecklineRequirements(succ, new_latest_checkline);
}
}
} // computeChecklineRequirements
// ----------------------------------------------------------------------------
/** This function defines the "path-to-nodes" for each graph node that has
@ -280,7 +285,8 @@ 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);
addSuccessor(i,i+1>=m_all_nodes.size() ? 0 : i+1);
//~ m_all_nodes[i]->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1);
} // if size==0
} // for i<m_allNodes.size()
} // setDefaultSuccessors
@ -313,8 +319,9 @@ void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
float sidewards_distance,
float upwards_distance) const
{
// Node 0 is always the node on which the start line is.
int current_node = getNode(0).getPredecessor();
// We start just before the start node (which will trigger lap
// counting when reached).
int current_node = m_all_nodes[getStartNode()]->getPredecessor();
float distance_from_start = 0.1f+forwards_distance;
@ -432,7 +439,7 @@ void QuadGraph::createMesh(bool show_invisible,
ind[6*i+4] = 4*i+2;
ind[6*i+5] = 4*i+3;
i++;
} // for i=1; i<m_all_quads
} // for i=1; i<QuadSet::get()
m_mesh_buffer->append(new_v, n*4, ind, n*6);
@ -446,7 +453,7 @@ void QuadGraph::createMesh(bool show_invisible,
// Now scale the length (distance between vertix 0 and 3
// and between 1 and 2) to be 'length':
Vec3 bb_min, bb_max;
m_all_quads->getBoundingBox(&bb_min, &bb_max);
QuadSet::get()->getBoundingBox(&bb_min, &bb_max);
// Length of the lap line about 3% of the 'height'
// of the track.
const float length=(bb_max.getZ()-bb_min.getZ())*0.03f;
@ -559,7 +566,57 @@ void QuadGraph::getSuccessors(int node_number,
} // getSuccessors
// ----------------------------------------------------------------------------
/** Increases
/** Recursively determines the distance the beginning (lower end) of the quads
* have from the start of the track.
* \param node The node index for which to set the distance from start.
* \param new_distance The new distance for the specified graph node.
*/
void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance)
{
GraphNode *gn = m_all_nodes[node];
float current_distance = gn->getDistanceFromStart();
// If this node already has a distance defined, check if the new distance
// is longer, and if so adjust all following nodes. Without this the
// length of the track (as taken by the distance from start of the last
// node) could be smaller than some of the paths. This can result in
// incorrect results for the arrival time estimation of the AI karts.
// See trac #354 for details.
// Then there is no need to test/adjust any more nodes.
if(current_distance>=0)
{
if(current_distance<new_distance)
{
float delta = new_distance - current_distance;
updateDistancesForAllSuccessors(gn->getIndex(), delta);
}
return;
}
// Otherwise this node has no distance defined yet. Set the new
// distance, and recursively update all following nodes.
gn->setDistanceFromStart(new_distance);
for(unsigned int i=0; i<gn->getNumberOfSuccessors(); i++)
{
GraphNode *gn_next = m_all_nodes[gn->getSuccessor(i)];
// The start node (only node with distance 0) is reached again,
// recursion can stop now
if(gn_next->getDistanceFromStart()==0)
continue;
computeDistanceFromStart(gn_next->getIndex(),
new_distance + gn->getDistanceToSuccessor(i));
} // for i
} // computeDistanceFromStart
// ----------------------------------------------------------------------------
/** Increases the distance from start for all nodes that are directly or
* indirectly a successor of the given node. This code is used when two
* branches merge together, but since the latest 'fork' indicates a longer
* distance from start.
* \param indx Index of the node for which to increase the distance.
* \param delta Amount by which to increase the distance.
*/
void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta)
{
@ -568,11 +625,9 @@ void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta)
for(unsigned int i=0; i<g.getNumberOfSuccessors(); i++)
{
GraphNode &g_next = getNode(g.getSuccessor(i));
// If we reach the beginning of the graph (usually node with index 0,
// but just in case also test for nodes with distance 0), all nodes
// are updated, so no need to recurse any further.
if(g_next.getIndex()==0 ||
g_next.getDistanceFromStart()==0)
// Stop when we reach the start node, i.e. the only node with a
// distance of 0
if(g_next.getDistanceFromStart()==0)
continue;
// Only increase the distance from start of a successor node, if
@ -778,7 +833,7 @@ video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &dimension,
// ---------------
scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode();
Vec3 bb_min, bb_max;
m_all_quads->getBoundingBox(&bb_min, &bb_max);
QuadSet::get()->getBoundingBox(&bb_min, &bb_max);
Vec3 center = (bb_max+bb_min)*0.5f;
float dx = bb_max.getX()-bb_min.getX();

View File

@ -56,8 +56,6 @@ private:
/** The actual graph data structure. */
std::vector<GraphNode*> m_all_nodes;
/** The set of all quads. */
QuadSet *m_all_quads;
/** For debug mode only: the node of the debug mesh. */
scene::ISceneNode *m_node;
/** For debug only: the mesh of the debug mesh. */
@ -76,16 +74,23 @@ private:
/** Stores the filename - just used for error messages. */
std::string m_quad_filename;
/** Wether the graph should be reverted or not */
bool m_reverse;
void setDefaultSuccessors();
void setChecklineRequirements(GraphNode* node, int latest_checkline);
void computeChecklineRequirements(GraphNode* node, int latest_checkline);
void addSuccessor(unsigned int from, unsigned int to);
void load (const std::string &filename);
void computeDistanceFromStart(unsigned int start_node, float distance);
void createMesh(bool show_invisible=true,
const video::SColor *track_color=NULL,
const video::SColor *lap_color=NULL);
unsigned int getStartNode() const;
QuadGraph (const std::string &quad_file_name,
const std::string graph_file_name);
const std::string graph_file_name,
const bool reverse);
~QuadGraph ();
public:
static const int UNKNOWN_SECTOR;
@ -116,7 +121,8 @@ public:
void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const;
void updateDistancesForAllSuccessors(unsigned int indx, float delta);
void setupPaths();
// ----------------------------------------------------------------------
void computeChecklineRequirements();
// ----------------------------------------------------------------------
/** Returns the one instance of this object. It is possible that there
* is no instance created (e.g. in battle mode, since it doesn't have
* a quad graph), so we don't assert that an instance exist, and we
@ -125,10 +131,13 @@ public:
// ----------------------------------------------------------------------
/** Creates a QuadGraph instance. */
static void create(const std::string &quad_file_name,
const std::string graph_file_name)
const std::string graph_file_name,
const bool reverse)
{
assert(m_quad_graph==NULL);
m_quad_graph = new QuadGraph(quad_file_name, graph_file_name);
// assignment to m_quad_graph is done in the constructor, since
// functions called from the constructor need it to be defined.
new QuadGraph(quad_file_name, graph_file_name, reverse);
} // create
// ----------------------------------------------------------------------
/** Cleans up the quad graph. It is possible that this function is called
@ -161,7 +170,7 @@ public:
// ----------------------------------------------------------------------
/** Returns the quad that belongs to a graph node. */
const Quad& getQuadOfNode(unsigned int j) const
{ return m_all_quads->getQuad(m_all_nodes[j]->getIndex()); }
{ return QuadSet::get()->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]; }
@ -173,11 +182,8 @@ public:
/** Returns the length of the main driveline. */
float getLapLength() const {return m_lap_length; }
// ----------------------------------------------------------------------
void setChecklineRequirements()
{
setChecklineRequirements(m_all_nodes[0], -1);
}
/** Returns true if the graph is to be reversed. */
bool isReverse() const {return m_reverse; }
}; // QuadGraph

View File

@ -19,16 +19,20 @@
#include "tracks/quad_set.hpp"
#include <stdlib.h>
#include <algorithm>
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "utils/string_utils.hpp"
/** Constructor, loads the quad set from a file.
* \param filename filename to load.
QuadSet *QuadSet::m_quad_set = NULL;
/** Constructor, loads the quad set from a file. Assigns a pointer
* to this instance to m_quad_set, so that it can be accessed using get().
*/
QuadSet::QuadSet(const std::string& filename) {
load(filename);
QuadSet::QuadSet()
{
m_quad_set = this;
} // QuadSet
// -----------------------------------------------------------------------------
@ -41,9 +45,10 @@ QuadSet::~QuadSet()
delete m_all_quads[i];
}
m_all_quads.clear();
m_quad_set = NULL;
} // ~QuadSet
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------}
/** This function interprets a point specification as an attribute in the
xml quadset file. It understands two different specifications:
p1="n:p" : get point p from square n (n, p integers)
@ -69,7 +74,10 @@ void QuadSet::getPoint(const XMLNode *xml, const std::string &attribute_name,
} // getPoint
// -----------------------------------------------------------------------------
void QuadSet::load(const std::string &filename)
/** Loads the set of all quads from the speciified filename.
* \param filename The absolute filename to load the quad file from.
*/
void QuadSet::init(const std::string &filename)
{
m_min = Vec3( 99999, 99999, 99999);
m_max = Vec3(-99999, -99999, -99999);

View File

@ -36,29 +36,54 @@ private:
/** The 2d bounding box, used for hashing. */
Vec3 m_min;
Vec3 m_max;
/** The list of all quads. */
std::vector<Quad*> m_all_quads;
void load (const std::string &filename);
/** Pointer to the one instance of a quad set. */
static QuadSet *m_quad_set;
void getPoint(const XMLNode *xml, const std::string &attribute_name,
Vec3 *result) const;
QuadSet();
~QuadSet();
public:
static const int QUAD_NONE=-1;
QuadSet (const std::string& filename);
~QuadSet ();
int getCurrentQuad(const Vec3& p, int oldQuad) const;
void init (const std::string &filename);
// ------------------------------------------------------------------------
/** Creates one instance of the quad set. */
static void create()
{
assert(m_quad_set==NULL);
m_quad_set = new QuadSet();
}
// ------------------------------------------------------------------------
/** Destroys the one instance of a quad set. */
static void destroy()
{
delete m_quad_set; m_quad_set = NULL;
}
// ------------------------------------------------------------------------
/** Static member function to access the QuadSet instance. */
static QuadSet *get() { return m_quad_set; }
// ------------------------------------------------------------------------
/** Returns the quad with a given index number. */
const Quad& getQuad(int n) const {return *(m_all_quads[n]); }
// ------------------------------------------------------------------------
/** Return the minimum and maximum coordinates of this quad set. */
void getBoundingBox(Vec3 *min, Vec3 *max)
{ *min=m_min; *max=m_max; }
// ------------------------------------------------------------------------
/** Returns the number of quads. */
unsigned int getNumberOfQuads() const
{return (unsigned int)m_all_quads.size(); }
// ------------------------------------------------------------------------
/** Returns the center of quad n. */
const Vec3& getCenterOfQuad(int n) const
{return m_all_quads[n]->getCenter(); }
// ------------------------------------------------------------------------
/** Returns the n-th. quad. */
const Quad& getQuad(int n) {return *(m_all_quads[n]); }
}; // QuadSet

View File

@ -86,11 +86,11 @@ Track::Track(const std::string &filename)
m_track_mesh = NULL;
m_gfx_effect_mesh = NULL;
m_internal = false;
m_reverse_available = true;
m_all_nodes.clear();
m_all_cached_meshes.clear();
m_is_arena = false;
m_camera_far = 1000.0f;
m_check_manager = NULL;
m_mini_map = NULL;
m_sky_particles = NULL;
m_sky_dx = 0.05f;
@ -120,8 +120,7 @@ Track::~Track()
void Track::reset()
{
m_ambient_color = m_default_ambient_color;
if(m_check_manager)
m_check_manager->reset(*this);
CheckManager::get()->reset(*this);
item_manager->reset();
m_track_object_manager->reset();
} // reset
@ -151,11 +150,7 @@ void Track::cleanup()
m_all_emitters.clearAndDeleteAll();
if(m_check_manager)
{
delete m_check_manager;
m_check_manager=NULL;
}
CheckManager::destroy();
delete m_track_object_manager;
m_track_object_manager = NULL;
@ -204,7 +199,7 @@ void Track::cleanup()
m_detached_cached_meshes.clear();
QuadGraph::destroy();
if(m_check_manager) delete m_check_manager;
if(m_mini_map)
{
assert(m_mini_map->getReferenceCount()==1);
@ -306,6 +301,7 @@ void Track::loadTrackInfo()
root->get("arena", &m_is_arena);
root->get("groups", &m_groups);
root->get("internal", &m_internal);
root->get("reverse", &m_reverse_available);
for(unsigned int i=0; i<root->getNumNodes(); i++)
{
@ -392,10 +388,11 @@ void Track::startMusic() const
/** Loads the quad graph, i.e. the definition of all quads, and the way
* they are connected to each other.
*/
void Track::loadQuadGraph(unsigned int mode_id)
void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
{
QuadGraph::create(m_root+"/"+m_all_modes[mode_id].m_quad_name,
m_root+"/"+m_all_modes[mode_id].m_graph_name);
m_root+"/"+m_all_modes[mode_id].m_graph_name,
reverse);
QuadGraph::get()->setupPaths();
#ifdef DEBUG
@ -1070,8 +1067,7 @@ void Track::update(float dt)
{
m_animated_textures[i]->update(dt);
}
if(m_check_manager)
m_check_manager->update(dt);
CheckManager::get()->update(dt);
item_manager->update(dt);
} // update
@ -1164,8 +1160,14 @@ void Track::createWater(const XMLNode &node)
* scene, quad, and graph file to load.
*/
void Track::loadTrackModel(World* parent, unsigned int mode_id)
void Track::loadTrackModel(World* parent, bool reverse_track,
unsigned int mode_id )
{
if(!m_reverse_available)
{
reverse_track = false;
}
CheckManager::create();
assert(m_all_cached_meshes.size()==0);
if(UserConfigParams::logMemory())
{
@ -1229,7 +1231,7 @@ void Track::loadTrackModel(World* parent, unsigned int mode_id)
// the race gui was created. The race gui is needed since it stores
// the information about the size of the texture to render the mini
// map to.
if (!m_is_arena) loadQuadGraph(mode_id);
if (!m_is_arena) loadQuadGraph(mode_id, reverse_track);
// Set the default start positions. Node that later the default
// positions can still be overwritten.
@ -1330,7 +1332,7 @@ void Track::loadTrackModel(World* parent, unsigned int mode_id)
}
else if(name=="checks")
{
m_check_manager = new CheckManager(*node, this);
CheckManager::get()->load(*node);
}
else if (name=="particle-emitter")
{
@ -1564,7 +1566,8 @@ void Track::loadTrackModel(World* parent, unsigned int mode_id)
// Only print warning if not in battle mode, since battle tracks don't have
// any quads or check lines.
if(!m_check_manager && race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES)
if(CheckManager::get()->getCheckStructureCount()==0 &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES)
{
printf("WARNING: no check lines found in track '%s'.\n",
m_ident.c_str());
@ -1577,9 +1580,9 @@ void Track::loadTrackModel(World* parent, unsigned int mode_id)
irr_driver->getSceneManager()->getMeshCache()->getMeshCount(),
irr_driver->getVideoDriver()->getTextureCount());
if (World::getWorld()->computeChecklineRequirements())
if (World::getWorld()->useChecklineRequirements())
{
QuadGraph::get()->setChecklineRequirements();
QuadGraph::get()->computeChecklineRequirements();
}
} // loadTrackModel
@ -1858,8 +1861,8 @@ std::vector< std::vector<float> > Track::buildHeightMap()
}
// ----------------------------------------------------------------------------
core::vector3df Track::getSunRotation()
/** Returns the rotation of the sun. */
const core::vector3df& Track::getSunRotation()
{
return m_sun->getRotation();
}

View File

@ -185,6 +185,9 @@ private:
* in the track seelction screen
*/
bool m_internal;
/** Whether this track should be available in reverse version */
bool m_reverse_available;
/** The type of sky to be used for the track. */
enum {SKY_NONE, SKY_BOX,
@ -282,13 +285,10 @@ private:
/** List of all bezier curves in the track - for e.g. camera, ... */
std::vector<BezierCurve*> m_all_curves;
/** Checkline manager. */
CheckManager *m_check_manager;
void loadTrackInfo();
void itemCommand(const Vec3 &xyz, Item::ItemType item_type,
bool drop);
void loadQuadGraph(unsigned int mode_id);
void loadQuadGraph(unsigned int mode_id, const bool reverse);
void convertTrackToBullet(scene::ISceneNode *node);
bool loadMainTrack(const XMLNode &node);
void createWater(const XMLNode &node);
@ -300,6 +300,8 @@ private:
public:
bool reverseAvailable() { return m_reverse_available; }
static const float NOHIT;
Track (const std::string &filename);
@ -313,7 +315,7 @@ public:
void reset();
void adjustForFog(scene::ISceneNode *node);
void adjustForFog(scene::IMesh* mesh, scene::ISceneNode* parent_scene_node);
const core::vector3df& getSunRotation();
/** Sets the current ambient color for a kart with index k. */
void setAmbientColor(const video::SColor &color,
unsigned int k);
@ -327,7 +329,9 @@ public:
// ------------------------------------------------------------------------
bool isArena () const { return m_is_arena; }
// ------------------------------------------------------------------------
void loadTrackModel (World* parent, unsigned int mode_id=0);
void loadTrackModel (World* parent,
bool reverse_track = false,
unsigned int mode_id=0);
// ------------------------------------------------------------------------
void addMusic (MusicInformation* mi)
{m_music.push_back(mi); }
@ -429,17 +433,13 @@ public:
ParticleKind* getSkyParticles () { return m_sky_particles; }
// ------------------------------------------------------------------------
bool isFogEnabled() const { return m_use_fog; }
CheckManager* getCheckManager() { return m_check_manager; }
// ------------------------------------------------------------------------
/** Whether this is an "internal" track. If so it won't be offered
* in the track seelction screen
*/
* in the track seelction screen. */
bool isInternal() const { return m_internal; }
core::vector3df getSunRotation();
TrackObjectManager* getTrackObjectManager() { return m_track_object_manager; }
// ------------------------------------------------------------------------
TrackObjectManager* getTrackObjectManager() {return m_track_object_manager;}
/** Get list of challenges placed on that world. Works only for overworld. */
const std::vector<OverworldChallenge>& getChallengeList() const