Merge branch 'master' into renderer_refactoring
This commit is contained in:
commit
a21d7b4b71
24
.travis.yml
24
.travis.yml
@ -21,17 +21,21 @@ addons:
|
||||
apt:
|
||||
packages:
|
||||
- build-essential
|
||||
- libogg-dev
|
||||
- libvorbis-dev
|
||||
- libopenal-dev
|
||||
- libxxf86vm-dev
|
||||
- libcurl4-openssl-dev
|
||||
- libfribidi-dev
|
||||
- libbluetooth-dev
|
||||
- libgl1-mesa-dev
|
||||
- libglu1-mesa-dev
|
||||
- libglew-dev
|
||||
- cmake
|
||||
- libbluetooth-dev
|
||||
- libcurl4-gnutls-dev
|
||||
- libfreetype6-dev
|
||||
- libfribidi-dev
|
||||
- libgl1-mesa-dev
|
||||
- libjpeg-dev
|
||||
- libogg-dev
|
||||
- libopenal-dev
|
||||
- libpng-dev
|
||||
- libvorbis-dev
|
||||
- libxrandr-dev
|
||||
- mesa-common-dev
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
|
||||
before_script:
|
||||
- export THREADS=$((`nproc` + 1))
|
||||
|
@ -25,7 +25,7 @@
|
||||
against the computer or your friends, and more!
|
||||
</p>
|
||||
</description>
|
||||
<url type="homepage">http://supertuxkart.sourceforge.net/</url>
|
||||
<url type="homepage">http://supertuxkart.net/</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">http://supertuxkart.sourceforge.net/persistent/images/4/4e/Supertuxkart-0.9-screenshot-2.jpg</screenshot>
|
||||
<screenshot>http://supertuxkart.sourceforge.net/persistent/images/a/a9/Supertuxkart-0.9-screenshot-1.jpg</screenshot>
|
||||
|
@ -12,7 +12,7 @@ Coding style
|
||||
============
|
||||
|
||||
The coding style used in Super Tux Kart can be found at
|
||||
http://supertuxkart.sourceforge.net/Coding_Style
|
||||
http://supertuxkart.net/Coding_Style
|
||||
|
||||
|
||||
Documentation line length
|
||||
|
@ -52,17 +52,21 @@ void FontManager::loadFonts()
|
||||
m_digit_ttf = new FaceTTF(stk_config->m_digit_ttf);
|
||||
|
||||
// Now load fonts with settings of ttf file
|
||||
unsigned int font_loaded = 0;
|
||||
RegularFace* regular = new RegularFace(m_normal_ttf);
|
||||
regular->init();
|
||||
m_fonts.push_back(regular);
|
||||
m_font_type_map[std::type_index(typeid(RegularFace))] = font_loaded++;
|
||||
|
||||
BoldFace* bold = new BoldFace(m_normal_ttf);
|
||||
bold->init();
|
||||
m_fonts.push_back(bold);
|
||||
m_font_type_map[std::type_index(typeid(BoldFace))] = font_loaded++;
|
||||
|
||||
DigitFace* digit = new DigitFace(m_digit_ttf);
|
||||
digit->init();
|
||||
m_fonts.push_back(digit);
|
||||
m_font_type_map[std::type_index(typeid(DigitFace))] = font_loaded++;
|
||||
} // loadFonts
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
@ -35,13 +37,15 @@ class FontWithFace;
|
||||
class FontManager : public NoCopy
|
||||
{
|
||||
private:
|
||||
PtrVector<FontWithFace> m_fonts;
|
||||
PtrVector<FontWithFace> m_fonts;
|
||||
|
||||
FT_Library m_ft_library;
|
||||
FT_Library m_ft_library;
|
||||
|
||||
FaceTTF* m_normal_ttf;
|
||||
FaceTTF* m_normal_ttf;
|
||||
|
||||
FaceTTF* m_digit_ttf;
|
||||
FaceTTF* m_digit_ttf;
|
||||
|
||||
std::unordered_map<std::type_index, int> m_font_type_map;
|
||||
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
@ -53,11 +57,11 @@ public:
|
||||
template <typename T> T* getFont()
|
||||
{
|
||||
T* out = NULL;
|
||||
for (unsigned int i = 0; i < m_fonts.size(); i++)
|
||||
const unsigned int n = m_font_type_map[std::type_index(typeid(T))];
|
||||
out = dynamic_cast<T*>(m_fonts.get(n));
|
||||
if (out != NULL)
|
||||
{
|
||||
out = dynamic_cast<T*>(m_fonts.get(i));
|
||||
if (out != NULL)
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
Log::fatal("FontManager", "Can't get a font!");
|
||||
return out;
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "guiengine/skin.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include <freetype/ftoutln.h>
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
|
||||
@ -145,10 +145,11 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index,
|
||||
FT_LOAD_DEFAULT), "loading a glyph");
|
||||
|
||||
if (dynamic_cast<BoldFace*>(this))
|
||||
if (dynamic_cast<BoldFace*>(this) != NULL)
|
||||
{
|
||||
// Embolden the outline of the glyph
|
||||
FT_Outline_Embolden(&(slot->outline), getDPI() * 2);
|
||||
font_manager->checkFTError(FT_Outline_Embolden(&(slot->outline),
|
||||
getDPI() * 2), "embolden a glyph");
|
||||
}
|
||||
|
||||
font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL),
|
||||
@ -321,7 +322,7 @@ void FontWithFace::dumpGlyphPage()
|
||||
// ----------------------------------------------------------------------------
|
||||
void FontWithFace::setDPI()
|
||||
{
|
||||
// Get face dpi:
|
||||
// Set face dpi:
|
||||
// Font size is resolution-dependent.
|
||||
// Normal text will range from 0.8, in 640x* resolutions (won't scale
|
||||
// below that) to 1.0, in 1024x* resolutions, and linearly up
|
||||
@ -445,7 +446,7 @@ void FontWithFace::render(const core::stringw& text,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector)
|
||||
{
|
||||
const bool is_bold_face = dynamic_cast<BoldFace*>(this);
|
||||
const bool is_bold_face = (dynamic_cast<BoldFace*>(this) != NULL);
|
||||
const bool black_border = font_settings ?
|
||||
font_settings->useBlackBorder() : false;
|
||||
const bool rtl = font_settings ? font_settings->isRTL() : false;
|
||||
@ -513,7 +514,7 @@ void FontWithFace::render(const core::stringw& text,
|
||||
if (c==L'\r' && text[i+1]==L'\n')
|
||||
c = text[++i];
|
||||
offset.Y += m_font_max_height * scale;
|
||||
offset.X = position.UpperLeftCorner.X;
|
||||
offset.X = float(position.UpperLeftCorner.X);
|
||||
if (hcenter)
|
||||
offset.X += (position.getWidth() - text_dimension.Width) >> 1;
|
||||
continue;
|
||||
|
@ -19,8 +19,9 @@
|
||||
|
||||
#include "graphics/material.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "audio/sfx_base.hpp"
|
||||
#include "audio/sfx_buffer.hpp"
|
||||
|
@ -407,7 +407,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
|
||||
{
|
||||
new_powerup = powerup_manager->getRandomPowerup(position, &n);
|
||||
if(new_powerup != PowerupManager::POWERUP_RUBBERBALL ||
|
||||
( World::getWorld()->getTime() - powerup_manager->getBallCollectTime()) >
|
||||
( World::getWorld()->getTimeSinceStart() - powerup_manager->getBallCollectTime()) >
|
||||
RubberBall::getTimeBetweenRubberBalls() )
|
||||
break;
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ void AIBaseController::crashed(const Material *m)
|
||||
const unsigned int NUM_COLLISION = 3;
|
||||
const float COLLISION_TIME = 1.5f;
|
||||
|
||||
float time = World::getWorld()->getTime();
|
||||
float time = World::getWorld()->getTimeSinceStart();
|
||||
if(m_collision_times.size()==0)
|
||||
{
|
||||
m_collision_times.push_back(time);
|
||||
|
@ -1001,7 +1001,7 @@ void Kart::collectedItem(Item *item, int add_info)
|
||||
*/
|
||||
float Kart::getStartupBoost() const
|
||||
{
|
||||
float t = World::getWorld()->getTime();
|
||||
float t = World::getWorld()->getTimeSinceStart();
|
||||
std::vector<float> startup_times = m_kart_properties->getStartupTime();
|
||||
for (unsigned int i = 0; i < startup_times.size(); i++)
|
||||
{
|
||||
@ -1991,9 +1991,9 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
|
||||
*/
|
||||
void Kart::playCrashSFX(const Material* m, AbstractKart *k)
|
||||
{
|
||||
if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return;
|
||||
if(World::getWorld()->getTimeSinceStart()-m_time_last_crash < 0.5f) return;
|
||||
|
||||
m_time_last_crash = World::getWorld()->getTime();
|
||||
m_time_last_crash = World::getWorld()->getTimeSinceStart();
|
||||
// After a collision disable the engine for a short time so that karts
|
||||
// can 'bounce back' a bit (without this the engine force will prevent
|
||||
// karts from bouncing back, they will instead stuck towards the obstable).
|
||||
|
14
src/main.cpp
14
src/main.cpp
@ -194,6 +194,7 @@
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "states_screens/user_screen.hpp"
|
||||
#include "states_screens/dialogs/message_dialog.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/command_line.hpp"
|
||||
@ -591,7 +592,7 @@ void cmdLineHelp()
|
||||
" --shadows=n Set shadow quality (0 to disable shadows).\n"
|
||||
"\n"
|
||||
"You can visit SuperTuxKart's homepage at "
|
||||
"http://supertuxkart.sourceforge.net\n\n",
|
||||
"http://supertuxkart.net\n\n",
|
||||
CommandLine::getExecName().c_str()
|
||||
);
|
||||
} // cmdLineHelp
|
||||
@ -1843,12 +1844,12 @@ void runUnitTests()
|
||||
{
|
||||
Log::info("UnitTest", "Starting unit testing");
|
||||
Log::info("UnitTest", "=====================");
|
||||
Log::info("UnitTest", "- GraphicsRestrictions");
|
||||
Log::info("UnitTest", "GraphicsRestrictions");
|
||||
GraphicsRestrictions::unitTesting();
|
||||
Log::info("UnitTest", " - NetworkString");
|
||||
Log::info("UnitTest", "NetworkString");
|
||||
NetworkString::unitTesting();
|
||||
|
||||
Log::info("UnitTest", " - Easter detection");
|
||||
Log::info("UnitTest", "Easter detection");
|
||||
// Test easter mode: in 2015 Easter is 5th of April - check with 0 days
|
||||
// before and after
|
||||
int saved_easter_mode = UserConfigParams::m_easter_ear_mode;
|
||||
@ -1880,9 +1881,12 @@ void runUnitTests()
|
||||
UserConfigParams::m_easter_ear_mode = saved_easter_mode;
|
||||
|
||||
|
||||
Log::info("UnitTest", " - Kart characteristics");
|
||||
Log::info("UnitTest", "Kart characteristics");
|
||||
CombinedCharacteristic::unitTesting();
|
||||
|
||||
Log::info("UnitTest", "Battle Graph");
|
||||
BattleGraph::unitTesting();
|
||||
|
||||
Log::info("UnitTest", "=====================");
|
||||
Log::info("UnitTest", "Testing successful ");
|
||||
Log::info("UnitTest", "=====================");
|
||||
|
@ -113,15 +113,6 @@ const btTransform &FollowTheLeaderRace::getStartTransform(int index)
|
||||
return m_track->getStartTransform(start_index);
|
||||
} // getStartTransform
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the original time at which the countdown timer started. This is
|
||||
* used by the race_gui to display the music credits in FTL mode correctly.
|
||||
*/
|
||||
float FollowTheLeaderRace::getClockStartTime() const
|
||||
{
|
||||
return m_leader_intervals[0];
|
||||
} // getClockStartTime
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a kart must be eliminated.
|
||||
*/
|
||||
|
@ -49,7 +49,6 @@ public:
|
||||
virtual void reset() OVERRIDE;
|
||||
virtual const std::string& getIdent() const OVERRIDE;
|
||||
virtual const btTransform &getStartTransform(int index) OVERRIDE;
|
||||
virtual float getClockStartTime() const;
|
||||
virtual void getKartsDisplayInfo(
|
||||
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
|
||||
virtual void init() OVERRIDE;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "karts/controller/soccer_ai.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
|
@ -17,9 +17,6 @@
|
||||
|
||||
#include "modes/three_strikes_battle.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
#include "main_loop.hpp"
|
||||
#include "audio/music_manager.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
@ -31,10 +28,14 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Constructor. Sets up the clock mode etc.
|
||||
*/
|
||||
|
@ -461,6 +461,7 @@ World::~World()
|
||||
race_manager->setRaceGhostKarts(false);
|
||||
race_manager->setRecordRace(false);
|
||||
race_manager->setWatchingReplay(false);
|
||||
race_manager->setTimeTarget(0.0f);
|
||||
|
||||
Camera::removeAllCameras();
|
||||
|
||||
|
@ -70,7 +70,8 @@ void WorldStatus::reset()
|
||||
{
|
||||
m_time = 0.0f;
|
||||
m_auxiliary_timer = 0.0f;
|
||||
|
||||
m_count_up_timer = 0.0f;
|
||||
|
||||
m_engines_started = false;
|
||||
|
||||
// Using SETUP_PHASE will play the track into sfx first, and has no
|
||||
@ -340,16 +341,19 @@ void WorldStatus::update(const float dt)
|
||||
{
|
||||
case CLOCK_CHRONO:
|
||||
m_time += dt;
|
||||
m_count_up_timer += dt;
|
||||
break;
|
||||
case CLOCK_COUNTDOWN:
|
||||
// stop countdown when race is over
|
||||
if (m_phase == RESULT_DISPLAY_PHASE || m_phase == FINISH_PHASE)
|
||||
{
|
||||
m_time = 0.0f;
|
||||
m_count_up_timer = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
m_time -= dt;
|
||||
m_count_up_timer += dt;
|
||||
|
||||
if(m_time <= 0.0)
|
||||
{
|
||||
|
@ -114,6 +114,8 @@ private:
|
||||
*/
|
||||
float m_auxiliary_timer;
|
||||
|
||||
float m_count_up_timer;
|
||||
|
||||
bool m_engines_started;
|
||||
void startEngines();
|
||||
public:
|
||||
@ -172,6 +174,10 @@ public:
|
||||
/** Called when the race actually starts. */
|
||||
virtual void onGo() {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Get the time since start regardless of which way the clock counts */
|
||||
float getTimeSinceStart() const { return m_count_up_timer; }
|
||||
|
||||
}; // WorldStatus
|
||||
|
||||
|
||||
|
@ -41,7 +41,7 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "karts/rescue_animation.hpp"
|
||||
#include "modes/follow_the_leader.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
@ -457,15 +457,7 @@ void RaceGUIBase::drawGlobalMusicDescription()
|
||||
|
||||
gui::IGUIFont* font = GUIEngine::getFont();
|
||||
|
||||
float race_time = World::getWorld()->getTime();
|
||||
// In the modes that the clock counts backwards, convert the
|
||||
// countdown time to time since start:
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
||||
race_time = ((FollowTheLeaderRace*)World::getWorld())->getClockStartTime()
|
||||
- race_time;
|
||||
else if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER &&
|
||||
race_manager->hasTimeTarget())
|
||||
race_time = race_manager->getTimeTarget() - World::getWorld()->getTime();
|
||||
float race_time = World::getWorld()->getTimeSinceStart();
|
||||
|
||||
// ---- Manage pulsing effect
|
||||
// 3.0 is the duration of ready/set (TODO: don't hardcode)
|
||||
|
@ -23,12 +23,17 @@
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <queue>
|
||||
|
||||
const int BattleGraph::UNKNOWN_POLY = -1;
|
||||
BattleGraph * BattleGraph::m_battle_graph = NULL;
|
||||
|
||||
@ -36,15 +41,19 @@ BattleGraph * BattleGraph::m_battle_graph = NULL;
|
||||
* then runs shortest path algorithm to find and store paths to be used
|
||||
* by the AI. */
|
||||
BattleGraph::BattleGraph(const std::string &navmesh_file_name,
|
||||
const XMLNode& node)
|
||||
const XMLNode *node)
|
||||
{
|
||||
m_items_on_graph.clear();
|
||||
|
||||
NavMesh::create(navmesh_file_name);
|
||||
m_navmesh_file = navmesh_file_name;
|
||||
buildGraph(NavMesh::get());
|
||||
computeFloydWarshall();
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
|
||||
// Compute shortest distance from all nodes
|
||||
for(unsigned int i=0; i < NavMesh::get()->getNumberOfPolys(); i++)
|
||||
computeDijkstra(i);
|
||||
|
||||
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
loadGoalNodes(node);
|
||||
|
||||
} // BattleGraph
|
||||
@ -59,21 +68,23 @@ BattleGraph::~BattleGraph(void)
|
||||
cleanupDebugMesh();
|
||||
} // ~BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Builds a graph from an existing NavMesh. The graph is stored as an adjacency
|
||||
* matrix. */
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Builds a graph from an existing NavMesh. The graph is stored as an
|
||||
* adjacency matrix. */
|
||||
void BattleGraph::buildGraph(NavMesh* navmesh)
|
||||
{
|
||||
unsigned int n_polys = navmesh->getNumberOfPolys();
|
||||
|
||||
m_distance_matrix = std::vector< std::vector<float> > (n_polys, std::vector<float>(n_polys, 9999.9f));
|
||||
m_distance_matrix = std::vector< std::vector<float> >
|
||||
(n_polys, std::vector<float>(n_polys, 9999.9f));
|
||||
for(unsigned int i=0; i<n_polys; i++)
|
||||
{
|
||||
NavPoly currentPoly = navmesh->getNavPoly(i);
|
||||
std::vector<int> adjacents = navmesh->getAdjacentPolys(i);
|
||||
const std::vector<int> &adjacents = navmesh->getAdjacentPolys(i);
|
||||
for(unsigned int j=0; j<adjacents.size(); j++)
|
||||
{
|
||||
Vec3 diff = navmesh->getCenterOfPoly(adjacents[j]) - currentPoly.getCenter();
|
||||
Vec3 diff = navmesh->getCenterOfPoly(adjacents[j])
|
||||
- currentPoly.getCenter();
|
||||
float distance = diff.length();
|
||||
m_distance_matrix[i][adjacents[j]] = distance;
|
||||
//m_distance_matrix[adjacents[j]][i] = distance;
|
||||
@ -81,14 +92,88 @@ void BattleGraph::buildGraph(NavMesh* navmesh)
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
// Allocate and initialise the previous node data structure:
|
||||
unsigned int n = getNumNodes();
|
||||
m_parent_poly = std::vector< std::vector<int> >
|
||||
(n, std::vector<int>(n, BattleGraph::UNKNOWN_POLY));
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j]>=9899.9f)
|
||||
m_parent_poly[i][j]=-1;
|
||||
else
|
||||
m_parent_poly[i][j] = i;
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
} // buildGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** computeFloydWarshall() computes the shortest distance between any two nodes.
|
||||
* At the end of the computation, m_distance_matrix[i][j] stores the shortest path
|
||||
* distance from i to j and m_parent_poly[i][j] stores the last vertex visited on the
|
||||
* shortest path from i to j before visiting j. Suppose the shortest path from i to j is
|
||||
* i->......->k->j then m_parent_poly[i][j] = k
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Dijkstra shortest path computation. It computes the shortest distance from
|
||||
* the specified node 'source' to all other nodes. At the end of the
|
||||
* computation, m_distance_matrix[i][j] stores the shortest path distance from
|
||||
* source to j and m_parent_poly[source][j] stores the last vertex visited on
|
||||
* the shortest path from i to j before visiting j. Suppose the shortest path
|
||||
* from i to j is i->......->k->j then m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeDijkstra(int source)
|
||||
{
|
||||
// Stores the distance (float) to 'source' from a specified node (int)
|
||||
typedef std::pair<int, float> IndDistPair;
|
||||
|
||||
class Shortest
|
||||
{
|
||||
public:
|
||||
bool operator()(const IndDistPair &p1, const IndDistPair &p2)
|
||||
{
|
||||
return p1.second > p2.second;
|
||||
}
|
||||
};
|
||||
std::priority_queue<IndDistPair, std::vector<IndDistPair>, Shortest> queue;
|
||||
IndDistPair begin(source, 0.0f);
|
||||
queue.push(begin);
|
||||
const unsigned int n=getNumNodes();
|
||||
std::vector<bool> visited;
|
||||
visited.resize(n, false);
|
||||
NavMesh *navmesh = NavMesh::get();
|
||||
while(!queue.empty())
|
||||
{
|
||||
// Get element with shortest path
|
||||
IndDistPair current = queue.top();
|
||||
queue.pop();
|
||||
int cur_index = current.first;
|
||||
if(visited[cur_index]) continue;
|
||||
visited[cur_index] = true;
|
||||
const NavPoly ¤t_poly = navmesh->getNavPoly(cur_index);
|
||||
const std::vector<int> &adjacents = current_poly.getAdjacents();
|
||||
for(unsigned int j=0; j<adjacents.size(); j++)
|
||||
{
|
||||
int adjacent = adjacents[j];
|
||||
// Distance already computed, can be ignored
|
||||
if(visited[adjacent]) continue;
|
||||
|
||||
float new_dist = current.second + m_distance_matrix[cur_index][adjacent];
|
||||
if(new_dist < m_distance_matrix[source][adjacent])
|
||||
{
|
||||
m_distance_matrix[source][adjacent] = new_dist;
|
||||
m_parent_poly[source][adjacent] = cur_index;
|
||||
}
|
||||
IndDistPair pair(adjacent, new_dist);
|
||||
queue.push(pair);
|
||||
}
|
||||
}
|
||||
} // computeDijkstra
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** THIS FUNCTION IS ONLY USED FOR UNIT-TESTING, to verify that the new
|
||||
* Dijkstra algorithm gives the same results.
|
||||
* computeFloydWarshall() computes the shortest distance between any two
|
||||
* nodes. At the end of the computation, m_distance_matrix[i][j] stores the
|
||||
* shortest path distance from i to j and m_parent_poly[i][j] stores the last
|
||||
* vertex visited on the shortest path from i to j before visiting j. Suppose
|
||||
* the shortest path from i to j is i->......->k->j then
|
||||
* m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeFloydWarshall()
|
||||
{
|
||||
@ -244,12 +329,12 @@ const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const
|
||||
} // differentNodeColor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void BattleGraph::loadGoalNodes(const XMLNode& node)
|
||||
void BattleGraph::loadGoalNodes(const XMLNode *node)
|
||||
{
|
||||
m_red_node.clear();
|
||||
m_blue_node.clear();
|
||||
|
||||
const XMLNode *check_node = node.getNode("checks");
|
||||
const XMLNode *check_node = node->getNode("checks");
|
||||
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *goal = check_node->getNode(i);
|
||||
@ -277,3 +362,97 @@ void BattleGraph::loadGoalNodes(const XMLNode& node)
|
||||
}
|
||||
}
|
||||
} // loadGoalNodes
|
||||
|
||||
// ============================================================================
|
||||
/** Unit testing for battle graph distance and parent node computation.
|
||||
* Instead of using hand-tuned test cases we use the tested, verified and
|
||||
* easier to understand Floyd-Warshall algorithm to compute the distances,
|
||||
* and check if the (significanty faster) Dijkstra algorithm gives the same
|
||||
* results. For now we use the cave mesh as test case.
|
||||
*/
|
||||
void BattleGraph::unitTesting()
|
||||
{
|
||||
Track *track = track_manager->getTrack("cave");
|
||||
std::string navmesh_file_name=track->getTrackFile("navmesh.xml");
|
||||
|
||||
double s = StkTime::getRealTime();
|
||||
BattleGraph *bg = new BattleGraph(navmesh_file_name);
|
||||
double e = StkTime::getRealTime();
|
||||
Log::error("Time", "Dijkstra %lf", e-s);
|
||||
|
||||
// Save the Dijkstra results
|
||||
std::vector< std::vector< float > > distance_matrix = bg->m_distance_matrix;
|
||||
std::vector< std::vector< int > > parent_poly = bg->m_parent_poly;
|
||||
bg->buildGraph(NavMesh::get());
|
||||
|
||||
// Now compute results with Floyd-Warshall
|
||||
s = StkTime::getRealTime();
|
||||
bg->computeFloydWarshall();
|
||||
e = StkTime::getRealTime();
|
||||
Log::error("Time", "Floyd-Warshall %lf", e-s);
|
||||
|
||||
int error_count = 0;
|
||||
for(unsigned int i=0; i<bg->m_distance_matrix.size(); i++)
|
||||
{
|
||||
for(unsigned int j=0; j<bg->m_distance_matrix[i].size(); j++)
|
||||
{
|
||||
if(bg->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f)
|
||||
{
|
||||
Log::error("BattleGraph",
|
||||
"Incorrect distance %d, %d: Dijkstra: %f F.W.: %f",
|
||||
i, j, distance_matrix[i][j], bg->m_distance_matrix[i][j]);
|
||||
error_count++;
|
||||
} // if distance is too different
|
||||
|
||||
// Unortunately it happens frequently that there are different
|
||||
// shortest path with the same length. And Dijkstra might find
|
||||
// a different path then Floyd-Warshall. So the test for parent
|
||||
// polygon often results in false positives, so it is disabled,
|
||||
// but I leave the code in place in case it is useful for some
|
||||
// debugging in the feature
|
||||
#undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
#ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
if(bg->m_parent_poly[i][j] != parent_poly[i][j])
|
||||
{
|
||||
error_count++;
|
||||
std::vector<int> dijkstra_path = getPathFromTo(i, j, parent_poly);
|
||||
std::vector<int> floyd_path = getPathFromTo(i, j, bg->m_parent_poly);
|
||||
if(dijkstra_path.size()!=floyd_path.size())
|
||||
{
|
||||
Log::error("BattleGraph",
|
||||
"Incorrect path length %d, %d: Dijkstra: %d F.W.: %d",
|
||||
i, j, parent_poly[i][j], bg->m_parent_poly[i][j]);
|
||||
continue;
|
||||
}
|
||||
Log::error("BattleGraph", "Path problems from %d to %d:",
|
||||
i, j);
|
||||
for (unsigned k = 0; k < dijkstra_path.size(); k++)
|
||||
{
|
||||
if(dijkstra_path[k]!=floyd_path[k])
|
||||
Log::error("BattleGraph", "%d/%d dijkstra: %d floyd %d",
|
||||
k, dijkstra_path.size(), dijkstra_path[k],
|
||||
floyd_path[k]);
|
||||
} // for k<dijkstra_path.size()
|
||||
|
||||
} // if dijkstra parent_poly != floyd parent poly
|
||||
#endif
|
||||
} // for j
|
||||
} // for i
|
||||
} // unitTesting
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines the full path from 'from' to 'to' and returns it in a
|
||||
* std::vector (in reverse order). Used only for unit testing.
|
||||
*/
|
||||
std::vector<int> BattleGraph::getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int > > parent_poly)
|
||||
{
|
||||
std::vector<int> path;
|
||||
path.push_back(to);
|
||||
while(from!=to)
|
||||
{
|
||||
to = parent_poly[from][to];
|
||||
path.push_back(to);
|
||||
}
|
||||
return path;
|
||||
} // getPathFromTo
|
@ -63,9 +63,9 @@ private:
|
||||
|
||||
void buildGraph(NavMesh*);
|
||||
void computeFloydWarshall();
|
||||
void loadGoalNodes(const XMLNode& node);
|
||||
void loadGoalNodes(const XMLNode *node);
|
||||
|
||||
BattleGraph(const std::string &navmesh_file_name, const XMLNode& node);
|
||||
BattleGraph(const std::string &navmesh_file_name, const XMLNode *node=NULL);
|
||||
~BattleGraph(void);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -86,17 +86,27 @@ private:
|
||||
{ return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool differentNodeColor(int n, NodeColor* c) const;
|
||||
void computeDijkstra(int n);
|
||||
static std::vector<int> getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int > > parent_poly);
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_POLY;
|
||||
|
||||
void findItemsOnGraphNodes();
|
||||
int pointToNode(const int cur_node,
|
||||
const Vec3& cur_point,
|
||||
bool ignore_vertical) const;
|
||||
static void unitTesting();
|
||||
|
||||
|
||||
/** Returns the one instance of this object. */
|
||||
static BattleGraph *get() { return m_battle_graph; }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Asserts that no BattleGraph instance exists. Then
|
||||
* creates a BattleGraph instance. */
|
||||
static void create(const std::string &navmesh_file_name,
|
||||
const XMLNode& node)
|
||||
const XMLNode *node)
|
||||
{
|
||||
assert(m_battle_graph==NULL);
|
||||
m_battle_graph = new BattleGraph(navmesh_file_name, node);
|
||||
@ -149,11 +159,6 @@ public:
|
||||
void insertItems(Item* item, int polygon)
|
||||
{ m_items_on_graph.push_back(std::make_pair(item, polygon)); }
|
||||
// ------------------------------------------------------------------------
|
||||
void findItemsOnGraphNodes();
|
||||
// ------------------------------------------------------------------------
|
||||
int pointToNode(const int cur_node,
|
||||
const Vec3& cur_point,
|
||||
bool ignore_vertical) const;
|
||||
}; //BattleGraph
|
||||
|
||||
#endif
|
||||
|
@ -660,7 +660,7 @@ void Track::startMusic() const
|
||||
*/
|
||||
void Track::loadBattleGraph(const XMLNode &node)
|
||||
{
|
||||
BattleGraph::create(m_root+"navmesh.xml", node);
|
||||
BattleGraph::create(m_root+"navmesh.xml", &node);
|
||||
|
||||
if(BattleGraph::get()->getNumNodes()==0)
|
||||
{
|
||||
|
@ -43,7 +43,6 @@ class ModelDefinitionLoader;
|
||||
#include "items/item.hpp"
|
||||
#include "scriptengine/script_engine.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "utils/aligned_array.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Automate the build process on Linux based on
|
||||
# http://supertuxkart.sourceforge.net/Build_STK_on_Linux
|
||||
# http://supertuxkart.net/Build_STK_on_Linux
|
||||
|
||||
# CMake build type
|
||||
BUILDTYPE=Debug
|
||||
|
Loading…
Reference in New Issue
Block a user